深入理解Linux网络技术内幕学习笔记第十一章:帧的传输

接收和传输过程的相似性:

    poll_list是设备列表,其中的设备有帧要接收。output_queue是设备列表,其中的设备有信息要传输。poll_list和output_queue都是softnet_data中的两个字段。

    只有开启的设备(_ _LINK_STATE_START标识的设备)才能接受调度以准备接收数据。只有开启传输的设备(_ _LINK_STATE_XOFF标识清除的设备)才可以接受调度以准备传输。

    当设备接受调度准备接收时,_ _LINK_STATE_RX_SCHED标识会设置。当设备接受调度准备传输时,_ _LINK_STATE_SCHED标识会设置。

    

dev_queue_xmit函数:

   dev_queue_xmit是驱动程序执行传输时的接口。其被调用时,传输帧所需的信息已经准备好,该函数只有一个参数sk_buff。要传输帧时,内核会提供dev_queue_xmit函数来从设备出口队列中退出一个帧(qdisc_run实现),然后将该帧传递给hard_start_xmit方法。若dev_queue_xmit失败,内核提供了_ _netif_schedule(后面会介绍)来为调度设备以准备再次传输。dev_queue_xmit函数的主要任务如下:

    检查帧是否由一些片段组成,若设备无法处理这些片段,旧要把这些片段结合起来。

    计算L4的校验和,除非设备支持在硬件内计算此校验和。

    选择要传输的帧。具体传输哪个帧依赖于是否使用队列规则,以及出口队列的状态。此缓冲区可能不是下一个实际传输的缓冲区。

对于有队列设备,当设备队列规则存在时,可以通过dev->qdisc访问,输入帧会通过enqueue函数指针而排入队列,之后通过qidsi_run退出队列并传输。

对于无队列设备(当然也没有队列规则),传输失败时,驱动程序无法把缓冲区放回任何队列,因为没有队列,所以会被丢弃。以回环设备为例,每当一个帧传输时,会被立刻传递出去,因为没有队列可以让帧重新排入,如果出错了,就会被丢弃。回环设备没有输入队列,传输设备要完成两个任务:一端传输,一端接收。

_ _netif_schedule函数的主要任务有两个:

    把设备添加到output_queue(见第九章softnet_data结构)列表的头部,该列表表示设备有数据要传输(应该是有数据要传输到设备吧,总感觉翻译是不是有问题?)。不同于poll_list只用于非NAPI设备,oupput_queue可用于NAPI和非NAPI设备。设备被添加到该列表后会关闭中断功能。

    为NET_TX_SOFTIRQ软IRQ调度以准备执行。

注意,若设备已经进入调度准备传输,_ _netif_schedule函数啥也不会做。

队列规则接口:

几乎所有设备都会使用出口队列调度出口流量,而内核可以使用队列规则安排帧。每当设备进入调度以准备传输帧时,qdisc_run函数就会选出下一个要传输的帧,该函数的主要工作是调用qdisc_restart函数完成的。

qdisc_restart函数:

qdisc_restart从队列中通过dequeue函数指针取出一个帧。假设dequeue调用成功。传输一个帧需要取得两个锁:

    保护出口队列的锁(dev->queue_lock),由qdisc_restart的调用者dev_queue_xmit取得。

    驱动程序锁(dev->xmit_lock)。该锁由qdisc_restart取得。该锁准确来说应该叫设备锁,因为同一时间,可能有有其他cpu在操作设备。

注意,当队列退出一个缓冲区后,qdisc_restart不会马上释放dev->queue_lock,因为若无法取得dev->xmit_lock,需要重新将帧排入队列。

最后,启用dev->hard_start_xmit函数进行帧的传输。

最后从整个流程看下来,dev_queue_xmit从出口队列退出一帧后,hard_start_xmit再进行帧的传输,对比接收过程没有硬中断(个人理解,好像是这样)。只有当dev_queue_xmit退出一帧失败时,会设置软IRQ调度设备以准备下一次退帧操作。

net_tx_action函数:

net_rx_action函数是与NET_RX_SOFTIRQ软件中断相关联的处理函数,负责设备驱动程序延期至中断事件处理阶段之后才进行的那一部分的输入帧的处理。这样,驱动程序再中断环境下就只做必须立即处理的事(把数据拷贝到内存,然后产生一个软中断通知内核此事)。net_tx_action的工作方式类似,此函数可由raise_softirq_irqoff触发(第九章的内容,我好像没记录),来完成下面两个任务:

    当设备的传输功能通过netif_wake_queue(相当于netif_start_queue和_ _netif_schedule)开启时,此函数确保当所有条件都满足时,帧确实被传送出去了。

    当传输已经完成时,设备驱动通过dev_kfree_skb_irq通知相关的缓冲区可以释放了。

关于第二点注意,由于dev_kfree_skb_irq在中断环境下执行,所以需要尽快完成,因此它并不实际释放缓冲区,只是把要释放的缓冲区指针添加到softnet_data的completion_queue列表中,随后net_tx_action完成真正的释放。

为了释放缓冲区,net_tx_action需要访问completion_queue,由于net_tx_action运行在中断环境外,设备驱动可以随时添加新的数据,因此所以在访问CPU关联的softnet_data结构时,要先关闭中断。

    

看门狗定时器:

当某些条件满足时,设备驱动会关闭传输。当设备没有在合理时间重启时,会触发看门狗定时器,来重新配置设备。相关的net_device字段如下:

trans_start:设备驱动设置的时间戳,记录上次帧传输启动的时间。

watchdog_timer:这是流量控制启动的定时器。定时器到期时执行dev_watchdog

watchdog_timeo:要等待的时间。设备驱动初始化。为0时,watchdog_timer不会启动。

tx_timeout:设备驱动提供的函数。当内核到期时,内核处理函数会调用tx_timeout复位网络卡。

总结:

整个帧的传输流程:dev_queue_xmit从出口队列退出一帧后,hard_start_xmit再进行帧的传输,对比接收过程没有硬中断(个人理解,好像是这样)。只有当dev_queue_xmit退出一帧失败时,或者队列中还有数据没有传输等情况,就会设置软IRQ调度。之后会调用net_tx_action来处理帧传输。这里提到的队列是指内存中的队列,传输就是要把内存中出口队列的数据拷贝到设备自己的出口队列上。(仔细观察dev_queue_xmit的流程图)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值