首页 > 社交 > 科普中国

25

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

问1:在服务器上查看 /proc/softirqs,为什么 NET_RX 要比 NET_TX 大的多的多?gEf拜客生活常识网

传输完成最终会触发 NET_RX,而不是 NET_TX。所以自然你观测 /proc/softirqs 也就能看到 NET_RX 更多了。gEf拜客生活常识网

好,现在你已经对内核是怎么发送网络包的有一个全局上的把握了。不要得意,我们需要了解的细节才是更有价值的地方,让我们继续!!gEf拜客生活常识网

二、网卡启动准备

现在的服务器上的网卡一般都是支持多队列的。每一个队列上都是由一个 RingBuffer 表示的,开启了多队列以后的的网卡就会对应有多个 RingBuffer。gEf拜客生活常识网

gEf拜客生活常识网

网卡在启动时最重要的任务之一就是分配和初始化 RingBuffer,理解了 RingBuffer 将会非常有助于后面我们掌握发送。因为今天的主题是发送,所以就以传输队列为例,我们来看下网卡启动时分配 RingBuffer 的实际过程。gEf拜客生活常识网

在网卡启动的时候,会调用到 __igb_open 函数,RingBuffer 就是在这里分配的。gEf拜客生活常识网

//file: drivers/net/ethernet/intel/igb/igb_main.c  
static int __igb_open(struct net_device *netdev, bool resuming)  
{  
 struct igb_adapter *adapter = netdev_priv(netdev);  
  
 //分配传输描述符数组  
 err = igb_setup_all_tx_resources(adapter);  
  
 //分配接收描述符数组  
 err = igb_setup_all_rx_resources(adapter);  
  
 //开启全部队列  
 netif_tx_start_all_queues(netdev);  
}  

在上面 __igb_open 函数调用 igb_setup_all_tx_resources 分配所有的传输 RingBuffer, 调用 igb_setup_all_rx_resources 创建所有的接收 RingBuffer。gEf拜客生活常识网

//file: drivers/net/ethernet/intel/igb/igb_main.c  
static int igb_setup_all_tx_resources(struct igb_adapter *adapter)  
{  
 //有几个队列就构造几个 RingBuffer  
 for (i = 0; i < adapter->num_tx_queues; i++) {  
  igb_setup_tx_resources(adapter->tx_ring[i]);  
 }  
}  

真正的 RingBuffer 构造过程是在 igb_setup_tx_resources 中完成的。gEf拜客生活常识网

//file: drivers/net/ethernet/intel/igb/igb_main.c  
int igb_setup_tx_resources(struct igb_ring *tx_ring)  
{  
 //1.申请 igb_tx_buffer 数组内存  
 size = sizeof(struct igb_tx_buffer) * tx_ring->count;  
 tx_ring->tx_buffer_info = vzalloc(size);  
  
 //2.申请 e1000_adv_tx_desc DMA 数组内存  
 tx_ring->size = tx_ring->count * sizeof(union e1000_adv_tx_desc);  
 tx_ring->size = ALIGN(tx_ring->size, 4096);  
 tx_ring->desc = dma_alloc_coherent(dev, tx_ring->size,  
        &tx_ring->dma, GFP_KERNEL);  
  
 //3.初始化队列成员  
 tx_ring->next_to_use = 0;  
 tx_ring->next_to_clean = 0;  
}  

从上述源码可以看到,实际上一个 RingBuffer 的内部不仅仅是一个环形队列数组,而是有两个。gEf拜客生活常识网

1)igb_tx_buffer 数组:这个数组是内核使用的,通过 vzalloc 申请的。
2)e1000_adv_tx_desc 数组:这个数组是网卡硬件使用的,硬件是可以通过 DMA 直接访问这块内存,通过 dma_alloc_coherent 分配。gEf拜客生活常识网

这个时候它们之间还没有啥联系。将来在发送的时候,这两个环形数组中相同位置的指针将都将指向同一个 skb。这样,内核和硬件就能共同访问同样的数据了,内核往 skb 里写数据,网卡硬件负责发送。gEf拜客生活常识网

gEf拜客生活常识网

最后调用 netif_tx_start_all_queues 开启队列。另外,对于硬中断的处理函数 igb_msix_ring 其实也是在 __igb_open 中注册的。gEf拜客生活常识网

三、accept 创建新 socket

在发送数据之前,我们往往还需要一个已经建立好连接的 socket。gEf拜客生活常识网

我们就以开篇服务器缩微源代码中提到的 accept 为例,当 accept 之后,进程会创建一个新的 socket 出来,然后把它放到当前进程的打开文件列表中,专门用于和对应的客户端通信。gEf拜客生活常识网

假设服务器进程通过 accept 和客户端建立了两条连接,我们来简单看一下这两条连接和进程的关联关系。

相关阅读:

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