网络协议栈14:Connect函数分解之网卡发送/接收数据流程
链路层经过对数据包的优先级进行排队后,把本次需要发送的数据包从优先级最高的队列的头部抽取出来,并下传给网卡驱动程序的数据发送函数,一般命名为xxx_hard_xmit(),由于硬件各个不同,写法也会各个不同,无法抽象出一个标准的写法,但其流程、目的是不变的,是可以抽像出来的,大致的流程如下:
1.通过设备结构体net_device中的tbusy(transmit busy)来判断网卡现在是否忙,如果等于1,表示网卡在忙,即目前仍然有数据在传输数据,此时是不能传输数据的,而是判断是否超时,如果未超时,则表示网卡正在忙,正在发送数据包,此时直接返回;如果是超时了,则重新初始化相关寄存器,之后设置 tbusy=0,然后继续下面的步骤。
2.设置tbusy = 1,准备传输数据,因为经过这样的设置,下面如果有数据需要发送时,遇到tbusy = 1,就会像重复第一步的判断。
3.硬件开始传输数据包,此时,主要是通过对网卡的寄存器的操作来完成的,一般的,我们会提供需要传送的数据的地址,以及数据的长度,然后经过芯片的寄存器把这些数据发送出去。
4.发送数据是一个for循环进行,一直发送,直到数据发送完才结束。结束发送数据后,就要把刚刚传过来的数据包释放掉,把相关的内存回收,以备后面继续使用。
5.最后,修改设备的一些统计信息,完成本次传输。
到此,connect函数从数据包的层层打包,传输,到最后从网卡上发送出去的流程,就完成了本地的所有传输,此后的数据,经过以太网的转发,送往远端的目的IP地址,此时,connect函数就开始睡眠,等待确认数据从远方传递回来。
Connect函数在等待确认数据的到达,即需要解决的问题是,数据如何从网卡被上传到链路层。这个过程也因为网卡的硬件各个不同,而无法有标准写法,只有一个标准的流程。
1. 当网卡接收到了一个完整的数据包后(硬件完成接收,而且是一个完整的数据包,即意味则网卡本身是有buffer的,是可以临时存储数据的),会发生一个中断信号给系统,这个中断信号是系统在启动时,由BIOS记录了跟一个中断处理程序关联到一起的,自然的,下面就是调用这个中断处理程序,来处理中断了。
2. 通过设备结构体net_device中的interrupt成员来判断网卡现在是否忙,如果interrupt = 1,表示有其他进程在运行中断处理程序,则退出,否则,继续下面的步骤。
3. 设置interrupt = 1,表示本进程在使用中断处理程序,其他的进程暂时不能使用。
4. 读取中断状态寄存器,根据状态寄存器判断中断发生的原因。有两种原因,一种是有新的数据包到达,另一种是上次传输的数据已经传输完成。
5. 如果有新的数据包到达,则调用接收数据包的子函数来接收数据。
6. 如果是上次数据传输完成引起的,则通知协议的上层,修改接口的统计数据,关闭tbusy标志位,为下次传输做准备。
7. 关闭标志位interrupt,表示本中断使用完成,现在其他进程可以使用中断处理函数了。
其中的第五步的调用子函数来接收数据的过程大致如下:
1. 申请skb缓冲区给准备接收的数据包,用于存储数据包
2. 从硬件中读取数据到skb缓冲区
3. 调用netif_rx()函数,把数据送往链路层。
4. 修改接口的统计数据。