最近在看linux內核代碼,學習之餘記點筆記。但是,目前整個linux源碼文件大小是360M,相當於100個哈利波特全集-_-,看完的都是神仙了。所以我只準備粗略地看下它的TCP/IP協議棧,然後記點心得。
嗯,我研究的內核版本是目前最新的,linux-2.6.33.1,其實應該都大同小異。學習源碼時,首先應該把它的整體脈絡理清楚,然後再局部細看,這是最簡捷的。本文也是簡潔的歸納一下。
在linux下,socket都掛接到虛擬文件系統(VFS)上,所以可以把它當做文件來操作,如write()與read()都可以對它使用。
當我們調用fd = socket()操作時,傳遞過程從上到下如下所示:
VFS層:file
運輸層:socket
網絡層:sock
嗯,相信大家都知道了吧,VFS層上面就是應用層了,每本計算機網絡相關書籍總是會先講這幾個層級結構。其中file, socket, sock是對應層次的一個內核結構,file涉及到虛擬文件系統,所以不予剖析了。
我們先看看socket結構的定義:
struct socket { socket_state state; kmemcheck_bitfield_begin(type); short type; kmemcheck_bitfield_end(type); unsigned long flags; /* * Please keep fasync_list & wait fields in the same cache line */ struct fasync_struct *fasync_list; wait_queue_head_t wait; struct file *file; struct sock *sk; const struct proto_ops *ops; };
我們只關心最後3個結構成員,file就是對應上面的VFS層,sk則是下面的網絡層,而ops則指向一個函數指針集合,對應於該運輸層的操作,實際上它可指向3個集合:inet_stream_ops, inet_dgram_ops, inet_sockraw_ops。還記得我們創建socket時需要設定的type參數嗎,SOCK_STREAM,SOCK_DGRAM,SOCK_RAW,相應的參數使套接字在初始化時讓ops指向對應的函數指針集合。
下面就是這個函數指針集合的定義:
struct proto_ops { int family; struct module *owner; int (*release) (struct socket *sock); int (*bind) (struct socket *sock, struct sockaddr *myaddr, int sockaddr_len); int (*connect) (struct socket *sock, struct sockaddr *vaddr, int sockaddr_len, int flags); int (*accept) (struct socket *sock, struct socket *newsock, int flags); unsigned int (*poll) (struct file *file, struct socket *sock, struct poll_table_struct *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 __user *optval, unsigned int optlen); int (*getsockopt)(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen); int (*sendmsg) (struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t total_len); int (*recvmsg) (struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t total_len, int flags); 。。。。。。。
};
是不是都似曾相識呢,對,上面的都是套接字的常用操作。
最後,我們再看看網絡層,sock結構,只列舉了我覺得有用的成員,這個結構很長。
struct sock { struct sock_common __sk_common; struct sk_buff_head sk_receive_queue; struct sk_buff_head sk_write_queue; struct socket *sk_socket; void *sk_user_data; struct sk_buff *sk_send_head; void (*sk_state_change)(struct sock *sk); void (*sk_data_ready)(struct sock *sk, int bytes); void (*sk_write_space)(struct sock *sk); void (*sk_error_report)(struct sock *sk); int (*sk_backlog_rcv)(struct sock *sk, struct sk_buff *skb); void (*sk_destruct)(struct sock *sk); };
加下劃線的兩個函數指針,用於與更底層交互,當網卡有數據可讀可寫時用於回調。繼續鑽下去的話還會很深。最重要的是,它的頭部是一個sock_common 結構。嗯,這相當於一個面向對象的設計,sock_common就類似是一個基類,通過強制轉換可以轉化。還要關注下sk_buff_head結構,他是socket內部使用的緩衝區,鏈表形式,以後我再寫下它。
sock_common結構定義如下:同樣只是簡單列舉一些重要的。
struct sock_common { union { struct hlist_node skc_node; struct hlist_nulls_node skc_nulls_node; }; atomic_t skc_refcnt; int skc_tx_queue_mapping; unsigned short skc_family; volatile unsigned char skc_state; unsigned char skc_reuse; int skc_bound_dev_if; struct proto *skc_prot; };
好了,大概結構就是這樣的吧。。可以看到,就算是C語言,也運用了很多面向對象的思想。還有很抱歉,我本來準備畫圖說明的,結果還是惰性使然,就沒畫了。本來體系結構就是個很模糊的東西,關於數據包的接收與傳送,以及他們在這體系結構間的傳遞,下篇再講解。