这篇文档是基于x86体系结构和转发IP分组的。
接收分组
1接收中断
假如网卡收到一个和自己MAC地址匹配或链路层广播的以太网帧,它还会形成一个中断。此网卡的驱动程序会处理此中断:
2softirq和bottomhalf
内核2.4之后linux系统命令,整个合同栈不再使用bottomhalf(下半文,没找到好的翻译),而是被软中断softirq代替。软中断softirq优势显著,可以同时在多个CPU上执行;而bottomhalf一次只能在一个CPU上执行,即在多个CPU执行时严格保持串行。
中断服务程序常常都是在CPU关中断的条件下执行的,以防止中断嵌套而使控制复杂化。并且CPU关中断的时间不能太长,否则容易遗失中断讯号。因此linux内核协议栈,Linux将中断服务程序一分为二,各叫做“TopHalf”和“BottomHalf”。后者一般对时间要求较为严格,必须在中断恳求发生后立刻或起码在一定的时间限制内完成。因而为了保证这些处理能原子地完成,TopHalf一般是在CPU关中断的条件下执行的。具体地说,TopHalf的范围包括:从在IDT中登记的中断入口函数仍然到驱动程序注册在中断服务队列中的ISR。而BottomHalf则是TopHalf按照须要来调度执行的,这种操作容许延后到稍后执行,它的时间要求并不严格,因而它一般是在CPU开中断的条件下执行的,例如网路底层操作就是这样,因为个别缘由,中断并没有立即响应,而是先记录出来,等到可以处理这种中断的时侯就一块处理了。并且,Linux的这些BottomHalf(以下简称BH)机制有两个缺点,也即:
(1)在任意一时刻,系统只能有一个CPU可以执行BottomHalf代码,以避免两个或多个CPU同时来执行BottomHalf函数而互相干扰。因而BH代码的执行是严格“串行化”的。
(2)BH函数不容许嵌套。
这两个缺点在单CPU系统中是无关紧要的,但在SMP系统中却是极其致命的。由于BH机制的严格串行化执行似乎没有充分借助SMP系统的多CPU特性。因此,Linux2.4内核在BH机制的基础上进行了扩充,这就是所谓的“软中断恳求”(softirq)机制。Linux的softirq机制是与SMP紧密不可分的。因此,整个softirq机制的设计与实现中自始自终都贯彻了一个思想:“谁触发,谁执行”(Whomarks,Whoruns),也即触发软中断的那种CPU负责执行它所触发的软中断,但是每位CPU都由它自己的软中断触发与控制机制。这个设计思想也促使softirq机制充分借助了SMP系统的性能和特性。
3NET_RX_SOFTIRQ网路接收软中断
这一阶段会按照合同的不同来处理数据分组。CPU开始处理软中断do_softirq(),,接着net_rx_action()处理上面标记的NET_RX_SOFTIRQ,把出对列的skb送入相应列表处理(按照合同不同到不同的列表)。例如,IP分组交给ip_rcv()处理,ARP分组交给arp_rcv()处理等。
4处理IPv4分组
下边以IPv4为例linux内核协议栈,讲解IPv4分组在高层的处理。
linux内核合同栈之网路层
ip_rcv()函数验证IP分组,例如目的地址是否本机地址,校准和是否正确等。若正确,则交给netfilter的NF_IP_PRE_ROUTING钩子(关于netfilter细节可以参考HackingtheLinuxKernelNetworkStack);否则,扔掉。到了ip_rcv_finish()函数,数据包就要按照skb结构的目的或路由信息各奔东西了。
至此,数据分组的接受和处理工作就告一段落了,至于于此相对的数据分组的发送,我就贴个图吧,具体细节可参考TheLinux®NetworkingArchitecture:DesignandImplementationofNetworkProtocolsintheLinuxKernelPrenticeHallAugust01,2004
dev_queue_xmit()处理发送分组
附一张Linux2.4核的netfilter框架下分组的迈向图:
总结下:
1.中断处理函数中:
网卡收到一帧------------------------〉
引起中断-------------------〉
cpu调用相应的中断处理函数(指向此网卡驱动中的相应的处理函数)(把此packet读到ram中)--------------------〉
呼叫netif_rx函数来打上timestamp,并把此skb装入到cpu设置的队列中-----------------〉
标记软中断(__cpu_raise_softirq)---------------------〉中断完成。
2.当软中断被调用时(一共在三个地方调用),呼叫NET_RX_SOFTIRQ(虽然就是net_rx_action()函数)来处理网路方面的软中断。-----------------〉
net_rx_action()依据数据包的合同类型在链表ptype_base[16]里找到相应的合同,并从中晓得了接收的处理函数,之后把数据包交给处理函数,这样就交给了下层处理,实际调用处理函数是通过net_rx_action()里的pt_prev->func()这一句。比如假如数据包是IP合同的话,ptype_base[ETH_P_IP]->func()(ip_rcv()),这样就把数据包交给了IP合同。依据包的类型,查找系统中注册了的相应的包处理函数嵌入式linux驱动程序设计从入门到精通,对于ipv4的ip包,呼叫ip_rcv-------------〉
NF_IP_PRE_ROUTING--------------〉
ip_rcv_finish(进行对此包的路由操作)---------------------〉
依据路由的结果,呼叫ip_local_deliver(给本机的包)/ip_forwardd(要转发的包)/ip_error()(出现错误的包)/ip_mr_input()(多播包的处理)ip_forward:要进行转发的包,checkttl,mtu,callNF_IP_FORWARDS,--------------〉
ip_forwmard_finish(checkotheripoptionsforforwardd!)-------------------〉
ip_send(假如须要分片,则调用ip_fragment,否则调用ip_finish_output()(callNF_IP_POST_ROUTING,之后时ip_finish_output2(填充链路层颈部到skb结构中)--------------〉
hh->hh_output/dsr->neighbour->output