linux内核中socket的实现Word格式文档下载.docx
《linux内核中socket的实现Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《linux内核中socket的实现Word格式文档下载.docx(23页珍藏版)》请在冰豆网上搜索。
31sys_getsockname(a0,(structsockaddr__user*)a1,
32(int__user*)a[2]);
33break;
34.....................................
35returnerr;
36}
可以看到代码比较简单,就是通过传递进来的call类型,来调用相应的socket相关的函数.
这里你可能注意到了,那就是一般文件句柄相关的操作,比如write,read,aio,poll这些并没有看到(也就是file_operations).这是因为socket上面其实还有一层vfs层,内核把socket当做一个文件系统来处理,并实现了相应的vfs方法.因此下面我们先来了解下vfs.然后会描述下进程如何通过vfs存取句柄.
vfs其实就相当于对下层的文件系统和上层应用之间的粘合层,它定义了文件系统需要实现的相关的操作,然后下层的文件系统只需要实现这些方法就可以了,也就是说在内核其他部分和上层应用看来,所有的文件系统没有任何区别.
下面的这张图就是从用户空间调用write的大体流程:
vfs中有4种主要的数据结构:
1超级块对象,代表一个已安装的文件系统.super_block
2索引节点对象,代表一个文件.inode
3目录项对象,代表一个目录项.dentry
4文件对象,表示一个被进程打开的文件.file
其中每种对象都包含一个操作对象.依次为super_operations,inode_operations,dentry_operations以及file_operations.各自操作不同的层次.然后我们的文件系统只需要实现这些方法,然后注册到内核就可以了.
接下来我们来看和vfs相应的结构:
第一个就是file_system_type结构,这个结构表示了一个文件系统:
37structfile_system_type{
38constchar*name;
39intfs_flags;
40///最关键的函数,得到文件系统的超级块.
41int(*get_sb)(structfile_system_type*,int,
42constchar*,void*,structvfsmount*);
43void(*kill_sb)(structsuper_block*);
44...........................................
45};
然后是vfsmount结构,它表示了一个安装点,换句话说也就是一个文件系统实例.
第三个是files_struct结构,它主要是为每个进程来维护它所打开的句柄.这里只需要注意一个就是fd_array和fstable中的fd的区别.当进程数比较少也就是小于NR_OPEN_DEFAULT(32)时,句柄就会存放在fd_array中,而当句柄数超过32则就会重新分配数组,然后将fd指针指向它(然后我们通过fd就可以取得相应的file结构).
而且files_struct是每个进程只有一个的.
46structfiles_struct{
47/*
48*readmostlypart
49*/
50atomic_tcount;
51structfdtable*fdt;
52structfdtablefdtab;
53/*
54*writtenpartonaseparatecachelineinSMP
55*/
56spinlock_tfile_lock____cacheline_aligned_in_smp;
57intnext_fd;
58structembedded_fd_setclose_on_exec_init;
59structembedded_fd_setopen_fds_init;
60///所打开的所有文件
61structfile*fd_array[NR_OPEN_DEFAULT];
62};
63
64
65structfdtable{
66unsignedintmax_fds;
67structfile**fd;
/*currentfdarray*/
68fd_set*close_on_exec;
69fd_set*open_fds;
70structrcu_headrcu;
71structfdtable*next;
72};
还有两个一个是fs_struct,一个是namespace也都是进程相关的.这里就不一一介绍了.
我这里vfs介绍只是个大概,需要详细了解的,可以去看ulk的vfs相关章节和linux内核设计与实现的相关章节.
因此下面的图表示了进程和socket的关系:
上面的这张图有些老了,新的内核中的inode节点中已经没有u这个联合体了,对应的是会有一个包含socket和inode的一个结构体,然后我们通过inode,而inode中专门有个i_mode域来判断相应的inode类型,比如socket就是S_IFSOCK.就可以直接计算出相应的socket的地址,然后就可以存取socket了.后面我们会介绍.
内核中标售socket有两个数据结构,一个是socket,另一个是sock,其中socket是一个generalBSDsocket,它也就是应用程序和4层协议之间的一个接口,屏蔽掉了相关的4层协议部分.而在内核中,socket所需要使用的相关的4层协议的信息全部是保存在sock结构当中的,而socket和sock这两个结构都有保存对方的指针,因此可以很容易的存取对方.
还有一个就是ops域,这个域保存了所有的相关的4层协议的操作函数..
而在sock中有一个sk_common保存了一个skc_prot域,这个域保存的是相应的协议簇的操作函数的集合.
后面介绍到socket创建的时候,我们会分析proto_ops和proto的区别.其实proto相当于对proto_ops的一层封装,最终会在proto中调用proto_ops.
73/**
74*structsocket-generalBSDsocket
75*@state:
socketstate(%SS_CONNECTED,etc)
76*@type:
sockettype(%SOCK_STREAM,etc)
77*@flags:
socketflags(%SOCK_ASYNC_NOSPACE,etc)
78*@ops:
protocolspecificsocketoperations
79*@fasync_list:
Asynchronouswakeuplist
80*@file:
Filebackpointerforgc
81*@sk:
internalnetworkingprotocolagnosticsocketrepresentation
82*@wait:
waitqueueforseveraluses
83*/
84structsocket{
85socket_statestate;
86shorttype;
87unsignedlongflags;
88conststructproto_ops*ops;
89structfasync_struct*fasync_list;
90structfile*file;
91structsock*sk;
92wait_queue_head_twait;
93};
94
95structsock_common{
96unsignedshortskc_family;
97volatileunsignedcharskc_state;
98unsignedcharskc_reuse;
99intskc_bound_dev_if;
100structhlist_nodeskc_node;
101structhlist_nodeskc_bind_node;
102atomic_tskc_refcnt;
103unsignedintskc_hash;
104structproto*skc_prot;
105#ifdefCONFIG_NET_NS
106structnet*skc_net;
107#endif
108};
109
110structproto_ops{
111intfamily;
112structmodule*owner;
113int(*release)(structsocket*sock);
114int(*bind)(structsocket*sock,
115structsockaddr*myaddr,
116intsockaddr_len);
117int(*connect)(structsocket*sock,
118structsockaddr*vaddr,
119intsockaddr_len,intflags);
120...................................................
121};
然后我们来看sock_init的实现,在这个函数中,将socket注册为一个伪文件系统,并安装相应的mount点:
122///相应的mount对象
123staticstructvfsmount*sock_mnt__read_mostly;
124///文件系统对象.
125staticstructfile_system_typesock_fs_type={
126.name="
sockfs"
127.get_sb=sockfs_get_sb,
128.kill_sb=kill_anon_super,
129};
130
131staticint__initsock_init(void)
132{
133/*
134*InitializesockSLABcache.
135*/
136
137sk_init();
138
139/*
140*InitializeskbuffSLABcache
141*/
142skb_init();
143
144///初始化一个inodecache.
145init_inodecache();
146///注册文件系统到内核.
147register_filesystem(&
sock_fs_type);
148///安装mount点.
149sock_mnt=kern_mount(&
150
151#ifdefCONFIG_NETFILTER
152netfilter_init();
153#endif
154return0;
155}
我们知道每次创建一个socket,都是要依赖于当前的protocolfamily类型的(后面会分析sys_socket的源码的时候会看到).而在内核中,每种类型的protocolfamily都会有一个相对应的net_proto_family结构,然后将这个结构注册到内核的net_families数组中,这样我们创建socket的时候,就可以调用这个数组来创建socket.
我们先来看sock_register的源码,也就是如何将一个net_proto_family注册到相应的数组:
156staticconststructnet_proto_family*net_families[NPROTO]__read_mostly;
157
158intsock_register(conststructnet_proto_family*ops)
159{
160interr;
161
162if(ops->
family>
=NPROTO){
163printk(KERN_CRIT"
protocol%d>
=NPROTO(%d)\n"
ops->
family,
164NPROTO);
165return-ENOBUFS;
166}
167
168spin_lock(&
net_family_lock);
169///代码非常简单,就是根据类型,然后放到相应的位置.
170if(net_families[ops->
family])
171err=-EEXIST;
172else{
173net_families[ops->
family]=ops;
174err=0;
175}
176spin_unlock(&
177
178printk(KERN_INFO"
NET:
Registeredprotocolfamily%d\n"
family);
179returnerr;
180}
我们知道每个协议簇和相应的套接口都对应有好多种组合,因此在协议簇的实现中保存了一个相应的结构来保存这些组合,然后后面就首先通过family然后确定到某个结构,再根据套接口的类型来得到这个结构,并赋值给sock.
这里要注意我们只分析af_inet的实现,其他的协议簇都差不多:
我们来看这个的实现:
181///可以看到这是一个数组,每个元素都是一个链表,也就是每种类型的socket就是一个链表.而这个链表所包含的是不同4层协议的inetsw.可是在inet中,现在每种类型的socket只对应一个4层协议.这里只是为了以后扩展.
182staticstructlist_headinetsw[SOCK_MAX];
183
184///相应的socket的对应的信息的结构.
185structinet_protosw{
186structlist_headlist;
187
188///需要这两个key才能定位一个inet_protosw.
189unsignedshorttype;
/*Thisisthe2ndargumenttosocket
(2).*/
190unsignedshortprotocol;
/*ThisistheL4protocolnumber.*/
191
192///相应的基于ipv4的4层协议的操作集合.
193structproto*prot;
194///相应的协议簇的操作信息.
195conststructproto_ops*ops;
196
197intcapability;
/*Which(ifany)capabilitydo
198*weneedtousethissocket
199*interface?
200*/
201charno_check;
/*checksumonrcv/xmit/none?
*/
202unsignedcharflags;
/*SeeINET_PROTOSW_*below.*/
203};
204
205voidinet_register_protosw(structinet_protosw*p)
206{
207structlist_head*lh;
208structinet_protosw*answer;
209intprotocol=p->
protocol;
210structlist_head*last_perm;
211.............................................
212answer=NULL;
213last_perm=&
inetsw[p->
type];
214///这个操作也很简单,就是将inet_protosw根据套接口类型插入到全局链表数组.
215list_for_each(lh,&
type]){
216answer=list_entry(lh,structinet_protosw,list);
217
218/*Checkonlythenon-wildmatch.*/
219if(INET_PROTOSW_PERMANENT&
answer->
flags){
220if(protocol==answer->
protocol)
221break;
222last_perm=lh;
223}
224
225answer=NULL;
226}
227if(answer)
228gotoout_permanent;
229///插入链表.
230list_add_rcu(&
p->
list,last_perm);
231..............................
接下来来分析inet_init的源码.
232///表示了所有的可能的当前协议簇和套接口类型的组合.
233staticstructinet_protoswinetsw_array[]=
234{
235{
236.type=SOCK_STREAM,
237.protocol=IPPROTO_TCP,
238.prot=&
tcp_prot,
239.ops=&
inet_stream_ops,
240.capability=-1,
241.no_check=0,
242.flags=INET_PROTOSW_PERMANENT|
243INET_PROTOSW_ICSK,
244},
245
246{
247.type=SOCK_DGRAM,
248.protocol=IPPROTO_UDP,
249.prot=&
udp_prot,
250.ops=&
inet_dgram_ops,
251.capability=-1,
252.no_check=UDP_CSUM_DEFAULT,
253.flags=INET_PROTOSW_PERMANENT,
254},
255
256
257{
258.type=SOCK_RAW,
259.protocol=IPPROTO_IP,/*wildcard*/
260.prot=&
raw_prot,
261.ops=&
inet_sockraw_ops,
262.capability=CAP_NET_RAW,
263.no_check=UDP_CSUM_DEFAULT,
264.flags=INET_PROTOSW_REUSE,
265}
266};
267
268///协议簇的创建函数.
269staticstructnet_proto_familyinet_family_ops={
270.family=PF_INET,
271.create=inet_create,
272.owner=THIS_MODULE,
273};
274
275staticint__initinet_init(void)
276{
277.............................................