精华内容
下载资源
问答
  • 一般来说,网卡主要有两个重要的功能:接收数据 和 发送数据。 所以,当网卡接收到数据包后,要通知 Linux 内核有数据需要处理。另外,网卡驱动应该提供让 Linux 内核把数据把发送出去的接口。 net_device 结构是 ...

    一般来说,网卡主要有两个重要的功能:接收数据发送数据

    所以,当网卡接收到数据包后,要通知 Linux 内核有数据需要处理。另外,网卡驱动应该提供让 Linux 内核把数据把发送出去的接口。

    net_device 结构是 Linux 为了适配不同类型的网卡设备而抽象出来的对象,不同的网卡驱动只需要按 Linux 的规范来填充 net_device 结构的各个成员变量,Linux 内核就能够识别出网卡,并工作起来。

    下面我们将分析网卡设备接收和发送数据包的实现原理。

    net_device 结构

    net_device 结构是 Linux 内核对网卡设备的抽象,但由于历史原因,net_device 结构的定义十分复杂。

    不过本文主要分析网卡设备收发数据的实现,所以不会分析 net_device 结构的所有成员。下面主要列出收发数据相关的成员,如下:

    
    struct net_device
    {
        char                name[IFNAMSIZ];  // 设备名字
        ...
        unsigned int        irq;             // 中断号
        ...
        int (*init)(struct net_device *dev); // 设备初始化设备的接口
        ...
        int (*open)(struct net_device *dev); // 打开设备时调用的接口
        int (*stop)(struct net_device *dev); // 关闭设备时调用的接口
    
        // 发送数据接口
        int (*hard_start_xmit)(struct sk_buff *skb,struct net_device *dev);
        ...
    };

    下面介绍一下各个成员的作用:

    • name:设备的名字。用于在终端显示设备的名字或者通过设备名字来搜索设备。
    • irq:中断号。当网卡从网络接收到数据包后,需要产生一个中断来通知 Linux 内核有数据包需要处理,而 irq 就是网卡驱动注册到内核中断服务的中断号。
    • init、open、stop:分别为设备的初始化接口,打开接口和关闭接口。
    • hard_start_xmit:当需要通过网卡设备发送数据时,可以调用这个接口来发送数据。

    所以,一个网卡驱动必须完成以下两个工作:

    • 通过实现 net_device 结构的 hard_start_xmit 方法来提供发送数据的功能。
    • 通过向内核注册硬件中断服务,来通知内核处理网卡设备接收到的数据包。

    也就是说,发送数据的功能是由 net_device 结构的 hard_start_xmit 方法提供,而通知内核处理接收到的数据包的功能是由网卡的硬件中断提供的。

    图1 展示了网卡接收和发送数据的过程:

     

     

    1 网卡接收和发送数据过程

    上图展示的是 NS8390网卡 接收和发送数据的过程(红色括号为接收过程,蓝色括号为发送过程),从上图可以发现,NS8390网卡驱动 完成了两件事情:

    • 将 net_device 结构的 hard_start_xmit 方法设置为 ei_start_xmit。
    • 向 Linux 内核注册了 ei_interrupt 硬件中断服务。

    所以,当网卡接收到数据包时,会触发 ei_interrupt 中断服务来通知内核有数据包需要处理。而当需要通过网卡发送数据时,将会调用 ei_start_xmit 方法把数据发送出去。

    【文章福利】:小编整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!正在跳转(需要自取)

     

    这里推荐大家可以看看Linux C/C++ 高级开发架构的【免费】课程:

    https://ke.qq.com/course/417774?flowToken=1017067

    课程内容的话和腾讯C++后台开发T8职级技术栈对标,围绕数据结构与算法、数据库、网络、操作系统、网络编程、分布式架构等方面全面提升,值得学习一波~

    接收数据过程

    当网卡从网络中接收到数据包后,会触发 ei_interrupt 中断服务,我们来看看 ei_interrupt 中断服务的实现:

    
    void ei_interrupt(int irq, void *dev_id, struct pt_regs *regs)
    {
        struct net_device *dev = dev_id;
        long e8390_base;
        int interrupts, nr_serviced = 0;
        struct ei_device *ei_local;
    
        e8390_base = dev->base_addr;
        ei_local = (struct ei_device *)dev->priv;
    
        spin_lock(&ei_local->page_lock);
        ...
        // (1) 通过读取网卡的中断类型来进行相应的操作
        while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0 
                && ++nr_serviced < MAX_SERVICE)
        {
            ...
            // (2) 如果中断类型为接收到数据包
            if (interrupts & (ENISR_RX + ENISR_RX_ERR)) {
                ei_receive(dev); // (3) 则从网卡读取数据
            }
            ...
        }
        ...
        spin_unlock(&ei_local->page_lock);
        return;
    }

    上面的代码删除了很多硬件相关的操作,因为本文并不是分析网卡驱动的实现。

    ei_interrupt 中断服务首先读取中断的类型,保存到 interrupts 变量中。然后判断中断类型是否为接收到数据包,如果是就调用 ei_receive 函数从网卡处读取数据。

    我们继续分析 ei_receive 函数的实现:

    static void ei_receive(struct net_device *dev)
    {
        ...
        while (++rx_pkt_count < 10) 
        {
            int pkt_len;  // 数据包的长度
            int pkt_stat; // 数据包的状态
            ...
            if ((pkt_stat & 0x0F) == ENRSR_RXOK) { // 如果数据包状态是合法的
                struct sk_buff *skb;
    
                skb = dev_alloc_skb(pkt_len + 2); // 申请一个数据包对象
                if (skb) {
                    skb_reserve(skb, 2);
                    skb->dev = dev;         // 设置接收数据包的设备
                    skb_put(skb, pkt_len);  // 增加数据的长度
    
                    // 从网卡中读取数据(由网卡驱动实现), 并将数据保存到skb中
                    ei_block_input(dev, pkt_len, skb, current_offset+sizeof(rx_frame));
    
                    skb->protocol = eth_type_trans(skb, dev); // 从以太网头部中获取网络层协议类型
    
                    netif_rx(skb); // 将数据包上送给内核网络协议栈
                    ...
                }
            }
            ...
        }
        ...
        return;
    }

    ei_receive 函数主要完成以下几个工作:

    • 申请一个 sk_buff 数据包对象,并且设置其 dev 字段为接收数据包的设备。
    • 通过调用 ei_block_input 函数从网卡中读取接收到的数据,并保存到刚申请的 sk_buff 数据包对象中。ei_block_input 函数是由网卡驱动实现的,所以这里不作详细分析。
    • 通过调用 eth_type_trans 函数从数据包的以太网头部中获取网络层协议类型。
    • 调用 netif_rx 函数将数据包上送给内核网络协议栈。

    当把数据包上送给内核网络协议栈后,数据包的处理就由内核接管。一般来说,内核网络协议栈会通过网络层的 IP协议 和传输层的 TCP协议 或者 UDP协议 来对数据包进行处理,处理完后就会把数据提交给应用层的进程进行处理。

    发送数据过程

    当网络协议栈需要通过网卡设备发送数据时,会调用 net_device 结构的 hard_start_xmit 方法,而对于 NS8390网卡 来说,hard_start_xmit 方法会被设置为 ei_start_xmit 函数。

    也就是说,使用 NS8390网卡 发送数据时,最终会调用 ei_start_xmit 函数将数据发送出去。我们来看看 ei_start_xmit 函数的实现:

    static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
    {
        ...
        length = skb->len; // 数据包的长度
        ...
        disable_irq_nosync(dev->irq);      // 关闭硬件中断
        spin_lock(&ei_local->page_lock);   // 对设备进行上锁, 避免多核CPU对设备的使用
        ...
        // 使用网卡驱动的发送接口把数据发送出去,skb->data 为数据包的数据部分
        ei_block_output(dev, length, skb->data, ei_local->tx_start_page);
        ...
        spin_unlock(&ei_local->page_lock); // 对设备进行解锁
        enable_irq(dev->irq);              // 打开硬件中断
        ...
        return 0;
    }

    删减了硬件相关的操作后,ei_start_xmit 函数的实现就非常简单:

    • 首先关闭网卡的硬件中断,防止发送过程中受到硬件中断的干扰。
    • 调用 ei_block_output 函数把数据包的数据发送出去,此函数由网卡驱动实现,这里不作详细分析。
    • 打开网卡的硬件中断,让网卡能够继续通知内核。

    总结

    本文主要简单的介绍了网卡设备接收和发送数据包的过程,而网卡设备的初始化过程并没有涉及。当然网卡设备的初始化过程也非常重要,可能会在后面的文章继续分析。


    作者:JaydenLie
    链接:https://juejin.cn/post/6970632333397327909
    来源:掘金
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

     

    展开全文
  • 下图简单描述了网卡驱动与Linux内核之间的联系:   关于上图的一些说明:  系统初始化: 1. 协议模块调用 dev_add_pack() 来注册协议处理函数到链表 &ptype_base; 2. __init br_init() 用于初始...

    下图简单描述了网卡驱动与Linux内核之间的联系:

     

    关于上图的一些说明: 

    系统初始化:

    1. 协议模块调用 dev_add_pack() 来注册协议处理函数到链表 &ptype_base;

    2. __init br_init() 用于初始化桥接相关的操作;

    3. __init net_dev_init() 初始化了两个软中断;

     

    网卡驱动初始化:

    1. 网卡驱动在其 probe() 函数里面初始化 net_device 结构体,用来描述网卡,以及提供操作网卡的接口;

     

    配置网卡:

    1. 当我们通过ifconfig来配置网卡时,会调用到net_device->open(),该函数最主要的是注册了一个中断(当网卡接收到数据或数据发送完成会触发中断);

     

    数据的发送与接收:

    1. 当我们需要发送数据时,最终调用的是网卡驱动提供的函数:net_device->hard_start_xmit();

    2. 当我们接收到数据时,会触发中断,中断处理函数调用会调用内核函数来接收数据,最终由驱动程序调用内核函数netif_receive_skb(),把报文送入协议栈(接下来的代码硬件无关,与具体报文处理协议相关,比如:ARP协议,IPv4协议,IPv6协议等)。

    3. 网卡的中断处理函数在调用内核函数接收数据时又分为非NAPI/NAPI两种方式;

    4. NAPI方式涉及到中断的下半部处理的概念以及软中断。

    5. 报文通过netif_receive_skb()送入协议栈之后,首先判断需不需要进行桥接处理;

    6. 如果报文没有被桥接代码处理,再调用协议处理函数来处理;

     

    展开全文
  • 网卡接收数据流程

    千次阅读 2017-06-23 13:49:19
    1.网络模型因特网的五层协议栈: 应用层 运输层 网络层 链路层 物理层七层ISO模型 应用层 表示层 会话层 运输层 网络层 链路层 物理层2.... 物理层:主要负责帧数据在节点间的... 网络层:典型为IP协议,数据称做

    1.网络模型

    因特网的五层协议栈:

        应用层
        运输层
        网络层
        链路层
        物理层

    七层ISO模型

        应用层
        表示层
        会话层
        运输层
        网络层
        链路层
        物理层

    2.网络模型各层意义

    物理层:主要负责帧数据在节点间的移动
    链路层:典型为以太网和PPP协议,数据称做帧(frame)
    网络层:典型为IP协议,数据称做数据报(datagram)
    网络层:典型为TCP/UDP协议,数据称做报文段(segment),主要负责应用程序端点间的传送
    会话层:主要是数据的定界和同步
    表示层:主要是解释数据的含义,包括数据压缩,加密等
    应用层:典型为HTTP,SMTP等协议,数据称做报文(message)

    3.网络模型各层头部信息

    以太网帧头部包含:目的MAC,源MAC和网络层协议类型,(组播的目的MAC:MAC地址只要第48bit是1就表示组播地址,一般组播地址的第1字节是0x01;组播的源MAC:它唯一标识这个设备,所以只能是单播地址)
    IP数据报头部包含:运输层的协议,源和目的IP地址

    4.网卡接收数据流程

    • (物理层)网卡接收来自其他节点的数据帧
    • (链路层)根据帧头信息获取数据报
    • (网络层)路由过滤,判断是否应该丢弃该数据报,并根据数据报头信息获取TCP/UDP数据段
    • (运输层)根据数据段头信息获取实际的用户数据
    • (应用层)根据应用层协议获取最终的有效信息
    展开全文
  • 网卡驱动收发过程

    2018-09-12 15:48:02
    网卡工作在物理层和数据链路层,主要由PHY/MAC芯片、Tx/Rx FIFO、DMA等组成,其中网线通过变压器接PHY芯片、PHY芯片通过MII接MAC芯片、MAC芯片接PCI总线 PHY芯片主要负责:CSMA/CD、模数转换、编解码、串并转换 ...

    网卡

    网卡工作在物理层和数据链路层,主要由PHY/MAC芯片、Tx/Rx FIFO、DMA等组成,其中网线通过变压器接PHY芯片、PHY芯片通过MII接MAC芯片、MAC芯片接PCI总线

    PHY芯片主要负责:CSMA/CD、模数转换、编解码、串并转换

    MAC芯片主要负责:

    比特流和帧的转换:7字节的前导码Preamble和1字节的帧首定界符SFD CRC校验
    Packet Filtering:L2 Filtering、VLAN Filtering、Manageability / Host Filtering

    网络驱动收包大致有3种情况:

    no NAPI:mac每收到一个以太网包,都会产生一个接收中断给cpu,即完全靠中断方式来收包缺点是当网络流量很大时,cpu大部分时间都耗在了处理mac的中断。

    netpoll:在网络和I/O子系统尚不能完整可用时,模拟了来自指定设备的中断,即轮询收包。缺点是实时性差。

    NAPI: 采用 中断 + 轮询 的方式:mac收到一个包来后会产生接收中断,但是马上关闭。直到收够了netdev_max_backlog个包(默认300),或者收完mac上所有包后,才再打开接收中断

    收发包过程图

    网卡收发包

    硬中断函数把napi_struct加入CPU的poll_list,软中断函数net_rx_action()遍历poll_list,执行poll函数。

    每个网络设备(MAC层)都有自己的net_device数据结构,这个结构上有napi_struct。
    每当收到数据包时,网络设备驱动会把自己的napi_struct挂到CPU私有变量上。
    这样在软中断时,net_rx_action会遍历cpu私有变量的poll_list,
    执行上面所挂的napi_struct结构的poll钩子函数,将数据包从驱动传到网络协议栈。

    中断上下部

    中断上下部

    展开全文
  • 数据网卡到应用的过程 数据网卡到应用的过程1、过程概述2、网卡3、网卡驱动 数据网卡到应用的过程 1、过程概述 假设一个HTTP请求的数据到达网卡,那数据是如何被层层处理并到达应用呢? 2、网卡 网卡(Network...
  • 介绍网卡的接受和发送过程,对网卡驱动编写提供参考
  • 问题背景:公司原来为了搜索局域网内的网络视频解码器开发了一个Decoder Finder,用的是UDP广播的...PC端,其实也就是用winpcap,直接和网卡通信,把消息包发出来,并且在接受响应。这样数据包不经过IP和UDP协议栈,
  • 网卡驱动收发过程图解

    千次阅读 2018-10-13 11:49:21
    网卡工作在物理层和数据链路层,主要由PHY/MAC芯片、Tx/Rx FIFO、DMA等组成,其中网线通过变压器接PHY芯片、PHY芯片通过MII接MAC芯片、MAC芯片接PCI总线 PHY芯片主要负责:CSMA/CD、模数转换、编解码、串并转换 ...
  • 数据网卡到应用的过程

    千次阅读 2019-04-07 09:35:11
    本文写写数据网卡到应用的过程,内容与图片很多整理自《网络是怎样连接的》、《Tomcat内核设计与剖析》,有的图片因清晰度不够我进行了重绘。 总览 本文围绕这张图从下至上展开。假设一个HTTP请求的数据到达网卡,...
  • 1、我在观察网卡发送数据与接收数据过程中发现,我从fpga上的一个网卡发送数据,然后另一个网卡接收数据,接收到的数据前面会有55h这8bit的数据。我从PC上发送数据,用fpga上的网卡接收数据,那么在接收到的数据...
  • RX过程 初始化阶段(配置网卡多队列) 1,网卡驱动创建rx descriptor ring,将ring的总线地址写入网卡寄存器 2,网卡驱动为每个descriptor分配sk_buff和数据缓存区 数据接收阶段(处理首包和后续包的区别) 3,网卡...
  • 在Linux内核中,内核为支持的网络设备提供了...接收数据过程在中断函数中用硬件操作(寄存器读写)的方式读网卡芯片里面的数据,提交给上层后,内核就会自动处理好,发送给应用程序。 数据发送过程:以DM9000网卡为例...
  • 现在我们重点是看看内核接受到数据后到vnic_rcv的过程。接受的起点基本是从硬件中断开始。 之前有个表提到了netif_rx(),一般在100M或一下网卡中用这个。 我当前的1000M网卡用的是napi。 简介一下: 在...
  • 本篇笔记写的是2.4中数据报的接收过程,从网卡到网络层的具体路线,2.4中大部分网卡采用的是中断的方式接收数据(好像是从2.5以后开始支持NAPI的,不太确定),本篇笔记总结的是非NAPI,即采用中断接受数据的路线.ok,开始...
  • 网卡数据的发送过程是当网卡准备好并启动后,由上层应用程序首先发出发送数据的命令。然后调用can_send()函数判断网卡是否空闲可以发送数据。如果网卡正忙,请求上层数据等待;如果网卡空闲,即调用send()函数发送...
  • 本文描述Linux网卡驱动程序的实现以及如何优化数据收发过程
  • tun/tap虚拟网卡收发机制解析

    千次阅读 2019-10-29 18:58:57
    tun/tap虚拟网卡收发机制
  • 这里解释下从网卡PHY到IP层的数据接收流程: 这里是以函数调用方式来体现:netif_add——>ethernetif_init——>low_level_init——>ethernetif_input——>low_level_input和tcpip_input——> ...
  • //这里不是发送某个网卡的某个包,而是针对所有流控对象Qdisc的调度,逻辑上与网卡没有直接关系 while (head) { struct Qdisc *q = head; spinlock_t *root_lock; head = head->next_sched; root_lock...
  • 一、数据如何从网卡写入内存中。 1、网卡驱动在内存中分配一片缓冲区用来接受数据包,叫做sk_buffer; 2、将上述缓存区的大小和地址(即接受描述符),存入rx ring buffer 中; 3、驱动通知网卡有一个新的描述符; 4...
  • 最近的一个项目中需要同时使用两块网卡收发UDP组播数据包,并且要求使用Socket的方式接收和发送网络数据包(我不会告诉你们我之前是直接使用SharpPcap来实现的)。在C#中Socket接触的比较早,但是用的不多,特别是在...
  • 网卡如何发送和接收数据包 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 68,383
精华内容 27,353
关键字:

网卡收发数据的过程