Do spisu tresci tematu 9

9.2.2 Struktury danych, zmienne, stale



Spis tresci


Wprowadzenie

Gniazda sa wbudowane w system plikow, dlatego na poczatku bede opisywal gniazdowe struktury danych z nim zwiazane. Nastepnie bede pisal o dziedzinach i na koncu o protokolach.


System plikow i gniazda

Oto diagram ilustrujacy jak gniazda sa wbudowane w system plikow:

(rysunek)


Deskryptory gniazd i struktura file

Kazde gniazdo jest identyfikowane przez swoj deskryptor, zwracany przez funkcje systemowe je tworzace. Taki deskryptor jest indeksem w tablicy deskryptorow procesu (current->files->fd). Pod tym indeksem tej tablicy znajduje sie wskaznik do struktury file w tablicy plikow.

include/linux/fs.h
struct file {
	mode_t f_mode;
	loff_t f_pos;
	unsigned short f_flags;
	unsigned short f_count;
	...	
	int f_owner;		
	struct inode * f_inode;
	struct file_operations * f_op;
...	
};

Funkcje tworzace gniazdo inicjuja pobieraja wolna struktury file i inicjuja w niej pola. Pobieraja i-wezel i przypisuja wskaznik do niego na pole f_inode. Ponadto podstawiaja pod pole f_op wskaznik do statycznie zdefiniowanej zmiennej socket_file_ops , ktora definiuje operacje plikowe na gniazdach: net/socket.c
static struct file_operations socket_file_ops = {
	sock_lseek,
	sock_read,
	sock_write,
	NULL,		
	sock_select,
	sock_ioctl,
	NULL,		
	NULL,		
	sock_close,
	NULL,		
	sock_fasync
};
Zobacz rowniez: Temat o systemie plikow

I-wezly

Z kazdym gniazdem zwiazany jest i-wezel wskazywany przez pole f_inode z tablicy plików. Jest przydzielany przez funkcje tworzące gniazdo.

include/linux/fs.h
struct inode {
	kdev_t		i_dev;
	unsigned long	i_ino;
	umode_t		i_mode;
	nlink_t		i_nlink;
	uid_t		i_uid;
	gid_t		i_gid;
	kdev_t		i_rdev;
	off_t		i_size;
	struct wait_queue *i_wait;
	...
	unsigned char i_sock;
	...
	union {
		...
		struct socket socket_i;
	} u;
};

Istotne dla gniazd pola to pole unii u : struct socket socket_i oraz znacznik: char i_sock - informujacy czy i-wezel zwiazany jest z gniazdem (ustawiany na 1 gdy tak). Ponadto kolejka procesow : i_wait. Do tej kolejki sa odkladane procesy przy blokujacych funkcjach systemowych.


Struktura socket

Jedna z ważniejszych struktur danych zwiazanych z gniazdami. Jest przechowywana w i-wezle. Przy wykonywaniu funkcji dla dziedzin jest zawsze przekazywana jako argument.

include/linux/net.h

struct socket {
  short			type;
  socket_state		state;
  long			flags;
  struct proto_ops	*ops;
  void			*data;	
  struct socket		*conn;	
  struct socket		*iconn;
  struct socket		*next;
  struct wait_queue	**wait;	
  struct inode		*inode;
  struct fasync_struct  *fasync_list;	
  struct file		*file;  	
};

Znaczenie pol:

type - typ gniazda ustawiany przy tworzeniu; 
     	możliwe wartości (include/linux/socket.h): 
	SOCK_STREAM - gniazdo strumieniowe
	SOCK_DGRAM - gniazdo datagramowe
	SOCK_RAW - gniazdo surowe
	SOCK_RDM - gniazdo komunikatow niezawodnie doreczanych
	SOCK_SEQPACKET - gniazdo pakietow uporzadkowanych
	SOCK_PACKET - gniazdo pakietow  (tylko Linux)
state - status gniazda; 
	oto definicja socket_state z include/linux/net.h 
   	wraz ze znaczeniem pol: 
	typedef enum {
  	  SS_FREE = 0,	              	/* niezaalokowane		*/
  	  SS_UNCONNECTED,	/* niepolaczone	*/
  	  SS_CONNECTING,	/* w trakcie laczenia	*/
  	  SS_CONNECTED,	/* polaczone z innym gniazdem*/
  	  SS_DISCONNECTING	/* w trakcie rozlaczania	*/
	 } socket_state;
