一、框架

1)网络协议接口层 向网络层协议提供统一的数据包收发接口,不论上层协议是ARP,还是IP,都通过dev_queue_xmit()函数发送数据,并通过netif_rx()函数接收数据。这一层的存在,使得上层协议独立于具体的设备。

2)网络设备接口层 向协议接口层提供统一的用于描述具体网络设备属性和操作的结构体net_device,给结构体是设备驱动功能层中各函数的容器。实际上,网络设备接口层从宏观上规划了具体操作硬件设备驱动功能层的结构。

3)设备驱动功能层的各函数是网络设备接楼层net_device数据结构体的具体成员,是驱动网络设备硬件完成相应动作的程序,它通过hard_start_xmit()函数启动发送操作,并通过网络设备上的中断触发接收操作。

4)网络设备与媒介层是完成数据包发送和接收的物理实现,包括网络适配器和具体的传输媒介,网络适配器被设备驱动功能层中的函数在物理上驱动。对Linux系统而言,网络设备和媒介都可以是虚拟的。

在设计具体的网络设备驱动程序时,需要完成的主要工作是编写设备驱动功能层的相关函数以填充net_device数据结构体的内容并将net_device注册入内核。

发送大致流程:

Linux 内核中,用 sk_buff(skb)来描述一个缓存,所谓分配缓存空间,就是建立一定数量的 sk_buff。sk_buff 是 Linux 内核网络协议栈实现中最重要的结构体,它是网络数据报文在内核中的表现形式。用户态应用程序(应用层)可以通过系统调用接口访问 BSD Socket 层,传递给 Socket 的数据首先会保存在 sk_buff 对应的缓冲区中,sk_buff 的结构定义在 include/linux/skbuff.h 文件中。它保存数据报文的结构为一个双向链表,如下所示:

当数据被储存到了 sk_buff 缓存区中,网卡驱动的发送函数 hard_start_xmit 也随之被调用,流程图如下所示:

  1. 首先创建一个 Socket,然后调用 write() 之类的写函数通过 Socket 访问网卡驱动,同时将数据保存在 sk_buff 缓冲区。
  2. Socket 调用发送函数 hard_start_xmit。hard_start_xmit 函数在初始化过程中会被挂接成类似于 xx_tx 的某个具体的发送函数,xx_tx 主要实现如下步骤:
    • 从 Tx BD 表中取出一个空闲的 BD。
    • 根据 sk_buff 中保存的数据修改 BD 的属性,一个是数据长度,另一个是数据报文缓存指针。值得注意的是,数据报文缓存指针对应的必须是物理地址,这是因为 DMA 在获取 BD 中对应的数据时只能识别物理地址。
    • 修改该 BD 的状态为就绪态,DMA 模块将自动发送处于就绪态 BD 中所对应的数据。
    • 移动发送 BD 表的指针指向下一个 BD。
  3. DMA 开始将处于就绪态 BD 缓存内的数据发送至网络,当发送完成后自动恢复该 BD 为空闲态。

二、重要的数据结构

1、net_device

include/linux/netdevice.h中定义了net_device结构体,他是网络设备驱动程序中最重要的结构。

该结构体,存储着网络设备的所有信息,每个网络设备都有这种结构。所有设备的net_device结构放在一个全局变量dev_base所有全局列表中。结构体中有一个next指针,用来连接系统中所有网络设备。内核把这些连接起来的设备组成一个链表,并由全局变量dev_base指向链表的第一个元素。

每个网络设备都会有一个对应的实例,然后调用register_netdevie()(定义与文件net/core/dev.c)注册到系统中,注销可以通过unregister_netdevice()。

