Przyjrzyjmy sie kodowi z socket.c. Mamy tam:
static struct file_operations socket_file_ops = {
sock_lseek,
sock_read,
sock_write,
NULL, /* nie mamy readdir() */
sock_select,
sock_ioctl,
NULL, /* nie mozna uzywac mmap */
NULL, /* nie mamy specjalnej obslugi open... */
sock_close,
NULL, /* nie mamy operacji fsync */
sock_fasync
};
Struktura struct file_operations jest zdefiniowana w fs.h. Jej
instancja, socket_file_ops, zawiera wskazniki do funkcji, zdefiniowanych takze
w socket.c. Ta struktura jest uzywana w funkcji get_fd: adres
( &socket_file_ops )jest przypisywany na pole f_op nowoutworzonej struktury file.
Wywolanie np. funkcji systemowej read dla gniazd jest w istocie realizowane przez funkcje
sock_read.
Inaczej jest realizowana funkcja fcntl:patrz dalej.
Funkcje zdefiniowane w socket.c robia niewiele. Ich zadanie to sprawdzenie,
czy bufor podany jako argument jest w istocie dostepny dla czytania/pisania
dla uzytkownika, ewentualnie weryfikacja poprawnosci argumentow (wyjatki :
sock_close,sock_fasync ).
Potem wywolywana jest odpowiednia funkcja rodziny gniazd, zdefiniowana w af_inet.c lub
w af_unix.c. Funkcje z af_unix.c zwykle realizuja zadane operacje. Funkcje z af_inet.c
przeciwnie, tylko wywoluja funkcje protokolu ( TCP,UDP,ICMP ...), ktore wreszcie
wykonuja zadanie. Adresy funkcji protokolu znajduja sie w strukturze ops
(bedacej polem stuktury socket). O funkcjach protokolu wiecej powinno byc
w Temacie 08.
sock_lseekW funkcjach read i write jest uzywana struktura iovec - jest to po prostu tablica buforow. Wiecej informacji - w opisie funkcji *msg.
sock_read
static int sock_read(struct inode *inode, struct file *file, char *ubuf, int size)
{
struct socket *sock;
int err;
struct iovec iov;
struct msghdr msg;
sock = socki_lookup(inode);
if (sock->flags & SO_ACCEPTCON) /* Jesli nie ma ustalonego polaczenia,*/
return(-EINVAL); /* zwroc EINVAL */
if(size<0)
return -EINVAL;
if(size==0) /* Dla zgodnosci z SYS5 */
return 0;
if ((err=verify_area(VERIFY_WRITE,ubuf,size))<0)
return err; /* czy uzytkownik moze pisac do ubuf */
msg.msg_name=NULL;
msg.msg_iov=&iov;
msg.msg_iovlen=1;
msg.msg_control=NULL;
iov.iov_base=ubuf;
iov.iov_len=size;
return(sock->ops->recvmsg(sock, &msg, size,(file->f_flags & O_NONBLOCK)
, 0,&msg.msg_namelen)); /* funkcja rodziny */
}
Tu jest opis funkcja recvmsg.sock_writesock_ioctl
int sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
struct socket *sock;
/* po prostu sock = &inode->u.socket_i */
sock = socki_lookup(inode);
/* funkcja rodziny */
return(sock->ops->ioctl(sock, cmd, arg));
}
case FIOSETOWN: /* okresl wlasciciela */
case SIOCSPGRP: /* okresl grupe procesow */
err=verify_area(VERIFY_READ,(int *)arg,sizeof(long));
if(err)
return err;
pid = get_user((int *) arg);
/* patrz komentarz do fcntl */
if (current->pid != pid && current->pgrp != -pid && !suser())
return -EPERM;
sk->proc = pid;
return(0);
case FIOGETOWN: /* pobierz wlasciciela */
case SIOCGPGRP: /* pobierz grupe procesow */
err=verify_area(VERIFY_WRITE,(void *) arg, sizeof(long));
if(err)
return err;
put_fs_long(sk->proc,(int *)arg);
return(0);
default:
if ((cmd >= SIOCDEVPRIVATE) && (cmd <= (SIOCDEVPRIVATE + 15)))
return(dev_ioctl(cmd,(void *) arg));
if (sk->prot->ioctl==NULL)
/* jesli nie mamy ioctl zaimplementowanej w protokole */
return(-EINVAL);
return(sk->prot->ioctl(sk, cmd, arg)); /* funkcja protokolu */
sock_select
static int sock_select(struct inode *inode, struct file *file,
int sel_type, select_table * wait)
{
struct socket *sock;
sock = socki_lookup(inode);
if (sock->ops->select)
/* funkcja protokolu */
return(sock->ops->select(sock, sel_type, wait));
return(0);
}
return( sk->prot->select( sk, sel_type, wait ) );
return datagram_select(sock->data,sel_type,wait);
sock_close
void sock_close(struct inode *inode, struct file *filp)
{
/* inode moze byc NULL jesli zamykamy deskryptor gniazda, ktore nie */
/* zostalo do konca zainicjalizowane (?) ( oryg. unfinished socket ) */
if (!inode)
return;
/* zwalniamy pamiec zajeta przez liste klientow, */
/* ktorym mamy wysylac SIGIO */
sock_fasync(inode, filp, 0);
/* budzimy polaczone z nami i czekajace na polaczenie procesy */
sock_release(socki_lookup(inode));
}
sock_fasync
static int sock_fasync(struct inode *inode, struct file *filp, int on)
{
struct fasync_struct *fa, *fna=NULL, **prev;
struct socket *sock;
unsigned long flags;
if (on)
{
/* alokujemy element listy fasync */
fna=(struct fasync_struct *)
kmalloc(sizeof(struct fasync_struct), GFP_KERNEL);
if (fna==NULL)
return -ENOMEM;
}
sock = socki_lookup(inode);
prev=&(sock->fasync_list);
save_flags(flags);
cli();
/* szukamy w liscie fasync_list wezla o polu fa_file==filp */
for(fa=*prev; fa!=NULL; prev=&fa->fa_next,fa=*prev)
if(fa->fa_file==filp)
break;
if(on)
{
if(fa!=NULL) /* znalezlismy wezel */
{
/* nie trzeba nic robic,*/
/* zwalniamy niepotrzebnie uprzednio zaalokowana pamiec */
/* czy to jest elegancki styl programowania ? */
kfree_s(fna,sizeof(struct fasync_struct));
restore_flags(flags);
return 0;
}
/* tworzymy nowy wezel */
fna->fa_file=filp;
fna->magic=FASYNC_MAGIC;
/* wstawiamy g do listy */
fna->fa_next=sock->fasync_list;
sock->fasync_list=fna;
}
else
{
if(fa!=NULL) /* znalezlismy wezel */
{
/* usuwamy go z listy */
*prev=fa->fa_next;
/* zwalniamy pamiec */
kfree_s(fa,sizeof(struct fasync_struct));
}
}
restore_flags(flags);
return 0;
}
To wszystko o funkcjach z socket_file_ops. sock_fcntl fcntl
(zdefiniowana w fs/fcntl.c ) sprawdza, czy przekazany jej jako argument
deskryptor pliku odnosi sie do gniazda. Jesli tak, wywoluje funkcje
sock_fcntl .
int sock_fcntl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct socket *sock;
sock = socki_lookup (filp->f_inode);
if (sock != NULL && sock->ops != NULL && sock->ops->fcntl != NULL)
/* wywolujemy funkcje rodziny */
return(sock->ops->fcntl(sock, cmd, arg));
return(-EINVAL);
}
static int inet_fcntl(struct socket *sock,
unsigned int cmd, unsigned long arg)
{
struct sock *sk;
sk = (struct sock *) sock->data;
switch(cmd)
{
case F_SETOWN:
/*
* Musimy sprawdzic, czy ustanawiany wlasciciel to biezacy proces
* ( albo czy biezacy proces ma przywileje nadzorcy) .
* Jest to silne ograniczenie, ale konieczne - w przeciwnym razie
* moznaby do dowolnego procesu w systemie wyslac sygnal SIGURG,
* co zwykle zabija proces.
* To ograniczenie weszlo stosunkowo niedawno...
*/
if (!suser() && current->pgrp != -arg && current->pid != arg)
return(-EPERM);
sk->proc = arg;
return(0);
case F_GETOWN:
return(sk->proc);
/* nie mamy innych operacji */
default:
return(-EINVAL);
}
}
Warto jeszcze napomknac, ze przy pomocy fcntl mozna ustawic na
gniezdzie flage O_NDELAY - wtedy operacje na gniezdzie sa nieblokujace
(tj. jesli wymagalyby uspienia wywolujacego procesu, to odpowiednia funkcja wraca
natychmiast z bledem ).