一文讲解Linux 内核网络协议栈-数据从接收到ip层

本文深入讲解Linux内核如何处理网络数据,从IO端口访问到中断响应,再到net_interrupt和net_rx函数。重点讨论了netif_rx函数、中断处理、NAPI优化以及数据包如何从输入队列到IP层的处理,包括packet_type结构体和ip_rcv函数的作用。
摘要由CSDN通过智能技术生成

【推荐阅读】

一文了解Linux上TCP的几个内核参数调优

一文剖析Linux内核中内存管理

分析linux启动内核源码

此处主要讲的是从数据来到,中断到最终数据包被处理的过程。

0:首先来介绍一下IO端口访问问题,内核提供了这样一组函数处理: // kernel/io.c中

>: inb( )、inw( )、inl( )函数

分别从I/O端口读取1、2或4个连续字节。

后缀“b”、“w”、“l”分别代表一个字节(8位)、一个字(16位)以及一个长整型(32 位)。

>: inb_p( )、inw_p( )、inl_p( )

分别从I/O端口读取1、2或4个连续字节,然后执行一条 “空指令” 使CPU暂停。 p 可以理解成pause

>: outb( )、outw( )、outl( )

分别向一个I/O端口写入1、2或4个连续字节。

>: outb_p( )、outw_p( )、outl_p( )

分别向一个I/O端口写入1、2或4个连续字节,然后执行一条“空指令”指令使CPU暂停。

>: insb( )、insw( )、insl( )

分别从I/O端口读入以1、2或4个字节为一组的连续字节序列。字节序列的长度由该函数的参数给出。

>: outsb( )、outsw( )、outsl( )

分别向I/O端口写入以1、2或4个字节为一组的连续字节序列。

1:当一个中断来到,首先响应 net_interrupt 函数

/*
 * The typical workload of the driver:
 * Handle the network interface interrupts.
 */
static irqreturn_t net_interrupt(int irq, void *dev_id)  // 注意参数是:中断号和设备id
 {
         struct net_device *dev = dev_id;
         struct net_local *np;
         int ioaddr, status;
         int handled = 0;
 
         ioaddr = dev->base_addr;       // 设备的IO地址 
 
         np = netdev_priv(dev);         // 得到dev私有数据
         status = inw(ioaddr + 0);      // 从端口读两个字节
 
         if (status == 0)
                 goto out;
         handled = 1;
 
         if (status & RX_INTR) {
                 /* Got a packet(s). */
                 net_rx(dev);          // 使用这个函数net_rx来获取一个数据包 -----> receive
         }                             // 这个函数下面会说到
 #if TX_RING
         if (status & TX_INTR) {       // 发送数据
                 /* Transmit complete. */
                 net_tx(dev);             // 发送数据使用net_tx ------> transmit
                 np->stats.tx_packets++;  // 计数 
                 netif_wake_queue(dev);   // 处理结束,唤醒下一个队列中等待者                                                        }
#endif 
         if (status & COUNTERS_INTR) 
         {                                /* Increment the appropriate 'localstats' field. */ 
              np->stats.tx_window_errors++; 
         } 
out: 
         return IRQ_RETVAL(handled); // 返回中断 
}

2:下面 需要看一下接收数据包函数net_rx

/* We have a good packet(s), get it/them out of the buffers. */
static void
net_rx(struct net_device *dev)        // 所谓接收数据包,其实就是构造skb数据结构 ^_^
{
         struct net_local *lp = netdev_priv(dev);
         int ioaddr = dev->base_addr;
         int boguscount = 10;
 
         do { // 下面是循环接收数据么
                 int status = inw(ioaddr);     // 获取状态
                 int pkt_len = inw(ioaddr);    // 获取包大小
 
                 if (pkt_len == 0)               /* 全部接收 */
                         break;                  /* 可以结束 */
 
                 if (status & 0x40) {    /* There was an error. */
                         lp->stats.rx_errors++;
                         if (status & 0x20) lp->stats.rx_frame_errors++;
                         if (status & 0x10) lp->stats.rx_over_errors++;
                         if (status & 0x08) lp->stats.rx_crc_errors++;
                         if (status & 0x04) lp->stats.rx_fifo_errors++;
                 } else {
                         /* Malloc up new buffer. */
                         struct sk_buff *skb;
 
                         lp->stats.rx_bytes+=pkt_len;     // 接收的字节数+pkt_len
 
                         skb = dev_alloc_skb(pkt_len);    // 需要接收多少bytes就分配多少空间给sk_buff
                         if (skb == NULL) {               // 需要丢包
                                 printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
                                            dev->name);
                                 lp->stats.rx_dropped++;  // 丢包数++
                                 break;
                         }
                         skb->dev = dev;                  // 现在开始构建skb包
 
                         /* 'skb->data' points to the start of sk_buff data area. */
                         memcpy(skb_put(skb,pkt_len), (void*)dev->rmem_start,    // 注意开始从dev向skb中放入数据,大小pkt_len
                                    pkt_len);
                         /* or */
                         insw(ioaddr, skb->data, (pkt_len + 1) >> 1);
 
                         netif_rx(skb);             
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux网络协议内核分析中,主要涉及到Transmission Control Protocol (TCP)和Linux Socket编程接口。TCP是一种传输协议,用于在网络中可靠地传输数据Linux Socket是从BSD Socket发展而来的接口,它提供给应用程序员与内核空间的网络协议栈进行通信的方法。通过Linux Socket,应用程序可以访问传输协议,并屏蔽了不同网络协议之间的差异。它位于应用,并提供了大量的系统调用,构成了网络程序的主体。在Linux系统中,Socket是文件系统的一部分,使得对网络的控制和对文件的控制一样方便。此外,INET Socket是调用IP协议的统一接口,与sock结构体关系紧密。通过对Linux网络协议内核的分析,我们可以深入了解TCPLinux Socket的运作机制以及网络应用程序的基础。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [理解 Linux 网络栈(1):Linux 网络协议栈简单总结](https://blog.csdn.net/weixin_33724659/article/details/85808277)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [一文了解Linux 内核网络协议栈](https://blog.csdn.net/m0_74282605/article/details/128483211)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值