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_lseek
W 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_write
sock_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 ).