struct net_device {/* 设备名称,对应 ifconfig 输出的网卡名称,例如 eth0,字母代表网络设备的类型,数字代表此类网络设备的数量 */char            name[IFNAMSIZ];/* 名称hash */struct hlist_node    name_hlist;/*  别名,用于 SNMP 协议 */char             *ifalias;/**    I/O specific fields*    FIXME: Merge these and struct ifmap into one*//*描述设备所用的共享内存,用于设备与内核沟通其初始化和访问只会在设备驱动程序内进行*/unsigned long        mem_end;unsigned long        mem_start;/* 设备自有内存映射到I/O内存的起始地址 */unsigned long        base_addr;/*设备与内核对话的中断编号,此值可由多个设备共享驱动程序使用request_irq函数分配此变量,使用free_irq予以释放*/int            irq;/* 侦测网络状态的改变次数 */atomic_t        carrier_changes;/**    Some hardware also needs these fields (state,dev_list,*    napi_list,unreg_list,close_list) but they are not*    part of the usual set specified in Space.c.*//*网络队列子系统使用的一组标识由__LINK_STATE_xxx标识*/unsigned long        state;struct list_head    dev_list;struct list_head    napi_list;struct list_head    unreg_list;struct list_head    close_list;/* 当前设备所有协议的链表 */struct list_head    ptype_all;/* 当前设备特定协议的链表 */struct list_head    ptype_specific;struct {struct list_head upper;struct list_head lower;} adj_list;/*用于存在其他一些设备功能可报告适配卡的功能,以便与CPU通信使用NETIF_F_XXX标识功能特性*/netdev_features_t    features;netdev_features_t    hw_features;netdev_features_t    wanted_features;netdev_features_t    vlan_features;netdev_features_t    hw_enc_features;netdev_features_t    mpls_features;netdev_features_t    gso_partial_features;/* 网络设备索引号 */int            ifindex;/* 设备组,默认都属于0组 */int            group;struct net_device_stats    stats;atomic_long_t        rx_dropped;atomic_long_t        tx_dropped;atomic_long_t        rx_nohandler;#ifdef CONFIG_WIRELESS_EXTconst struct iw_handler_def *wireless_handlers;struct iw_public_data    *wireless_data;
#endif/* 设备操作接口,主要用来操作网卡硬件 */const struct net_device_ops *netdev_ops;/* ethtool操作接口 */const struct ethtool_ops *ethtool_ops;
#ifdef CONFIG_NET_SWITCHDEVconst struct switchdev_ops *switchdev_ops;
#endif
#ifdef CONFIG_NET_L3_MASTER_DEVconst struct l3mdev_ops    *l3mdev_ops;
#endif
#if IS_ENABLED(CONFIG_IPV6)const struct ndisc_ops *ndisc_ops;
#endif#ifdef CONFIG_XFRMconst struct xfrmdev_ops *xfrmdev_ops;
#endif/* 头部一些操作,如链路层缓存,校验等 */const struct header_ops *header_ops;/* 标识接口特性,IFF_XXX,如IFF_UP */unsigned int        flags;/*用于存储用户空间不可见的标识由VLAN和Bridge虚拟设备使用*/unsigned int        priv_flags;/* 几乎不使用,为了兼容保留 */unsigned short        gflags;/* 结构对齐填充 */unsigned short        padded;/* 与interface group mib中的IfOperStatus相关 */unsigned char        operstate;unsigned char        link_mode;/*接口使用的端口类型*/unsigned char        if_port;/*设备使用的DMA通道并非所有设备都可以用DMA,有些总线不支持DMA*/unsigned char        dma;/*最大传输单元,标识设备能处理帧的最大尺寸Ethernet-1500*/unsigned int        mtu;/* 最小mtu,Ethernet-68 */unsigned int        min_mtu;/* 最大mut,Ethernet-65535 */unsigned int        max_mtu;/*     设备所属类型ARP模块中,用type判断接口的硬件地址类型以太网接口为ARPHRD_ETHER*/unsigned short        type;/*设备头部长度Ethernet报头是ETH_HLEN=14字节*/unsigned short        hard_header_len;unsigned char        min_header_len;/* 必须的头部空间 */unsigned short        needed_headroom;unsigned short        needed_tailroom;/* Interface address info. *//* 硬件地址,通常在初始化过程中从硬件读取 */unsigned char        perm_addr[MAX_ADDR_LEN];unsigned char        addr_assign_type;/* 硬件地址长度 */unsigned char        addr_len;unsigned short        neigh_priv_len;unsigned short          dev_id;unsigned short          dev_port;spinlock_t        addr_list_lock;/* 设备名赋值类型,如NET_NAME_UNKNOWN */unsigned char        name_assign_type;bool            uc_promisc;struct netdev_hw_addr_list    uc;struct netdev_hw_addr_list    mc;struct netdev_hw_addr_list    dev_addrs;#ifdef CONFIG_SYSFSstruct kset        *queues_kset;
#endif/* 混杂模式开启数量 */unsigned int        promiscuity;/* 非零值时,设备监听所有多播地址 */unsigned int        allmulti;/* Protocol-specific pointers */
/* 特定协议的指针 */
#if IS_ENABLED(CONFIG_VLAN_8021Q)struct vlan_info __rcu    *vlan_info;
#endif
#if IS_ENABLED(CONFIG_NET_DSA)struct dsa_switch_tree    *dsa_ptr;
#endif
#if IS_ENABLED(CONFIG_TIPC)struct tipc_bearer __rcu *tipc_ptr;
#endifvoid             *atalk_ptr;/* ip指向in_device结构 */struct in_device __rcu    *ip_ptr;struct dn_dev __rcu     *dn_ptr;struct inet6_dev __rcu    *ip6_ptr;void            *ax25_ptr;struct wireless_dev    *ieee80211_ptr;struct wpan_dev        *ieee802154_ptr;
#if IS_ENABLED(CONFIG_MPLS_ROUTING)struct mpls_dev __rcu    *mpls_ptr;
#endif/** Cache lines mostly used on receive path (including eth_type_trans())*//* Interface address info used in eth_type_trans() */unsigned char        *dev_addr;#ifdef CONFIG_SYSFS/* 接收队列 */struct netdev_rx_queue    *_rx;/* 接收队列数 */unsigned int        num_rx_queues;unsigned int        real_num_rx_queues;
#endifstruct bpf_prog __rcu    *xdp_prog;unsigned long        gro_flush_timeout;/* 如网桥等的收包回调 */rx_handler_func_t __rcu    *rx_handler;/* 回调参数 */void __rcu        *rx_handler_data;#ifdef CONFIG_NET_CLS_ACTstruct tcf_proto __rcu  *ingress_cl_list;
#endifstruct netdev_queue __rcu *ingress_queue;
#ifdef CONFIG_NETFILTER_INGRESS/* netfilter入口 */struct nf_hook_entry __rcu *nf_hooks_ingress;
#endif/* 链路层广播地址 */unsigned char        broadcast[MAX_ADDR_LEN];
#ifdef CONFIG_RFS_ACCELstruct cpu_rmap        *rx_cpu_rmap;
#endif/* 接口索引hash */struct hlist_node    index_hlist;/** Cache lines mostly used on transmit path*//* 发送队列 */struct netdev_queue    *_tx ____cacheline_aligned_in_smp;/* 发送队列数 */unsigned int        num_tx_queues;unsigned int        real_num_tx_queues;/* 排队规则 */struct Qdisc        *qdisc;
#ifdef CONFIG_NET_SCHEDDECLARE_HASHTABLE    (qdisc_hash, 4);
#endif/*可在设备发送队列中排队的最大数据包数*/unsigned long        tx_queue_len;spinlock_t        tx_global_lock;/*     网络层确定传输超时,调用驱动程序tx_timeout接口的最短时间*/int            watchdog_timeo;#ifdef CONFIG_XPSstruct xps_dev_maps __rcu *xps_maps;
#endif
#ifdef CONFIG_NET_CLS_ACTstruct tcf_proto __rcu  *egress_cl_list;
#endif/* These may be needed for future network-power-down code. *//* watchdog定时器 */struct timer_list    watchdog_timer;/* 引用计数 */int __percpu        *pcpu_refcnt;/*     网络设备的注册和除名以两步进行,该字段用于处理第二步*/struct list_head    todo_list;struct list_head    link_watch_list;/* 设备的注册状态 */enum { NETREG_UNINITIALIZED=0,NETREG_REGISTERED,    /* completed register_netdevice */NETREG_UNREGISTERING,    /* called unregister_netdevice */NETREG_UNREGISTERED,    /* completed unregister todo */NETREG_RELEASED,        /* called free_netdev */NETREG_DUMMY,        /* dummy device for NAPI poll */} reg_state:8;/* 设备要被释放标记 */bool dismantle;enum {RTNL_LINK_INITIALIZED,RTNL_LINK_INITIALIZING,} rtnl_link_state:16;bool needs_free_netdev;void (*priv_destructor)(struct net_device *dev);#ifdef CONFIG_NETPOLLstruct netpoll_info __rcu    *npinfo;
#endifpossible_net_t            nd_net;/* mid-layer private */union {void                    *ml_priv;struct pcpu_lstats __percpu        *lstats;struct pcpu_sw_netstats __percpu    *tstats;struct pcpu_dstats __percpu        *dstats;struct pcpu_vstats __percpu        *vstats;};#if IS_ENABLED(CONFIG_GARP)struct garp_port __rcu    *garp_port;
#endif
#if IS_ENABLED(CONFIG_MRP)struct mrp_port __rcu    *mrp_port;
#endifstruct device        dev;const struct attribute_group *sysfs_groups[4];const struct attribute_group *sysfs_rx_queue_group;const struct rtnl_link_ops *rtnl_link_ops;/* for setting kernel sock attribute on TCP connection setup */
#define GSO_MAX_SIZE        65536unsigned int        gso_max_size;
#define GSO_MAX_SEGS        65535u16            gso_max_segs;#ifdef CONFIG_DCBconst struct dcbnl_rtnl_ops *dcbnl_ops;
#endifu8            num_tc;struct netdev_tc_txq    tc_to_txq[TC_MAX_QUEUE];u8            prio_tc_map[TC_BITMASK + 1];#if IS_ENABLED(CONFIG_FCOE)unsigned int        fcoe_ddp_xid;
#endif
#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)struct netprio_map __rcu *priomap;
#endifstruct phy_device    *phydev;struct lock_class_key    *qdisc_tx_busylock;struct lock_class_key    *qdisc_running_key;bool            proto_down;
};

