Linux网络协议栈(二)--数据链路层数据的接收与发送

 

2.1 数据的接收

数据的接收是通过net_rx_action进行的。

这里简单描述一下使用NAPI方式的驱动程序的接收方式,没有使用NAPI的驱动,实际上系统里面也为其模拟了一个NAPI实体,所以其实是一样的。

驱动程序收到数据包之后会调用,netif_rx_schedule,它的目的就是使软中断net_rx_action能够被执行。

net_rx_action

主要工作就是调用n->poll(n, weight);(这个poll就是驱动程序里面调用netif_napi_add注册的poll,老一点的kernel中驱动程序是注册dev->poll函数,net_rx_action也是调用dev->poll函数)

poll函数会从网卡取得数据,并调用netif_receive_skb传送数据。

netif_receive_skb

把数据传送给所有注册到ptype_all的第三层协议:

       list_for_each_entry_rcu(ptype, &ptype_all, list) {

              if (!ptype->dev || ptype->dev == skb->dev) {

                     if (pt_prev)

                            ret = deliver_skb(skb, pt_prev, orig_dev);

                     pt_prev = ptype;

              }

       }

deliver_skb

static inline int deliver_skb(struct sk_buff *skb,

                           struct packet_type *pt_prev,

                           struct net_device *orig_dev)

{

       atomic_inc(&skb->users);

       return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);

}

就是调用第三层协议注册的func函数,这个函数是数据链路层到第三层的唯一接口。

把数据传送给:

       type = skb->protocol;

       list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type)&15], list) {

              if (ptype->type == type &&

                  (!ptype->dev || ptype->dev == skb->dev)) {

                     if (pt_prev)

                            ret = deliver_skb(skb, pt_prev, orig_dev);

                     pt_prev = ptype;

              }

       }

还有传送给网桥和VLAN

       skb = handle_bridge(skb, &pt_prev, &ret, orig_dev);

       skb = handle_macvlan(skb, &pt_prev, &ret, orig_dev);

 

 

 

2.2 数据的发送

device_queue_xmit

第三层协议要传送数据的时候,就调用这个函数。这是唯一的3层到2层发送数据的入口函数!它的具体流程:

       struct Qdisc *q;

       rc = q->enqueue(skb, q);

       qdisc_run(dev);

              qdisc_restart(dev)

有一种特殊情况:如果dev->enqueue == NULL,直接调用dev->hard_start_xmit()loopback之类的本地接口就是这样做的。

这里的Qdisc,是在网口UP的时候,dev_activate所启动的。

dev_activate

qdisc = qdisc_create_dflt(dev, &pfifo_fast_ops,TC_H_ROOT);

static struct Qdisc_ops pfifo_fast_ops = {

       .id           =     "pfifo_fast",

       .priv_size =     PFIFO_FAST_BANDS * sizeof(struct sk_buff_head),

       .enqueue  =     pfifo_fast_enqueue,

       .dequeue  =     pfifo_fast_dequeue,

       .requeue  =     pfifo_fast_requeue,

       .init         =     pfifo_fast_init,

       .reset             =     pfifo_fast_reset,

       .dump            =     pfifo_fast_dump,

       .owner           =     THIS_MODULE,

};

所以q->enqueue(skb, q)就是调用pfifo_fast_enqueue函数很简单如果queue还没有满则把skb放入queue的末尾

系统中有其他的qdisc_ops,比如优先级调度之类的,以后章节再详述,这里我们只说了缺省的FIFO方式。

qdisc_run

如果queue的状态不是__LINK_STATE_XOFF(在驱动程序里面调用的netif_stop_queue(dev)netif_start_queue(dev);就是用来控制这个符号的),并且当前queue没有被处理(__LINK_STATE_QDISC_RUNNING)就可以对queue进行处理:处理的方式是

void __qdisc_run(struct net_device *dev)

{

       do {

              if (!qdisc_restart(dev))

                     break;

       } while (!netif_queue_stopped(dev));

 

       clear_bit(__LINK_STATE_QDISC_RUNNING, &dev->state);

}

qdisc_restart

主要就是调用驱动程序提供的hard_start_xmit对数据进行传输。

net_tx_action

从上面的流程可以看到,包传输的整个过程是一气呵成的,根本没有调用到系统里面设置的软中断函数net_tx_action

这是因为我只描述了正常的流程,如果在过程中有些意外发生,比如queue目前状态是__LINK_STATE_XOFF或者__LINK_STATE_QDISC_RUNNING或者hard_start_xmit返回error了,那包实际上就没有被传送,而是继续放在queue里面的。系统就会在合适的时候(比如pfifo_fast_requeuenetif_wake_queueqdisc_watchdog)调用netif_schedule或者__netif_schedule来唤起软中断net_tx_action的执行,net_tx_action主要任务也就是调用qdisc_run传送数据包

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值