![socket数据发送过程zz_第1页](http://file3.renrendoc.com/fileroot_temp3/2022-4/16/863c503b-f419-45d2-b27a-74857b1202b9/863c503b-f419-45d2-b27a-74857b1202b91.gif)
![socket数据发送过程zz_第2页](http://file3.renrendoc.com/fileroot_temp3/2022-4/16/863c503b-f419-45d2-b27a-74857b1202b9/863c503b-f419-45d2-b27a-74857b1202b92.gif)
![socket数据发送过程zz_第3页](http://file3.renrendoc.com/fileroot_temp3/2022-4/16/863c503b-f419-45d2-b27a-74857b1202b9/863c503b-f419-45d2-b27a-74857b1202b93.gif)
![socket数据发送过程zz_第4页](http://file3.renrendoc.com/fileroot_temp3/2022-4/16/863c503b-f419-45d2-b27a-74857b1202b9/863c503b-f419-45d2-b27a-74857b1202b94.gif)
![socket数据发送过程zz_第5页](http://file3.renrendoc.com/fileroot_temp3/2022-4/16/863c503b-f419-45d2-b27a-74857b1202b9/863c503b-f419-45d2-b27a-74857b1202b95.gif)
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、.socket数据发送过程zz本文在基于以下三个条件所写的:1OSI七层网络通信模型。2所阐述的函数是基于Linux2.6.1内核。3在面向连接的通信协议TCP/IPV4的根底上。由于七层模型应用层,表示层,会话层,传输层,网络层,数据链路层,物理层可以简化为以下五层构造应用层Application Layer,传输层Transport Layer,网络层Network Layer,数据链路层Data Link Layer,物理层Physical Layer.其中七层模型中的前三层都归结为五层构造中的应用层。为了简化讨论,本文主要从这五层构造来讨论。Layer 5:应用层Application
2、 Layer在TCP协议上,当通过三方握手建立了连接之后,就进入数据包的本质发送阶段,在本文中以send命令来阐述。当通过send将数据包发送之后,glibc函数库会启用另外一个其定义的别用名函数_libc_sendto,该函数最后会间接执行到sendto系统调用:inline_syscall#nrname,args;/#nr说明是该系统调用带有nr个args参数sendto系统调用的参数值是6,而name就是sendto从上面的分析可以看出glibc将要执行的下面一条语句是inline_syscall6name,arg1,arg2,arg3,arg4,arg5,arg6在该函数中一段主要功能
3、实现代码如下:_asm_ _volatile_"callsys#%0%1=%2%3%4%5%6%7%8":inline_syscall_r0_out_constraint_sc_0,"=r"_sc_19,"=r"_sc_16,"=r"_sc_17,"=r"_sc_18,"=r"_sc_20,"=r"_sc_21:"0"_sc_0,"2"_sc_16,"3"_sc_17,"4"_
4、sc_18,"1"_sc_19,"5"_sc_20,"6"_sc_21:inline_syscall_clobbers;_sc_ret=_sc_0,_sc_err=_sc_19;该代码采用了嵌入汇编详细介绍查阅嵌入汇编相关书籍,其中:_sc_0=sendto;_sc_19-_sc_21分别是arg1-arg6;inline_syscall_r0_out_constraint:功能相当于"=r",选用一个存放器来存储输出变量。"0"-"6"分别是%0-%6,代表_sc_0-_s
5、c_21接下来函数最终通过Linux中顶顶有名的INT 0X80陷入系统核心。详细的过程可以参考内核相关书籍。下面是一个兄弟对INT 0X80的简要介绍:在陷入系统内核以后,最终会调用系统所提供的系统调用函数sys_sendto,该函数直接调用了_sock_sendmsg,该函数对进程做一个简单的权限检查之后就触发套接字socket中定义的虚拟sendmsg的函数,进而进入到下一层传输层处理。Layer 4:传输层Transport Layer由上层的讨论可知,系统触发了sendmsg虚拟接口函数,其实就是传输层中的tcp_sendmsg或是udp_sendmsg,看你所使用的协议而定。本文介
6、绍tcp_sendmsg.该函数需要做如下工作:1为sk_buff后面简称skb分配空间,该函数首先尝试在套接字缓冲队列中寻找空闲空间,假设找不到就使用tcp_alloc_pskb为其重新分配空间。2下面这步就会tcp_sendmsg函数的主要部分了,将数据拷贝到缓冲区。它分为如下两种情况:2.1假设skb还有剩余空间的话,就使用skb_add_data来向skb尾部添加数据包。代码如下:ifskb_tailroomskb0/*We have some space in skb head.Superb!*/ifcopy skb_tailroomskbcopy=skb_tailroomskb;i
7、ferr=skb_add_dataskb,from,copy!=0 gotodo_fault;2.2假设skb没有了可用空间,内核会使用TCP_PAGE宏来为发送的数据包分配一个高速缓存页空间,当该页被正确地分配后就调用Copy_from_usertopage地址,fromusr空间,n将用户空间数据包复制到page所在的地址空间。但是我们都知道数据包在协议层之间的传输是通过skb的,难道将数据包复制到这个新分配的page中,内核就可以去睡大觉了吗?当然不是!接下来内核就要来处理这个问题了,那么怎样来处理呢?此时就需要使用到skb中的另外一个数据区struct skb_shared_info,
8、但是该数据区在创立skb时是没有为其分配空间的,也就是说它开场纯粹就是个指针,而没有详细的告诉它要指向什么地方。这时大家应该知道它可以指向什么地方了,对,就是page!在内核中对这种情况的详细是通过fill_page_descstruct sk_buff*skb,int I,struct page*page,int off,int size来实现的,代码如下:staticinlinevoidfill_page_descstruct sk_buff*skb,int i,struct page*page,int off,int sizeskb_frag_t*frag=&skb_shinfo
9、skb-fragsi;frag-page=page;frag-page_offset=off;frag-size=size;skb_shinfoskb-nr_frags=i+1;这里需要注意的是struct skb_shared_info只能通过skb_shinfo来获取,在该构造体中skb_flag_t类型的flagsi就是详细指向page的数组。2.3至此skb数据包的装载工作算是完毕了,接下来就需要做一些后续工作,包括是否要分片,以及后来的TCP协议头的添加。先看在tcp_sendmsg中的最后一个重要函数tcp_push,它的调用格式如下:staticinlinevoidtcp_pus
10、hstruct sock*sk,struct tcp_opt*tp,int flags,int mss_now,int nonagle细心的朋友会发现,在该函数中传输的竟然不是skb,而是一个名为sock的构造体,那这又是什么东东呢?个人理解是它在顶层协议层之间例如:应用层和传输层之间的传输起着非常重要的作用,相当于沟通两层之间的纽带。再深化查找下该构造体的构成,我们很容易发现这样一个构造体变量:struct sk_buff_head,有名称我们可以知道它是用来描绘skb头部信息的一个构造体,它指向了buffer的数据区。这下我们也明白了点,这个构造体其实还充当了一个队列作用,是用来存储skb
11、的数据区。协议层之间传输完之后,详细到该层处理时内核就会从sk_buff_head逐个中取出skb数据区来处理,例如添加协议头等。好了,tcp_sendmsg到此完毕了它的使命了,下面将要需要的一个函数就是在tcp_push中直接用到的一个函数:_tcp_push_pending_frames,该函数又直接调用tcp_write_xmit函数来进一步对数据包处理,它包括一下两步:1检查是否需要对数据包进展分片,条件是只要skb中全部数据长度大于当前路由负荷量就需要分片。2采用skb_cloneskb,GFP_ATOMIC为TCP_HEAD分配一个sk_buff空间,这里需要注意的是skb_cl
12、one分配空间的特点,它首先是按照参数skb来来复制出一个新的sk_buff,新的skb和旧的skb共享数据变量缓存区,但是构造体缓冲区不是共享的,这似乎和copy on write机制有些相似。3在分配了一个新的skb之后,内核就会执行tcp_transmit_skb.其实内核中是将2,3步合在一起的,如下:tcp_transmit_skbsk,skb_cloneskb,GFP_ATOMIC接下来就是tcp_transmit_skb函数的实现过程了。1通过skb_push在skb前面参加tcp协议头信息。这包括序列号,源地址,目的地址,校验和等。2通过tcp_opt构造体它是在该函数的开场部
13、分从sock构造体中获得的tcp_func构造体中的.queue_xmit虚拟功能函数,在IPV4中是调用了ip_queue_xmit,这样就进入了下一层-网络层。Layer 3网络层Network Layer在ip_queue_xmit函数中需要做的事情有一下几件:1是否需要将数据包进展路由,假设需要的话就跳到包路由子程序段。判断是否需要路由是由如下语句执行的:rt=struct rtable*skb-dst;ifrt!=NULLgoto packet_routed;在skb的dst变量中指明发送目的地址。它存放了路由途径中的下台主机地址。假设是需要对数据包进展路由,那么其执行分如下步骤1.
14、1使用skb_push在skb前面插入一段ip_headsize大小的空间。1.2填写ip协议头,包括ttl,protocol等1.3写入校验和,最后调用NF_HOOK宏,关于NF_HOOK后面介绍。调用的NF_HOOK宏语句如下:NF_HOOKPF_INET,NF_IP_LOCAL_OUT,skb,NULL,rt-u.dst.dev,dst_output;2假设没有路由地址,内核会尝试从外部可选项中来获取该地址,此时传输层发现没有路由地址会不断地发出重发机制,直到路由地址获取到。当获取到路由地址之后,内核会通过以下语句重新将地址赋给skb-dst.之后就会进入到1所述的路由子程序段执行。sk
15、b-dst=dst_clone&rt-u.dst;所以这样看来正常情况下内核都会进入1.3所阐述的NF_HOOK宏的执行。关于NF_HOOK宏,我也不怎么理解,但是查了下内核后可以大体的知道,当二维数组nf_hookspfhook其下标分别是调用宏中的第一个和第二个参数中定义了需要的钩子函数时,就会调用nf_hook_slow函数来处理,假设没有定义钩子函数就直接调用NF_HOOK中的最后一个参数所指向的函数,在这里是:dst_outputskb。在网上搜了下,发现一篇讲解NF_HOOK的帖子,很详细,链接如下:上面已经谈到,当存在钩子函数时,内核转向nf_hook_slow函数来处理
16、。下面阐述下这个函数:1检查hook函数是否真的已经设置,假设没有设置就将hook对应位通过移位来设置;当确认已经设置后就取出该钩子函数,如下:elem=&nf_hookspfhook;2执行nf_iterate函数,该函数采用list_for_each_continue_rcuHOOK链表中的每个nf_hook_ops钩子构造体,通过其内部变量priority来判断它的优先级是否大于系统所定义的INT_MIN,假设小于就继续搜索,否那么就执行该构造体单元中所指向的钩子函数。ifhook_thresh elem-prioritycontinue;/*Optimization:we do
17、n't need to hold module reference here,since function can't sleep.-RR*/switchelem-hookhook,skb,indev,outdev,okfn。当钩子函数成功执行之后,它会返回一个NF_ACCEPT标志,3判断nf_iterate函数的返回标志,如下:switchverdictcase NF_ACCEPT:ret=okfnskb;break;case NF_DROP:kfree_skbskb;ret=-EPERM;break;由上面的代码可以看到,当标志是NF_ACCEPT时,内核会继续调用okf
18、nskb函数,也就是传递给NF_HOOK的最后一个参数dst_outputskb。该函数非常简单,就是间接启用和skb相关的output函数,如下:for;err=skb-dst-outputskb;iflikelyerr=0return err;ifunlikelyerr!=NET_XMIT_BYPASSreturn err;内核这句skb-dst-outputskb,就将skb打入到了下面的一层-数据链路层.Layer 2数据链路层Data Link Layer上层的output函数最终会触发链路层中的dev_queue_xmitskb函数。在该函数中需要做的事情如下:1对传输过来的skb
19、包进展检查,主要是:1.1数据包有分散的数据片段即skb_infoskb-nr_frags 0,但是接口不能传输这样的数据包片段即dev-features中没有设置NETIF_F_FRAGLIST,这个时候内核就会执行数据包线性化函数_skb_linearizeskb,GFP_ATOMIC,简单来说该函数就是将skb中的数据片段存储到由内核所创立的一个缓冲区中,并释放掉原来的skb数据区,将skb指向新分配的数据缓冲区。1.2和上面的条件很相似,不过还添加了一个判断条件,那就是设备是否在高内存缓冲区,并且设备又不支持DMA对数据的存取,此时也需要将数据包线性化。2假设包没有实现IP校验,就需要
20、再次对数据包检验。3启用qdisc_rundev,该函数检查网卡是否可以接收数据,假设不可以就重新检查直到可以发送为止,假设可以就调用qdisc_restart来详细实现。qdisc_restart的实现如下:3.1检查dev中数据包队列是否为空,假设不为空就试图获取驱动程序的使用权限,当网卡可以接收数据包时就调用dev-hard_start_xmitskb,dev来执行驱动程序的数据包发送函数。3.2假设没有获取到驱动程序的使用权限,这中情况一般是在调用hard_start_xmitskb,dev时出现了暂时的配置错误。这时可以检查下驱动程序在被什么使用,假设是死循环的话,将数据包丢弃!3.
21、3执行netif_scheduledev,在该函数之后的情况我就不再多说了,有一个网友写的很精彩,链接如下:至此,各协议层的数据包发送过程就算是全部完成了,接下来就进入到驱动程序的详细介绍。网卡底层驱动开发1驱动模块的加载module_initfn在驱动的开发之中,大家都知道是从module_initfn开场的,该内核宏允许你添加自定义的初始化函数。这里略微扯远点,看下module_init是如何在内核中实现的,展开如下:#define module_init(;x);_initcall(;x);#define _initcall&
22、;#40;fn);device_initcall(;fn);#define device_initcall(;fn);_define_initcall(;"6",fn);#define _define_initcall(;level,fn);static initcall_t _initcall_#fn _attribute_used_attribute_(;(;_section_(;".initc
23、all"level".init");););=fn虽然很长,其实就是做了一件事情,说明了系统最终调用的初始化函数为initcall_t _initcall_#fn#fn用fn代替即可,在内核启动的过程中do_initcalls函数会调用该初始化函数。当然在以上宏定义中还给出一些关于初始化函数的其它信息:_attribute_used_attribute_(;(;_section_(;".initcall"这里的_attribute_、used、_section_
24、都是GNU编译器的保存字。_attribute_:表示属性,也就是赋予它所修饰的变量或函数后面指定的属性;used:表示该变量或函数代码的执行过程中会被用到;_section_(;".initcall":指将其所修饰的变量或函数编译进.initcall段。那么initcall段的地址在什么地方呢?它包括在_initcall_start到_initcall_end区间里,在arch/i386/kernel/vmlinux.lds.S中找到可以找到该变量。好了,言归正传。接下来内核将要调用初始化函数fn了。2初始化函数fn,在该函数中需要做的事情如下:2.1为对应
25、的网络设备例如:ether,Wlan等分配net_device构造体alloc_netdev或是alloc_etherdev,关于这两个函数其实很相似,后者也是直接调用了alloc_netdev最终实现。不同之处在于:后者使调用alloc_netdev时使用了内核所提供的初始化函数ether_setup;前者使用的是程序员自定义的函数xxx_setupalloc_netdev的实现代码如下:alloc_size=sizeof*dev+sizeof_priv+31;dev=struct net_device*kmallocalloc_size,GFP_KERNEL;ifdev=NULLprint
26、kKERN_ERR"alloc_dev:Unable to allocate device memory.n";return NULL;memsetdev,0,alloc_size;ifsizeof_privdev-priv=void*longdev+1+31&31;setupdev;strcpydev-name,mask;从代码可以看出alloc_netdev函数间接调用了上层函数所提供的setup函数来初始化dev构造体。到此大家都知道了初始化函数中包括的是翻开,关闭设备,传输函数等各个主要变量的初始化。2.2通过register_netdevdev来注册一个已
27、经初始化好了的net_device设备。其实注册设备就是将dev链接到内核中的netdev的链表之中。当使用ifconfig来为一个网络设备配置地址时,内核ioctl函数就会设置dev-flag中的IFF_UP标志以翻开接口,当IFF_UP设置之后,内核就将调用open函数。3接口的翻开接口翻开函数中需要做的工作如下:3.1设置MAC地址,一般来说接口是不支持硬件地址改变的,所以就没有必要自定义MAC地址设置函数,而只需要采用默认设置。默认设置是在eth_setup函数中赋予的,就是将dev-set_mac_address设置为eth_mac_add,该函数首先会判断接口是否在工作,只有不在工
28、作时才会启用设置命令memcpy,如下所示:struct sockaddr*addr=p;ifnetif_runningdevreturn-EBUSY;memcpydev-dev_addr,addr-sa_data,dev-addr_len;3.2必要时使用端口申请request_region.为什么说是必要时呢?因为端口申请的目的就是使得进程可以独享IO端口访问权限,不至于出现资源争用。但是当你能确定IO端口只是被单个进程使用时,就可以省去该步骤。但是为了程序的强健性考虑,还是加上这个函数为妙。下面简单介绍下request_region,他的调用格式如下:requset_regionstar
29、t,size,name调用该函数时时确定start-start+size地址空间是否可以使用,当确认可以使用之后就调用端口读写函数来对端口进展访问。一些端口读写函数如下:void insbunsigned port,void*addr,unsigned long count;void outsbunsigned port,void*addr,unsigned long count;读或写从内存地址addr开场的count字节.数据读自或者写入单个port端口.void inswunsigned port,void*addr,unsigned long count;void outswunsig
30、ned port,void*addr,unsigned long count;读或写16-位值到一个单个16-位端口.void inslunsigned port,void*addr,unsigned long count;void outslunsigned port,void*addr,unsigned long count;读或写32-位值到一个单个32-位端口.还补充一点:start的值是在驱动程序中赋予的,一般驱动程序都会定义一个ion,接着通过MODULE_PARM将其输出,这样在用户在通过insmod加载模块时可以将该参数传递进来。3.3中断申请中断申请使用的时request_irq函数。该函数的调用形式如下:int request_irqunsigned int irq,void*handlerint irq,void*dev_id,struct pt_regs*regsirq是要申请的硬件中断号。在Intel平台,范围0-15。handler是向系统登记的中断处理函数。这是一个回调
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 男性在商业领域的个人成长规划
- 电子商务物流配送的技术创新与应用
- 学校月度工作总结
- 电子商务中的价格策略与运营管理
- 地理-湖南省佩佩教育湖南省长沙市四大名校2025届高三下学期2月联考试题和答案
- 现代建筑设计与科技创新的互动关系
- 知识经济时代的科技创新与知识产法保护
- 会员推广合作协议书范本
- 国企入职考试同步测试(附答案)
- 网络服务实施协议书(2篇)
- GB/T 44865-2024物联网基于物联网和传感网技术的动产监管集成平台系统要求
- 2023年贵州省公务员录用考试《行测》真题及答案解析
- 体育赛事招商服务收费方案
- 燃气公司绩效考核管理制度
- 2025年高考数学总复习:集合与常用逻辑用语(教师卷)
- 肌力分级护理课件
- 第三章-自然语言的处理(共152张课件)
- 中学教学课件:下第课《认识人工智能》课件
- 《乌有先生历险记》注释版
- 2023版初中语文新课程标准
- 六年级口算训练每日100道
评论
0/150
提交评论