header_ops

Struct header_ops包含以一系列函数指针,主要是对链路层协议头的操作,比如协议头的创建、解析、重建协议头。

struct header_ops {/*协议头的创建函数指针*/int (*create) (struct sk_buff *skb, struct net_device *dev,unsigned short type, const void *daddr,const void *saddr, unsigned len);/*协议头解析函数指针*/int (*parse)(const struct sk_buff *skb, unsigned char *haddr);      /*协议头重构函数指针*/int    (*rebuild)(struct sk_buff *skb);                    int (*cache)(const struct neighbour *neigh, struct hh_cache *hh);void   (*cache_update)(struct hh_cache *hh,const struct net_device *dev,const unsigned char *haddr);
};

net_device_ops

网卡操作函数,如up,down,发包等(ifconfig XXX up,ifconfig XXX down等都是调用的这个结构体中的api)。net_device_ops的成员都是函数指针,是网络驱动程序实现的功能函数。

struct net_device_ops {int   (*ndo_init)(struct net_device *dev);    //提供网络设备的初始化、创建网络设备struct   net_device 数据结构实例、初始化struct net_device的相关数据                                                     域如设备名、i/o端口地址、中断号、向内核注册设备void    (*ndo_uninit)(struct net_device *dev);     //注销设备的时候用int   (*ndo_open)(struct net_device *dev);        //打开网络设备,主要注册的设备才能打开int  (*ndo_stop)(struct net_device *dev);        //停止网络设备,注销的时候调用和open相反netdev_tx_t   (*ndo_start_xmit) (struct sk_buff *skb,struct net_device *dev);             //初始化数据包发送过程,初始化成功后数据包就放入网络适配器的发哦送你个数据缓冲区u16(*ndo_select_queue)(struct net_device *dev,struct sk_buff *skb);                 //当网络设备支持多个发送队列时用于选择发送队列................
}