flags - flagi gniazd; możliwe wartości (include/linux/net.h)
	SO_ACCEPTCON - ustawiane jesli funkcja listen 
	   zakonczyla sie poprawnie
	SO_WAITDATA - oczekuje na dane
	SO_NOSPACE - nie ma miejsca na wyslanie
ops - wskazniki do funkcji wlasciwych dla danego protokolu; 
	ustawiane przy tworzeniu gniazda; 
	patrz  struct proto_ops i tablica pops
data - wykorzystywane w funkcjach protokolow; 
	np w dziedzinach Unixa i Inetu wskazuje na strukture 
	sock por.dziedziny: Unix, Internet
conn - nie jest wykorzystywane (wbrew komentarzom)
iconn - to pole jest wykorzystywane tylko do zapamiętania
	partnera przy socketpair (dziedzina Unixa)
wait_queue - taki sam jak w i-wezle; 
             kolejka procesow czekajacych na polaczenie lub i/o
inode - wskaznik do i-wezla (w tablicy i-wezlow)
fasync_list - lista ze wskaznikami do struktur file zwiazanymi 
	z otwartymi gniazdami; okresla grupe procesow zwiazanych 
      z gniazdem; jest wykorzystywana do tzw. wejscia/wyjscia 
      asynchronicznego
file - wskaznik do struktury file w tablicy plikow 

Inicjacja dziedzin (struktury i zmienne)

Nastepuje przy wywolaniu funkcji sock_init i proto_init wykonywanych przy bootowaniu systemu. Do inicjowania wykorzystuje sie strukture net_proto zawierajaca: nazwe i wskaznik do funkcji inicjujacej. include/linux/net.h

struct net_proto {
	const char *name;	
	void (*init_func)(struct net_proto *);
};
Informacje w strukturach net_proto o wszystkich dostepnych protokolach przechowuje tablica protocols zdefiniowana w net/protocols.c.

Identyfikacja dziedzin

Do tego celu wykorzystywana jest tablica pops rozmiaru NPROTO (obecnie 16) zawierajaca wskazniki do struktur proto_ops przechowywujacych wskazniki do funkcji na gniazdach odpowiednich dla dziedzin. Jej inicjacja nastepuje przy wykonaniu funkcji proto_init, ktora korzysta z tablicy protocols.

include/linux/net.h
struct proto_ops {
  int family;

  int (*create)	(struct socket *sock, int protocol);
  int (*dup) (struct socket *newsock, struct socket *oldsock);
  int (*release)(struct socket *sock, struct socket *peer);
  int (*bind)(struct socket *sock, struct sockaddr *umyaddr,
		int sockaddr_len);
  int (*connect) (struct socket *sock, struct sockaddr    
   	*uservaddr, int sockaddr_len, int flags);
  int (*socketpair)(struct socket *sock1, struct socket sock2);
  int (*accept)	(struct socket *sock, struct socket 
newsock, int flags);
  int (*getname)(struct socket *sock, struct sockaddr *uaddr,
		 int *usockaddr_len, int peer);
  int (*select)	(struct socket *sock, int sel_type,
			 select_table *wait);
  int (*ioctl)	(struct socket *sock, unsigned int cmd,
			 unsigned long arg);
  int (*listen)	(struct socket *sock, int len);
  int (*shutdown)	(struct socket *sock, int flags);
  int (*setsockopt)	(struct socket *sock, int level, int 
optname, char *optval, int optlen);
  int (*getsockopt)	(struct socket *sock, int level, int 
optname, char *optval, int *optlen);
  int (*fcntl)	(struct socket *sock, unsigned int cmd,
	unsigned long arg);	
  int (*sendmsg)	(struct socket *sock, struct msghdr *m, int 
total_len, int nonblock, int flags);
  int (*recvmsg)	(struct socket *sock, struct msghdr *m, int 
total_len, int nonblock, int flags, int *addr_len);
};

Pole family przyjmuje jedna z wartosci (AF_...) PF_... np.: PF_UNIX, PF_INET... Odpowiada wartosci family podawanej przy funkcji systemowej socket. Opis struktury msghdr .


Struktura sockaddr

Patrz: nazwy gniazd.


