网卡驱动把数据从设备拷贝到内存后会通过 netif_rx 把数据提交给上层,而上层通过 dev_queue_xmit 发送数
据,如果符合条件,调用驱动的 hard_start_xmit 把数据发送出去。本文
主要描述这两个流程的细节,其中流量控制,虚拟设备的绑定,网桥都在这一层有所涉及,但不予讨论,并且
基本不涉及 NAPI。
1,核心数据结构
struct softnet_data
{
int throttle;
int cng_level;
int avg_blog;
struct sk_buff_head input_pkt_queue;
struct list_head poll_list;
struct net_device *output_queue;
struct sk_buff *completion_queue;
struct net_device backlog_dev;
}
前三个用于流量控制,
input_pkt_queue,接受的 skb 队列
poll_list,如果设备上有要接收的数据,就把设备添加到 poll_list 中,并把 skb 挂在 input_pkt_queue 上,
由 net_rx_action 处理
output_queue,如果设备上有发送的数据,就把设备添加到这个队列
completion_queue,数据如果成功发送(通过中断指示),就把 skb 挂在这个队列上,net_tx_action 会释放掉
skb 占用的内存
backlog_dev,处理接收数据的时候,提供通用的设备轮询方法,backlog_process
每个 cpu 都有一个这样的数据结构
2,数据的接收
netdevice->state 中的_ _LINK_STATE_START flag 字段用于表示网卡是否开启,数据接收的时候也用于
检查这个字段。
netif_rx:
作用:把数据挂在 input_pkt_queue 上
如果 input_pkt_queue 上没有数据,就调用 netif_rx_schedule(&queue->backlog_dev),把这个 backlog 设备
添加到 poll_list 的链表中,对非 NAPI 驱动来说,这个设备就是链表上的唯一设备.如果队列上有数据就把数
据挂在 input_pkt_queue 上。等待软中断处理函数 net_rx_action 对队列上的数据进行处理。而对于 NAPI 驱
动,每个设备都有私有的 input_pkt_queue,驱动把 dev 添加到 poll_list 链表中(dev 是 realdev,而不是 back
log dev),把 skb 挂在私有队列上。
net_rx_action
作用:负责把 input_pkt_queue 上的数据提交给上层
The job of net_rx_action is pretty simple: to browse the poll_list list of devices that hav
e something in their ingress queue and invoke for each one the associated poll virtual function
对于非 NAPI 驱动来说,就是调用 backlog_dev 的 poll 方法 process_backlog。而对于 NAPI 驱动,则调用驱动
定义的 poll 方法。也就是说,process_backlog 处理共有的 input_pkt_queue,NAPI 驱动利用 poll 处理私有的
input_pkt_queue
2,数据的发送
__link_state_xoff 字段用于指示设备是否可以发送。netif_start_queue 和 netif_stop_queue 用于开启传输
和关闭传输。这是由驱动做的事。
与 netif_rx 对应的函数是 dev_queue_xmit,数据的发送有两个路径,1,将 skb 放到设备的发送队列,通过 qd
isc_run 发送,2,而没有流量控制的直接调用 hard_start_xmit 将数据发送出去(一些虚拟设备)。
qdisc_run,调用 qdisc_restart,经过一些复杂的队列控制,将数据通过 hard_start_xmit 发送出去。
net_tx_action,主要做两件事
1,轮询 output_queue 的 net 设备,调用 qdisc_run 将设备发送队列上的数据发送出去
2,释放 complete 队列上的 skb 内存
可见数据的接受有公共队列,而发送都是私有队列,当数据从上层传下来时就已经指定要通过哪个设备发送 s
kb->dev。
3,数据接受和发送是对称的两个过程,实质上就是对输入队列和输出队列的操作。
• poll_list is the list of devices that are polled because they have a non
empty receive queue. output_queue is the list of devices that have some
thing to transmit.
• Only open devices (ones with the _ _LINK_STATE_START flag set) can be
scheduled for reception. Only devices with transmission enabled (ones with
the _ _LINK_STATE_XOFF flag cleared) can be scheduled for transmission.
• When a device is scheduled for reception, its _ _LINK_STATE_RX_SCHED
flag is set. When a device is scheduled for transmission, its _ _LINK_ST
ATE_SCHED flag is set.
转自: http://blogold.chinaunix.net/u3/109935/showart_2197272.html
已经有了dev_queue_xmit函数,为什么还需要软中断来发送呢?
我们可以看到在dev_queue_xmit中将skb进行了一些处理(比如合并成一个包,计算校验和等)
处理完的skb是可以直接发送的了,这时dev_queue_xmit也会先将skb入队(skb一般都是在这个函数中入队的)
并且调用qdisc_run尝试发送,但是有可能发送失败,这时就将skb重新入队,调度软中断,并且自己直接返回。
软中断只是发送队列中的skb以及释放已经发送的skb,它无需再对skb进行线性化或者校验和处理
另外在队列被停止的情况下,dev_queue_xmit仍然可以把包加入队列,但是不能发送
这样在队列被唤醒的时候就需要通过软中断来发送停止期间积压的包
简而言之,dev_queue_xmit是对skb做些最后的处理并且第一次尝试发送,软中断是将前者发送失败或者没发完的包发送出去。
(其实发送软中断还有一个作用,就是释放已经发送的包,因为某些情况下发送是在硬件中断中完成的,
为了提高硬件中断处理效率,内核提供一种方式将释放skb放到软中断中进行,
这时只要调用dev_kfree_skb_irq,它将skb加入softnet_data的completion_queue中,然后开启发送软中断,
net_tx_action会在软中断中将completion_queue中的skb全部释放掉)
转自: http://blog.csdn.net/peimichael/archive/2009/10/19/4699609.aspx