ndo_select_queue函数选择数据包发送队列。设备驱动程序通过函数ndo_start_xmit从任意发送队列中得到发送的数据包,通过函数static inline u16 skb_get_queue_mapping(const struct sk_buff *skb)得到数据包所在队列

ndo_open函数中需要做如下工作:

  • 使能设备使用的硬件资源,申请 I/O 区域、中断和 DMA 通道等。
  • 调用 Linux 内核提供的 netif_start_queue( )函数,激活设备发送队列。

ndo_close函数需要完成如下工作:

  • 调用 Linux 内核提供的 netif_stop_queue( ) 函数,停止设备传输包。
  • 释放设备所使用的I/O区域、中断和 DMA 资源。
/** 网络设备打开和释放函数模板*/
static int xxx_open(struct net_device *dev)
{/* 申请端口、IRQ等,类似于fops->open */ret = request_irq(dev->irq, &xxx_interrupt, 0, dev->name, dev);···netif_start_queue(dev);···
}static int xxx_release(struct net_device *dev)
{/* 释放端口、IRQ 等,类似于fops->close */free_irq(dev->irq, dev);···netif_stop_queue(dev);···
}

ndo_start_xmit函数会启动数据包的发送,当系统调用驱动程序的xmit函数时,需要向其传入一个sk_buff结构体指针,以使得驱动程序能从上层传递下来的数据包。