Struktury msghdr i iovec

Funkcje systemowe sendmsg i recvmsg korzystaja z tych struktur. Ponadto kazda funkcja systemowa odbierajaca i wysylajaca dane korzysta z funkcji dla protokolow : xx_recvmsg, xx_sendmsg (patrz: proto_ops), ktorych argumentem jest wskaznik do struktury msghdr.

include/linux/uio.h
struct iovec
{
	void *iov_base;	
	int iov_len;
};

Ta struktura definiuje pojedynczy blok danych. Pole iov_base jest wskaznikiem na obszar danych, iov_len okresla jego dlugosc.

include/linux/socket.h
struct msghdr 
{
	void	*	msg_name;		
	int		msg_namelen;	
	struct iovec *	msg_iov;	
	int 		msg_iovlen;	
	void 	*	msg_control;	
	int		msg_controllen;	
	int		msg_flags;	
};
Pola msg_name i msg_namelen okreslaja nazwe gniazda. Wskaznik msg_iov definuje tablice blokow danych o liczbie elementow msg_iovlen, ktora nie moze byc wieksza niz MAXIOVLEN (obecnie 16). Pola msg_control i msg_controllen umozliwiaja przekazywanie praw dostepu i deskryptorow plikow w protokole Unixa (patrz opis struktury cmsghdr w protokole Unixa). Flagi (pole flags) moga byc zerem lub alternatywa stalych: MSG_OOB, MSG_PEEK lub MSG_DONTROUTE - analogicznie jak argument flags funkcji systemowych 'recv' i 'send'.

Uwaga: R.Stevens podaje inne nazwy niektorych pol w tej strukturze. Ponadto przyklady Stevensa dotyczace przekazywania deskryptorow w dziedzinie Unixa nie beda dzialac ma Linux-ie (trzeba dokonac kilku zmian), ze wzgledu na koniecznosc uzycia struktury cmsghdr.


Wstep - dziedzina Unixa

W tej dziedzinie nie wyrozniamy protokolow. Wystepuja tylko dwa rodzaje gniazd:datagramowe i strumieniowe. W odroznieniu od innych dziedzin procesy moga komunikowac sie tylko w obrebie tego samego systemu. Jadro dba bez uzycia rzesylania w sieci o przekazywanie komunikatow z jednego procesu do innego. Jest to pewna forma komunikacji miedzyprocesowej wbudowana w interfejs gniazd. Wazna cecha gniazd Unixa (i tylko Unixa) jest mozliwosc przesylania deskryptorow plikow i praw dostepu miedzy nie powiazanymi procesami. Gniazda identyfikujemy za pomoca specjalnych plikow, ktore sa tworzone w systemie.


Funkcje dziedziny Unixa

Wskazniki do funkcji implementujacych operacje na gniazdach dla dziedziny Unixa zawarte sa w zmiennej unix_proto_ops zdefiniowanej w net/unix/af_unix.c Patrz: opis struktury proto_ops.


Strukura sock i zmienna unix_socket_list

Strukura sock jest zdefiniowana w include/net/sock.hi jest podstawowa struktura danych przechowujaca informacje o obslugiwanych gniazdach (rowniez w dziedzinie Internetu). Posiada pole: struct sock *next umozliwiajace tworzenie list gniazd. W dziedzinie Unixa na glowe listy wszystkich gniazd wskazuje zmienna: unix_socket_list. Tylko pola wykorzystywane przez d. Unixa:

struct sock 
{	atomic_t		wmem_alloc;
	atomic_t		rmem_alloc;
	volatile char		dead,
	struct sock		*next;
	struct sk_buff_head	write_queue, receive_queue;
	struct wait_queue	**sleep;
	volatile unsigned short	shutdown;
	volatile unsigned char	state;
	unsigned char		ack_backlog;
	unsigned char		max_ack_backlog;
	unsigned short		rcvbuf;
	unsigned short		sndbuf;
	unsigned short		type;
	union
	{ ...
	  	struct unix_opt	af_unix;
	} protinfo;  		
	struct timer_list	timer;		
 	struct socket		*socket; 
	void			(*state_change)(struct sock *sk);
	void			(*data_ready)(struct sock *sk,int bytes);
	void			(*write_space)(struct sock *sk);
	void			(*error_report)(struct sock *sk);
};


