首页 > 社交 > 科普中国

25

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

在上述代码中,我们看到 while 循环不断地从队列中取出 skb 并进行发送。注意,这个时候其实都占用的是用户进程的系统态时间(sy)。只有当 quota 用尽或者其它进程需要 CPU 的时候才触发软中断进行发送。gEf拜客生活常识网

所以这就是为什么一般服务器上查看 /proc/softirqs,一般 NET_RX 都要比 NET_TX 大的多的第二个原因。对于读来说,都是要经过 NET_RX 软中断,而对于发送来说,只有系统态配额用尽才让软中断上。gEf拜客生活常识网

我们来把精力在放到 qdisc_restart 上,继续看发送过程。gEf拜客生活常识网

static inline int qdisc_restart(struct Qdisc *q)  
{  
 //从 qdisc 中取出要发送的 skb  
 skb = dequeue_skb(q);  
 ...  
  
 return sch_direct_xmit(skb, q, dev, txq, root_lock);  
}  

qdisc_restart 从队列中取出一个 skb,并调用 sch_direct_xmit 继续发送。gEf拜客生活常识网

//file: net/sched/sch_generic.c  
int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,  
   struct net_device *dev, struct netdev_queue *txq,  
   spinlock_t *root_lock)  
{  
 //调用驱动程序来发送数据  
 ret = dev_hard_start_xmit(skb, dev, txq);  
}  

4.6 软中断调度

在 4.5 咱们看到了如果系统态 CPU 发送网络包不够用的时候,会调用 __netif_schedule 触发一个软中断。该函数会进入到 __netif_reschedule,由它来实际发出 NET_TX_SOFTIRQ 类型软中断。gEf拜客生活常识网

软中断是由内核线程来运行的,该线程会进入到 net_tx_action 函数,在该函数中能获取到发送队列,并也最终调用到驱动程序里的入口函数 dev_hard_start_xmit。gEf拜客生活常识网

gEf拜客生活常识网

//file: net/core/dev.c  
static inline void __netif_reschedule(struct Qdisc *q)  
{  
 sd = &__get_cpu_var(softnet_data);  
 q->next_sched = NULL;  
 *sd->output_queue_tailp = q;  
 sd->output_queue_tailp = &q->next_sched;  
  
 ......  
 raise_softirq_irqoff(NET_TX_SOFTIRQ);  
}  

在该函数里在软中断能访问到的 softnet_data 里设置了要发送的数据队列,添加到了 output_queue 里了。紧接着触发了 NET_TX_SOFTIRQ 类型的软中断。(T 代表 transmit 传输)gEf拜客生活常识网

软中断的入口代码我这里也不详细扒了,感兴趣的同学《图解Linux网络包接收过程》参考一文中的 3.2 小节 - ksoftirqd内核线程处理软中断。gEf拜客生活常识网

我们直接从 NET_TX_SOFTIRQ softirq 注册的回调函数 net_tx_action讲起。用户态进程触发完软中断之后,会有一个软中断内核线程会执行到 net_tx_action。gEf拜客生活常识网

牢记,这以后发送数据消耗的 CPU 就都显示在 si 这里了,不会消耗用户进程的系统时间了gEf拜客生活常识网

//file: net/core/dev.c  
static void net_tx_action(struct softirq_action *h)  
{  
 //通过 softnet_data 获取发送队列  
 struct softnet_data *sd = &__get_cpu_var(softnet_data);  
  
 // 如果 output queue 上有 qdisc  
 if (sd->output_queue) {  
  
  // 将 head 指向第一个 qdisc  
  head = sd->output_queue;  
  
  //遍历 qdsics 列表  
  while (head) {  
   struct Qdisc *q = head;  
   head = head->next_sched;  
  
   //发送数据  
   qdisc_run(q);  
  }  
 }  
}  

软中断这里会获取 softnet_data。前面我们看到进程内核态在调用 __netif_reschedule 的时候把发送队列写到 softnet_data 的 output_queue 里了。软中断循环遍历 sd->output_queue 发送数据帧。gEf拜客生活常识网

来看 qdisc_run,它和进程用户态一样,也会调用到 __qdisc_run。gEf拜客生活常识网

//file: include/net/pkt_sched.h  
static inline void qdisc_run(struct Qdisc *q)  
{  
 if (qdisc_run_begin(q))  
  __qdisc_run(q);  
}  

然后一样就是进入 qdisc_restart => sch_direct_xmit,直到驱动程序函数 dev_hard_start_xmit。gEf拜客生活常识网

4.7 igb 网卡驱动发送

我们前面看到,无论是对于用户进程的内核态,还是对于软中断上下文,都会调用到网络设备子系统中的 dev_hard_start_xmit 函数。在这个函数中,会调用到驱动里的发送函数 igb_xmit_frame。

相关阅读:

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