*_tx

struct netdev_queue *_tx ____cacheline_aligned_in_smp;

____cacheline_aligned_in_smp是一个宏定义,参见:

linux网络源码分析(1) - TIANCJ - 博客园

____cacheline_aligned和____cacheline_aligned_in_smp_傲世阿龍的博客-CSDN博客_____cacheline_aligned

网络协议栈把数据包放入设备的队列后调用网络设备驱动程序的函数hard_start_xmit来通知网络设备数据包已经准备好了可以发送了。一个网络设备可能有多个发送队列,根据队列优先级的不同调度不同的队列,由struct netdev_queue来管理队列,*_tx指向struct netdev_queue数组的首地址,

struct netdev_queue {
/** read-mostly part*/struct net_device *dev;//队列所属的网络设备netdevice_tracker   dev_tracker;struct Qdisc __rcu  *qdisc;//队列操作函数struct Qdisc     *qdisc_sleeping;unsigned long       tx_maxrate;/** Number of TX timeouts for this queue* (/sys/class/net/DEV/Q/trans_timeout)*/atomic_long_t        trans_timeout;/** write-mostly part*/spinlock_t     _xmit_lock ____cacheline_aligned_in_smp;int         xmit_lock_owner;/** Time (in jiffies) of last Tx*/unsigned long     trans_start;unsigned long       state;//队列状态} ____cacheline_aligned_in_smp;

2、sk_buff

ref:

理解Linux内部网络实现之关键数据结构 sk_buff – Yang Blog

网卡适配器收发数据帧流程 - 云物互联 - 博客园

netdevice.h - include/linux/netdevice.h - Linux source code (v3.3) - Bootlin

Linux网络设备驱动专题_火锅娃的博客-CSDN博客_网络设备驱动

网络设备驱动介绍(浅析)_小嵌同学的博客-CSDN博客_网络设备驱动

网络设备之net_device结构与操作 - AlexAlex - 博客园

网络设备结构体net_device介绍_TCH_world的博客-CSDN博客_net_device结构体

net_device 结构详情_wilber的技术博客_51CTO博客

https://www.csdn.net/tags/NtDaMg0sNTI2NzctYmxvZwO0O0OO0O0O.html

linux 内核协议栈 网络设备抽象 net_device_老王不让用的博客-CSDN博客

linux网络子系统分析(三)—— 设备无关层_whenloce的博客-CSDN博客_netdev_pick_tx

内核net_device设备框架的一个缺陷 - 云+社区 - 腾讯云

Linux内核中的struct net_device的dev_addr和perm_addr之间有什么区别 - VoidCC

linux网络设备驱动(一)相关推荐

  1. Linux网络设备驱动结构概述

    2019独角兽企业重金招聘Python工程师标准>>> 网络设备驱动相比字符型设备的驱动要复杂一些,除了总体上驱动的框架有一些相似外,有很多地方都是不同,但网络设备驱动有一个很大的特 ...

  2. STM32MP157驱动开发——Linux 网络设备驱动

    STM32MP157驱动开发--Linux 网络设备驱动 一.简介 STM32MP1 GMAC 接口简介 YT8511C 详解 二.驱动开发 1.网络外设的设备树 2.设备驱动 三.测试 网速测试 参 ...

  3. linux pci 网卡驱动,linux网络设备驱动_pci网卡

    <linux网络设备驱动_pci网卡>由会员分享,可在线阅读,更多相关<linux网络设备驱动_pci网卡(12页珍藏版)>请在技术文库上搜索. 1. LinuxLinux 网 ...