wmem_alloc, rmem_alloc -  ile pamieci dla buforow odbioru
	i wysylania zaalokowano dla tego gniazda; por. pola 
	rcvbuf i sndbuf ponizej
dead - znacznik ustawiany na 1 (martwe gniazdo), gdy gnia-
	zdo usunieto z listy gniazd, ale zostaly polaczenia 
	(por. timer)
next - nastepna struktura sock na liscie
receive_queue - kolejka komunikatow do odebrania
sleep - kolejka spiacych procesow; ta sama co w i-wezle
shutdown - okresla tryb zamkniecia gniazda (patrz fcja 
	shutdown) moze przyjac wartoeci stalych 
	(flag bitowych): SND_SHUTDOWN, RCV_SHUTDOWN i maski 
	SHUTDOWN_MASK
state - okresla stan gniazda; wykorzystuje stale TCP_CLOSE,
	 TCP_LISTEN,... 
ack_backlog - aktualna liczba polaczen; nie jest sprawdzana 
	wartosc tego pola
max_ack_backlog - maksymalna liczba polaczen; przekazywane 
	przy fcji listen; wartosc 
	tego pola nigdzie nie jest sprawdzana
rcvbuf, sndbuf - przechowuja informacje o rozmiarach bufo-
	row dla kolejek danych wysylanych i odbieranych; 
	mozna zmienic przez fcje setsockopt 
	i opcje SO_RCVBUF, SO_SNDBUF;  
type - typ gniazda; to samo co w socket.type
unia protinfo i pole af_unix - patrz unix_opt
timer - inicjowany przy usunieciu gniazda, z ktorym byly 
	polaczenia; najpierw po 1 sek. pozniej co 10 sek. 
	wywoluje procedure sprawdzajaca czy wszystkie 
	polaczenia zostaly zamkniete, gdy tak to struktura 
	sock zostaje usunieta z pamieci
socket - wskaznik do struktury socket w i-wezle

wskazniki do funkcji def_callback1, def_callback2, 
def_callback3 z net/unix/af_unix.c: 
state_change - obudz spiacych w kolejce sleep
write_space, error_report, data_ready - j/w i ponadto 
	wyslij sygnal SIGIO do grupy procesow zwiazanych 
	z gniazdem o ile pozwalaja na to flagi gniazda
error_report - tak samo jak state_change
Porownaj w dziedzinie Internetu.

Struktura unix_opt

Wykorzystywana jako pole unii protinfo w strukturze sock. Okresla specyficzne dla Unixa wlasnosci.

struct unix_opt
{
	int 			family;
	char *			name;
	int  			locks;
	struct inode *		inode;
	struct semaphore	readsem;
	struct sock *		other;
	int 			marksweep;
	int			inflight;
};

family - zawsze AF_UNIX 
locks - z iloma gniazdami jest polaczenie
inode - i-wezel zwiazany z gniazdem po wykonaniu funkcji
	unix_bind; to nie jest i-wezel, o ktorym pisze
	wczesniej
readsem - semafor uzywany przy odbiorze (unix_recvmsg)
other - partner z ktorym jest polaczenie
marksweep - ustawiany podczas wykonywania funkcji 	
	usuwajacych zbedne deskryptory plikow przekazywane
 	przez funkcje gniazd	
inflight - inkrementowany jesli deskryptor tego gniazda 
	jest przekazywany do innego procesu (sendmsg) i
 	dekrementowany po przekazaniu (recvmsg);
marksweep - wykorzystywany przy usuwaniu zbednych 	
	deskryptorow plikow podczas przekazywania ich 
	przez gniazdo 


Struktura sockaddr_un

Patrz: nazwy gniazd.


Struktura cmsghdr

Uzywana przy przekazywaniu deskryptorow i praw dostepu za pomoca funkcji systemowych sendmsg i recvmsg.

linux/un.h
struct cmsghdr {
	unsigned int cmsg_len;
	int cmsg_level;
	int cmsg_type;
	unsigned char cmsg_data[0];
};

