首页 > 社交 > 科普中国

25

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

在驱动函数里,将 skb 会挂到 RingBuffer上,驱动调用完毕后,数据包将真正从网卡发送出去。gEf拜客生活常识网

gEf拜客生活常识网

我们来看看实际的源码:gEf拜客生活常识网

//file: net/core/dev.c  
int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,  
   struct netdev_queue *txq)  
{  
 //获取设备的回调函数集合 ops  
 const struct net_device_ops *ops = dev->netdev_ops;  
  
 //获取设备支持的功能列表  
 features = netif_skb_features(skb);  
  
 //调用驱动的 ops 里面的发送回调函数 ndo_start_xmit 将数据包传给网卡设备  
 skb_len = skb->len;  
 rc = ops->ndo_start_xmit(skb, dev);  
}  

其中 ndo_start_xmit 是网卡驱动要实现的一个函数,是在 net_device_ops 中定义的。gEf拜客生活常识网

//file: include/linux/netdevice.h  
struct net_device_ops {  
 netdev_tx_t  (*ndo_start_xmit) (struct sk_buff *skb,  
         struct net_device *dev);  
  
}  

在 igb 网卡驱动源码中,我们找到了。gEf拜客生活常识网

//file: drivers/net/ethernet/intel/igb/igb_main.c  
static const struct net_device_ops igb_netdev_ops = {  
 .ndo_open  = igb_open,  
 .ndo_stop  = igb_close,  
 .ndo_start_xmit  = igb_xmit_frame,   
 ...  
};  

也就是说,对于网络设备层定义的 ndo_start_xmit, igb 的实现函数是 igb_xmit_frame。这个函数是在网卡驱动初始化的时候被赋值的。具体初始化过程参见《图解Linux网络包接收过程》一文中的 2.4 节,网卡驱动初始化。gEf拜客生活常识网

所以在上面网络设备层调用 ops->ndo_start_xmit 的时候,会实际上进入 igb_xmit_frame 这个函数中。我们进入这个函数来看看驱动程序是如何工作的。gEf拜客生活常识网

//file: drivers/net/ethernet/intel/igb/igb_main.c  
static netdev_tx_t igb_xmit_frame(struct sk_buff *skb,  
      struct net_device *netdev)  
{  
 ......  
 return igb_xmit_frame_ring(skb, igb_tx_queue_mapping(adapter, skb));  
}  
  
netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb,  
    struct igb_ring *tx_ring)  
{  
 //获取TX Queue 中下一个可用缓冲区信息  
 first = &tx_ring->tx_buffer_info[tx_ring->next_to_use];  
 first->skb = skb;  
 first->bytecount = skb->len;  
 first->gso_segs = 1;  
  
 //igb_tx_map 函数准备给设备发送的数据。  
 igb_tx_map(tx_ring, first, hdr_len);  
}  

在这里从网卡的发送队列的 RingBuffer 中取下来一个元素,并将 skb 挂到元素上。gEf拜客生活常识网

gEf拜客生活常识网

igb_tx_map 函数处理将 skb 数据映射到网卡可访问的内存 DMA 区域。gEf拜客生活常识网

//file: drivers/net/ethernet/intel/igb/igb_main.c  
static void igb_tx_map(struct igb_ring *tx_ring,  
      struct igb_tx_buffer *first,  
      const u8 hdr_len)  
{  
 //获取下一个可用描述符指针  
 tx_desc = IGB_TX_DESC(tx_ring, i);  
  
 //为 skb->data 构造内存映射,以允许设备通过 DMA 从 RAM 中读取数据  
 dma = dma_map_single(tx_ring->dev, skb->data, size, DMA_TO_DEVICE);  
  
 //遍历该数据包的所有分片,为 skb 的每个分片生成有效映射  
 for (frag = &skb_shinfo(skb)->frags[0];; frag++) {  
  
  tx_desc->read.buffer_addr = cpu_to_le64(dma);  
  tx_desc->read.cmd_type_len = ...;  
  tx_desc->read.olinfo_status = 0;  
 }  
  
 //设置最后一个descriptor  
 cmd_type |= size | IGB_TXD_DCMD;  
 tx_desc->read.cmd_type_len = cpu_to_le32(cmd_type);  
  
 /* Force memory writes to complete before letting h/w know there  
  * are new descriptors to fetch  
  */  
 wmb();  
}  

当所有需要的描述符都已建好,且 skb 的所有数据都映射到 DMA 地址后,驱动就会进入到它的最后一步,触发真实的发送。gEf拜客生活常识网

4.8 发送完成硬中断

当数据发送完成以后,其实工作并没有结束。因为内存还没有清理。当发送完成的时候,网卡设备会触发一个硬中断来释放内存。gEf拜客生活常识网

在《图解Linux网络包接收过程》 一文中的 3.1 和 3.2 小节,我们详细讲述过硬中断和软中断的处理过程。gEf拜客生活常识网

在发送完成硬中断里,会执行 RingBuffer 内存的清理工作,如图。gEf拜客生活常识网

相关阅读:

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