  4. Linux网络设备驱动-以太网驱动分析

    1.概述 网络上数据包的发送和接收由网络设备完成.网络设备与字符设备.块设备不同,其并不对应于/dev目录下的文件,也不能使用常规的操作方法操作网络设备.现在比较通用的做法是通过套接字访问网络设备.网 ...

  5. 编写Linux网络设备驱动(上)

    编写Linux网络设备驱动(上) <编写Linux网络设备驱动(上)> 来自:刘建文 | 学术半·IT歌·文 作者:刘建文 关键字:Linux 驱动程序 永久链接地址:http://art ...

  6. 《Linux设备驱动开发详解》——第16章 Linux网络设备驱动一

    Linux系统对网络设备驱动定义了4各层次:网络协议接口层,网络设备接口层,提供实际功能的设备驱动功能层,网络设备与媒介层 16.1 Linux网络设备驱动的结构 (1)网络协议接口层:提供统一的数据 ...

  7. linux网络设备驱动结构体,Linux网络设备驱动之设备驱动的注册与注销(二)

    网络设备驱动的注册与注销由 register_netdev( ) 和 unregister_netdev( ) 函数完成,这两个函数的原型为: int register_netdev(struct n ...

  8. Linux 网络设备驱动开发(一) —— linux内核网络分层结构

    Linux内核对网络驱动程序使用统一的接口,并且对于网络设备采用面向对象的思想设计. Linux内核采用分层结构处理网络数据包.分层结构与网络协议的结构匹配,既能简化数据包处理流程,又便于扩展和维护. ...

  9. Linux网络设备驱动概述

    一.概述     Linux网络设备驱动程序遵循通用的接口,设计时采用的是面向对象的方法,一个设备就是一个对象(net_device结构).一个网络设备最基本的方法有初始化.发送和接收等. Linux ...

  10. 《linux设备驱动开发详解》笔记——14 linux网络设备驱动

    14.1 网络设备驱动结构 网络协议接口层:硬件无关,标准收发函数dev_queue_xmit()和netif_rx();  注意,netif_rx是将接收到的数据给上层,有时也在驱动收到数据以后调用 ...

最新文章

  1. Nature:如何做一篇肠道菌群免疫的顶级文章
  2. leetcode-169.求众数
  3. 2020年第十八届西电程序设计竞赛网络预选赛之Problem A 失败的在线考试
  4. 在生产环境中使用 Sentinel
  5. 如何用firebug调试js
  6. centos 7 菜鸟第一天 双启动
  7. ThreadLocal 内存泄漏问题
  8. Intel-VT 与虚拟化限制
  9. Python六大开源框架对比:Web2py略胜一筹(转)
  10. [暗黑破坏神2]PLUGY与HACKMAP共存
  11. FPGA代码规则检查工具
  12. 笛卡尔积:(SQL语句中)
  13. 计算机技术概论知识点,《计算机基础概论》知识点.pdf
  14. matlab读取sgy格式文件的m文件,matlab读取segy格式的文件
  15. Vue Tags Input
  16. ppt矩形里面的图片怎么放大缩小_如何在PPT中插入大量图片而又保持其美感?
  17. 简单 Python 快乐之旅之:Python 基础语法之 JSON 专题
  18. 批处理 Win10锁屏背景图片的提取
  19. 复利计算器app发布
  20. 第5组UI 让你的界面酷炫到底-ViewAniMator之ViewSwitcher、ImageSwitcher(图像切换器)、TextSwitcher(文本切换器)、ViewFlipper

热门文章

  1. 2022-2028年全球与中国工业用智能眼镜行业产销需求与投资预测分析
  2. 网页 Failed to initialize player‘s 3D settings 小游戏4399 修复
  3. SQL获取两个日期之间的天数
  4. 双平衡吉尔伯特混频器设计(项目设计论文)
  5. 安卓版本与adb的问题
  6. 孙思邈的养生哲学和孔子的阴阳和谐之道
  7. rebase和merge区别
  8. Unity3D Shader 新手教程(5/6) —— Bumped Diffuse Shader
  9. 浏览器打开JSP文件只显示源代码的问题
  10. 高德足迹地图在哪里_高德地图怎么点亮城市 足迹地图查看方法