W celu przeslania deskryptorow nalezy wskaznik na te strukture nalezy przypisac polu msg_control w odpowiedniej strukturze msghdr (patrz opis) i przypisac wartosci: cmsg_level=SCM_RIGHTS, cmsg_type=SOL_SOCKET, cmsg_len=sizeof(struct cmsghdr)+ rozmiar cmsg_data. Pole cmsg_data traktujemy jako tablice int i zapisujemy tam deskryptory plikow, ktore chcemy przekazac. Patrz rowniez: sendmsg, recvmsg


Struktura sk_buff (Unix)

W dziedzinie Unixa funkcje buforow przechowujacych przesylane komunikaty pelni lista struktur sk_buff. Ponadto jest zdefiniowana glowa listy (straznik) - struktura sk_buff_head. Funkcja wysylajaca (unix_sendmsg) tworzy i wstawia bufor bezposrednio do kolejki (sock.receive_queue) odbiorcy.

linux/skbuff.h
struct sk_buff_head 
{
	struct sk_buff	* next;
	struct sk_buff	* prev;
__u32		qlen;		
};

struct sk_buff 
{
	struct sk_buff	* next;	
	struct sk_buff	* prev;	
	struct sk_buff_head * list;	
	struct sock	*sk;			
	union 
	{
		...
		void *filp;
	} h;
  
	struct sk_buff	*data_skb; 
	unsigned char	*head;			
	unsigned char	*data;			
	unsigned char	*tail;			
	unsigned char 	*end;			
	void 		(*destructor)(struct sk_buff *);	
};


Istotne pola:
next, prev, list - struktura listowa
sk - wskaznik na gniazdo, z ktorym jest zwiazany bufor
pole unii h.filp - tablica deskryptorow przekazywana przez 
	gniazdo (tylko pr.Unixa); patrz struktury msghdr, 
	cmsghdr
len - aktualna dlugosc danych w buforze
truesize - rozmiar bufora 
head - wskaznik do bufora
data - wskaznik do pierwszego elementu danych w buforze
tail - wskaznik do ostatniego elementu danych+1 (bufor 
	moze byc niezapelniony)
end - wskaznik do ostatniego elementu bufora (data_skb
	+truesize)
destructor - wskaznik do funkcji usuwajacej bufor

Ta struktura jest rowniez wykorzystywana w dziedzine Internetu.

Wstep - dziedzina Internetu

Dziedzina Internetu udostepnia trzy protokoly: TCP (strumieniowy), UDP (datagramowy) i RAW (gniazda surowe). W odroznieniu od dziedziny Unixa ta dziedzina jest prawdziwie sieciowa tzn. umozliwia komunikacje miedzy roznymi komputerami.


Funkcje dziedziny Internetu

Wskazniki do funkcji systemowych implementujacych operacje na gniazdach sa zawarte w zmiennej inet_proto_ops w net/ipv4/af_inet.c

Patrz opis struktury proto_ops


Struktura proto

Definuje dane o gniazdach i operacje na gniazdach dla protokolow dziedziny Internetu. Kazdy protokol posiada zdefiniowana zmienna wskazujaca na ta strukture: tcp_prot, udp_prot , raw_prot. Odpowiedni wskaznik jest przypisywany przy tworzeniu gniazda polu prot w strukturze sock. Funkcje dziedziny Internetu wywoluja odpowiednie funkcje dla protokolow korzystajac wlasnie z tego pola.

net/sock.h
struct proto 
{
        void                    (*close)(struct sock *sk, unsigned long timeout);
        int                     (*build_header)(struct sk_buff *skb,
                                        __u32 saddr,
                                        __u32 daddr,
                                        struct device **dev, int type,
                                        struct options *opt, int len,
                                        int tos, int ttl, struct rtable ** rp);
        int                     (*connect)(struct sock *sk,
                                        struct sockaddr_in *usin, int addr_len);
        struct sock *           (*accept) (struct sock *sk, int flags);
        void                    (*queue_xmit)(struct sock *sk,
                                        struct device *dev, struct sk_buff *skb,
                                        int free);
        void                    (*retransmit)(struct sock *sk, int all);
        void                    (*write_wakeup)(struct sock *sk);
        void                    (*read_wakeup)(struct sock *sk);
        int                     (*rcv)(struct sk_buff *buff, struct device *dev,
                                        struct options *opt, __u32 daddr,
                                        unsigned short len, __u32 saddr,
                                        int redo, struct inet_protocol *protocol);
        int                     (*select)(struct sock *sk, int which,
                                        select_table *wait);
        int                     (*ioctl)(struct sock *sk, int cmd,
                                        unsigned long arg);
        int                     (*init)(struct sock *sk);
        void                    (*shutdown)(struct sock *sk, int how);
        int                     (*setsockopt)(struct sock *sk, int level, int optname,
                                        char *optval, int optlen);
        int                     (*getsockopt)(struct sock *sk, int level, int optname,
                                        char *optval, int *option);      
        int                     (*sendmsg)(struct sock *sk, struct msghdr *msg, int len,
                                        int noblock, int flags);
        int                     (*recvmsg)(struct sock *sk, struct msghdr *msg, int len,
                                        int noblock, int flags, int *addr_len);
        int                     (*bind)(struct sock *sk, struct sockaddr *uaddr, int addr_len);
        unsigned short          max_header;
        unsigned long           retransmits;
        char                    name[32];
        int                     inuse, highestinuse;
        struct sock *           sock_array[SOCK_ARRAY_SIZE];
};

