首页 > 社交 > 科普中国

25

常驻编辑 科普中国 2022-06-02 子系统   网络   队列   内核   网卡   函数   源码   内存   过程   数据   用户

如果查找不到,则调用 __neigh_create 创建一个邻居。gEf拜客生活常识网

//file: net/core/neighbour.c  
struct neighbour *__neigh_create(......)  
{  
 //申请邻居表项  
 struct neighbour *n1, *rc, *n = neigh_alloc(tbl, dev);  
  
 //构造赋值  
 memcpy(n->primary_key, pkey, key_len);  
 n->dev = dev;  
 n->parms->neigh_setup(n);  
  
 //最后添加到邻居 hashtable 中  
 rcu_assign_pointer(nht->hash_buckets[hash_val], n);  
 ......  

有了邻居项以后,此时仍然还不具备发送 IP 报文的能力,因为目的 MAC 地址还未获取。调用 dst_neigh_output 继续传递 skb。gEf拜客生活常识网

//file: include/net/dst.h  
static inline int dst_neigh_output(struct dst_entry *dst,   
     struct neighbour *n, struct sk_buff *skb)  
{  
 ......  
 return n->output(n, skb);  
}  

调用 output,实际指向的是 neigh_resolve_output。在这个函数内部有可能会发出 arp 网络请求。gEf拜客生活常识网

//file: net/core/neighbour.c  
int neigh_resolve_output(){  
  
 //注意:这里可能会触发 arp 请求  
 if (!neigh_event_send(neigh, skb)) {  
  
  //neigh->ha 是 MAC 地址  
  dev_hard_header(skb, dev, ntohs(skb->protocol),  
           neigh->ha, NULL, skb->len);  
  //发送  
  dev_queue_xmit(skb);  
 }  
}  

当获取到硬件 MAC 地址以后,就可以封装 skb 的 MAC 头了。最后调用 dev_queue_xmit 将 skb 传递给 Linux 网络设备子系统。gEf拜客生活常识网

4.5 网络设备子系统

gEf拜客生活常识网

邻居子系统通过 dev_queue_xmit 进入到网络设备子系统中来。gEf拜客生活常识网

//file: net/core/dev.c   
int dev_queue_xmit(struct sk_buff *skb)  
{  
 //选择发送队列  
 txq = netdev_pick_tx(dev, skb);  
  
 //获取与此队列关联的排队规则  
 q = rcu_dereference_bh(txq->qdisc);  
  
 //如果有队列,则调用__dev_xmit_skb 继续处理数据  
 if (q->enqueue) {  
  rc = __dev_xmit_skb(skb, q, dev, txq);  
  goto out;  
 }  
  
 //没有队列的是回环设备和隧道设备  
 ......  
}  

开篇第二节网卡启动准备里我们说过,网卡是有多个发送队列的(尤其是现在的网卡)。上面对 netdev_pick_tx 函数的调用就是选择一个队列进行发送。gEf拜客生活常识网

netdev_pick_tx 发送队列的选择受 XPS 等配置的影响,而且还有缓存,也是一套小复杂的逻辑。这里我们只关注两个逻辑,首先会获取用户的 XPS 配置,否则就自动计算了。代码见 netdev_pick_tx => __netdev_pick_tx。gEf拜客生活常识网

//file: net/core/flow_dissector.c  
u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb)  
{  
 //获取 XPS 配置  
 int new_index = get_xps_queue(dev, skb);  
  
 //自动计算队列  
 if (new_index < 0)  
  new_index = skb_tx_hash(dev, skb);}  

然后获取与此队列关联的 qdisc。在 linux 上通过 tc 命令可以看到 qdisc 类型,例如对于我的某台多队列网卡机器上是 mq disc。gEf拜客生活常识网

#tc qdisc  
qdisc mq 0: dev eth0 root  

大部分的设备都有队列(回环设备和隧道设备除外),所以现在我们进入到 __dev_xmit_skb。gEf拜客生活常识网

//file: net/core/dev.c  
static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,  
     struct net_device *dev,  
     struct netdev_queue *txq)  
{  
 //1.如果可以绕开排队系统  
 if ((q->flags & TCQ_F_CAN_BYPASS) && !qdisc_qlen(q) &&  
     qdisc_run_begin(q)) {  
  ......  
 }  
  
 //2.正常排队  
 else {  
  
  //入队  
  q->enqueue(skb, q)  
  
  //开始发送  
  __qdisc_run(q);  
 }  
}  

上述代码中分两种情况,1 是可以 bypass(绕过)排队系统的,另外一种是正常排队。我们只看第二种情况。gEf拜客生活常识网

先调用 q->enqueue 把 skb 添加到队列里。然后调用 __qdisc_run 开始发送。gEf拜客生活常识网

//file: net/sched/sch_generic.c  
void __qdisc_run(struct Qdisc *q)  
{  
 int quota = weight_p;  
  
 //循环从队列取出一个 skb 并发送  
 while (qdisc_restart(q)) {  
    
  // 如果发生下面情况之一,则延后处理:  
  // 1. quota 用尽  
  // 2. 其他进程需要 CPU  
  if (--quota <= 0 || need_resched()) {  
   //将触发一次 NET_TX_SOFTIRQ 类型 softirq  
   __netif_schedule(q);  
   break;  
  }  
 }  
}  
    

相关阅读:

  • 量子子系统的新理论
  • 无法检验的科学是科学吗?
  • 伊朗成功发射太空拖船“萨曼轨道传输器”
  • HUAWEI
  • 纳莱迪人儿童遗骸发现谜团重重
  • 并不是所有的物联网平台都适合系统集成
  • 网络销售怎么去聊客户(网络销售出单难吗)
  • 多益网络怎么样(广州多益网络工作感受)
  • 网络短视频内容审核标准细则发布:短视频节目不得未经
  • 移动网络怎么样(移动300兆相当于电信多少兆)
    • 网站地图 |
    • 声明:登载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。文章内容仅供参考,不做权威认证,如若验证其真实性,请咨询相关权威专业人士。