Wskazniki do funkcji okreslaja operacje na gniazdach dla protokolow. Tablica sock_array jest tablica haszujaca (operacje na niej zdefiniowane w af_inet.c). Jej elementami sa listy struktur sock, kluczem jest pole sock.num. MAX_ARRAY_SOCK obecnie wynosi 256 Pole inuse zawiera liczbe wszystkich struktur sockw sock_array. Pole highestinuse zawiera najwieksza wartosc, ktora dotychczas przyjal inuse (tylko do celow statystycznych).


Struktura sock

Ta struktura przechowuje wszystkie informacje o gniezdzie w dziedzinie Internetu. Umozliwia tworzenie list, ktore sa elementami tablicy haszujacej w strukturach protodla kazdego z protokolow. Czesc pol jest wykorzystywana przez protokoly i nizsze warstwy obslugujace polaczenia.

Tam mozna obejrzec strukture sock: net/sock.h

Czesc z pol tej struktury ma analogiczne znaczenie jak dla protokolu Unixa.
proc - identyfikator procesu lub grupy gniazd
write_queue, receive_queue - kolejki buforow sk_buf
prot - wskaznik na strukture proto dla odpowiedniego 
	protokolu
daddr - adres stacji-partnera (32 bitowy id-sieci/
	id-stacji); 
do lub z funkcji systemowych przekazywany razem ze 
	struktura sockaddr_in w polu sin_addr.s_addr
saddr - adres wysylajacego 
rcv_saddr - zwykle ten sam co wyzej; wyjatek w przypadku 
	opcji broadcast lub mulitcast 
mtu - max rozmiar transmisji (nie mozna przesylac wiekszych 
	pakietow niz mtu)
mss - wykorzystywany przy ustalaniu mtu
user_mss - ustalony mtu przez uzytkownika
num - klucz do tablicy haszujacej sock_array	
	w strukturze proto
err, err_soft - kody bledow
protocol - identyfikator protokolu
state - status gniazda; patrz stale statusow
ack_backlog - biezaca liczba polaczen
max_ack_backlog - max liczba polaczen (podawane przy
	listen)
debug - ustawione na niezerowa wartosc powoduje wypisywanie 
	komunikatow
type - type gniazda (SOCK_DGRAM, SOCK_STREAM,...)
socket - wskaznik do struktury socket

Porownaj z dziedzina Unixa.


Struktura sockaddr_in

Patrz: nazwy gniazd.


Struktura sk_buff (Internet)

Dokladnie ta sama struktura co w dziedzine Unixa i o analogicznym znaczeniu. W niej mozna zapisywac dane do wyslania i odebrania. Umozliwia tworzenie list i na takie listy wskazuja pola write_queue i recv_queue struktury sock. Czesc pol sk_buff jest wykorzystywana przez protokoly nizszych warstw.


Bibliografia

  1. Pliki zrodlowe jadra Linux-a 2.0.0.
  2. R. Stevens: "Programowanie zastosowan sieciowych w systemie Unix".
  3. Dokumentacja biblioteki libc dostepna w /usr/info/.
  4. Kernel Hackers Guide.

Pytania i odpowiedzi

Gniazda sa baaaardzo interesujacym i calkiem latwym tematem, dlatego nikt nie zadal mi zadnych pytan.


Autor: Pawel Gorecki