精华内容
下载资源
问答
  • linux网络设备驱动

    千次阅读 2017-02-08 21:44:18
    网络设备驱动架构Linux网络设备驱动程序的体系结构,依次为网络协议接口层,网络设备接口层,提供实际功能的设备驱动功能层以及网络设备与媒介层。 网络协议接口层向网络层协议提供统一的数据包收发接口,不论上层...

    网络设备驱动架构

    Linux网络设备驱动程序的体系结构,依次为网络协议接口层,网络设备接口层,提供实际功能的设备驱动功能层以及网络设备与媒介层。
    网络协议接口层向网络层协议提供统一的数据包收发接口,不论上层协议是ARP,还是IP,都通过dev_queue_xmit()函数发送数据,并通过netif_rx()函数接受数据。
    网络设备接口层向协议接口层提供统一的用于描述具体网络属性和操作的结构体net_device,该结构体是设备驱动功能层中各函数的容器。
    设备驱动功能层的各函数是网络设备接口层net_device数据结构的具体成员,是驱使网络设备硬件完成相应动作的程序,它通过hard_start_xmit() 函数启动发送操作,并通过网络设备上的中断触发操作。
    网络设备与媒介层是完成数据包发送和接收的物理实体,包括网络适配器和具体的传输媒介,网络适配器被设备驱动功能层中的函数在物理上驱动。
    这里写图片描述

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

    struct net_device {
        char            name[IFNAMSIZ];
        struct hlist_node   name_hlist;
        char            *ifalias;
        /*
         *  I/O specific fields
         *  FIXME: Merge these and struct ifmap into one
         */
        unsigned long       mem_end;
        unsigned long       mem_start;
        unsigned long       base_addr;
        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.
         */
    
        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;
    
        struct {
            struct list_head upper;
            struct list_head lower;
        } all_adj_list;
    
        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;
        int         group;
    
        struct net_device_stats stats;
    
        atomic_long_t       rx_dropped;
        atomic_long_t       tx_dropped;
        atomic_long_t       rx_nohandler;
    
    .....
    
        /* for setting kernel sock attribute on TCP connection setup */
    #define GSO_MAX_SIZE        65536
        unsigned int        gso_max_size;
    #define GSO_MAX_SEGS        65535
        u16         gso_max_segs;
    
    #ifdef CONFIG_DCB
        const struct dcbnl_rtnl_ops *dcbnl_ops;
    #endif
        u8          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;
    #endif
        struct phy_device   *phydev;
        struct lock_class_key   *qdisc_tx_busylock;
        bool            proto_down;
    };
    

    网络设备驱动的注册于注销

    网络设备的注册与注销由register_netdev()ungister_netdev()函数完成。

    net_device结构体的分配和网络设备驱动的注册需在网络设备驱动初始化是进行,而net_device结构体的释放和网络设备驱动的注销在设备或驱动被移除的时候执行。

    net_device_ops结构体是网络设备的一系列硬件操作行数的集合。

    struct net_device_ops {
        int         (*ndo_init)(struct net_device *dev);
        void            (*ndo_uninit)(struct net_device *dev);
        int         (*ndo_open)(struct net_device *dev);
        int         (*ndo_stop)(struct net_device *dev);
        netdev_tx_t     (*ndo_start_xmit)(struct sk_buff *skb,
                              struct net_device *dev);
        netdev_features_t   (*ndo_features_check)(struct sk_buff *skb,
                                  struct net_device *dev,
                                  netdev_features_t features);
        u16         (*ndo_select_queue)(struct net_device *dev,
                                struct sk_buff *skb,
                                void *accel_priv,
                                select_queue_fallback_t fallback);
        void            (*ndo_change_rx_flags)(struct net_device *dev,
                                   int flags);
        void            (*ndo_set_rx_mode)(struct net_device *dev);
        int         (*ndo_set_mac_address)(struct net_device *dev,
                                   void *addr);
        int         (*ndo_validate_addr)(struct net_device *dev);
        int         (*ndo_do_ioctl)(struct net_device *dev,
                                struct ifreq *ifr, int cmd);
        int         (*ndo_set_config)(struct net_device *dev,
                                  struct ifmap *map);
        int         (*ndo_change_mtu)(struct net_device *dev,
                              int new_mtu);
        int         (*ndo_neigh_setup)(struct net_device *dev,
                               struct neigh_parms *);
        void            (*ndo_tx_timeout) (struct net_device *dev);
    
        struct rtnl_link_stats64* (*ndo_get_stats64)(struct net_device *dev,
                                 struct rtnl_link_stats64 *storage);
        struct net_device_stats* (*ndo_get_stats)(struct net_device *dev);
    
        int         (*ndo_vlan_rx_add_vid)(struct net_device *dev,
                                   __be16 proto, u16 vid);
        int         (*ndo_vlan_rx_kill_vid)(struct net_device *dev,
                                    __be16 proto, u16 vid);
    #ifdef CONFIG_NET_POLL_CONTROLLER
        void                    (*ndo_poll_controller)(struct net_device *dev);
        int         (*ndo_netpoll_setup)(struct net_device *dev,
                                 struct netpoll_info *info);
        void            (*ndo_netpoll_cleanup)(struct net_device *dev);
    #endif
    
    ......
    
        int         (*ndo_get_lock_subclass)(struct net_device *dev);
        int         (*ndo_set_tx_maxrate)(struct net_device *dev,
                                  int queue_index,
                                  u32 maxrate);
        int         (*ndo_get_iflink)(const struct net_device *dev);
        int         (*ndo_change_proto_down)(struct net_device *dev,
                                 bool proto_down);
        int         (*ndo_fill_metadata_dst)(struct net_device *dev,
                                   struct sk_buff *skb);
        void            (*ndo_set_rx_headroom)(struct net_device *dev,
                                   int needed_headroom);
    };
    

    网络设备的初始化

    网络设备的初始化
    进行硬件上的准备工作,检查网络设备是否存在,如果存在,则检测设备所使用的硬件资源。
    进行软件接口上的准备工作,分配net_device结构体并对齐数据和函数指针成员赋值。
    获得设备的私有信息指针并初始化各成员的值,如果私有信息包括自旋锁或信号量等并发或同步机制,则需对其进行初始化。

    网络设备的打开与释放

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

    网络设备关闭
    调用linux内核提供的netif_stop_queue()函数,停止设备传输包
    释放设备所使用的I/O驱动,中断和DMA资源。

    网络设备驱动包发送

    linux网络子系统在发送数据包时,会用驱动程序提供的hard_start_transmit()函数,该函数用于启动数据包的发送。在设备初始化的时候,这个函数指针需被初始化以指向设备的xxx_tx()函数。

    数据包发送流程
    网络设备驱动程序从上层协议传递过来的sk_buff参数获得数据包的有效数据和长度,将有效数据放入临时缓冲区。
    对于以太网,如果有效数据的长度小于以太网冲突检测所要求数据帧的最小长度ETH_ZLEN,则给临时缓冲区的末尾填充0。
    设置硬件的寄存器,驱使网络设备进行数据发送操作。

    数据接收流程

    网络设备接收数据的主要方法是由中断引发设备的中断处理函数,中断处理函数判断中断类型,如果为接收中断,则读取接收到的数据,分配sk_buffer数据结构和数据缓冲区,将接收到的数据复制到数据缓冲区,并调用netif_rx()函数将sk_buffer传递给上层协议。

    网络连接状态

    网络适配器硬件电路可以检测出链路上是否有载波,载波反映了网络的连接是否正常,网络设备驱动可以通过netif_carrier_on()netif_carrier_off()函数改变设备的连接状态,如果驱动检测到连接状态发生变化,也应该以netif_carrier_on()netif_carrier_off()函数显式地通知内核。

    函数netif_carrier_ok()可用于向调用者返回链路上的载波信号是否存在。

    展开全文
  • Linux-虚拟网络设备-tun/tap

    万次阅读 2017-09-05 18:44:33
     TUN和TAP设备是Linux内核虚拟网络设备,纯软件实现。 OS向连接到TUN/TAP设备的用户空间程序发送报文;用户空间程序可像物理口发送报文那像向TUN/TAP口发送报文,在这种情况下,TUN/TAP设备发送(或注入)报文到...

    基本概念

    A gateway to userspace。

         TUN和TAP设备是Linux内核虚拟网络设备,纯软件实现。
         OS向连接到TUN/TAP设备的用户空间程序发送报文;用户空间程序可像物理口发送报文那像向TUN/TAP口发送报文,在这种情况下,TUN/TAP设备发送(或注入)报文到OS协议栈,就像报文从物理端口收到一样。
    链接:

    TUN/TAP: The user-space application/VM can read or write an ethernet frame to the tap interface and it would reach the host kernel, where it would be handled like any other ethernet frame that reached the kernel via physical (e.g. eth0) ports. You can potentially add it to a software-bridge (e.g. linux-bridge)

    如何工作

         TUN/TAP 为简单的点对点或以太网设备,不是从物理介质接收数据包,而是从用户空间程序接收;不是通过物理介质发送数据包,而是将它们发送到用户空间程序。
         假设您在 tap0 上配置 IPX,那么每当内核向 tap0 发送一个 IPX 数据包时,它将传递给应用程序(例如 VTun)。应用程序加密、压缩数据包,并通过 TCP/UDP 发送到对端。对端的应用程序解压缩、解密接收的数据包,并将数据包写入 TAP 设备,然后内核处理数据包,就像该数据包来自真实的物理设备。
         在Linux内核中添加了一个TUN/TAP虚拟网络设备的驱动程序和一个与之相关的字符设备/dev/net/tun,字符设备tun作为用户空间和内核空间交换数据的接口。
         用户空间的应用程序可以通过这个设备文件来和内核中的驱动程序进行交互,其他操作方式和普通文件操作无异。当内核将数据包发送到虚拟网络设备时,数据包被保存在设备相关的一个队列中,直到用户空间程序通过打开的字符设备tun的描述符读取时,它才会被拷贝到用户空间的缓冲区中,其效果就相当于,数据包直接发送到了用户空间。通过系统调用write发送数据包时其原理与此类似。
         tun/tap驱动程序中包含两部分:字符设备驱动和网卡驱动。利用网卡驱动部分接受来自tcp/ip协议栈的网络分包并发送或者反过来将接收到的网络分包传给协议栈处理。而字符设备驱动部门将网络分包在内核与用户态之间传送,模拟物理链路的数据接受和发送。tun/tap驱动很好的实现了两种驱动的结合。

    用途

         用于加密、VPN、隧道、虚拟机等等(encryption, VPN, tunneling,virtual machines)。
         tun/tap设备的用处是将协议栈中的部分数据包转发给用户空间的应用程序,给用户空间的程序一个处理数据包的机会。于是比较常用的数据压缩、加密等功能就可以在应用程序中实现。tun/tap设备最常用的场景是VPN,包括tunnel以及应用层的IPSec等。

    tap/tun在libvirt中的应用


    这里写图片描述

    VPN


    这里写图片描述

    其他

    常用命令

    root@ubuntu:~# ip tuntap help
    Usage: ip tuntap { add | del } [ dev PHYS_DEV ] 
              [ mode { tun | tap } ] [ user USER ] [ group GROUP ]
              [ one_queue ] [ pi ] [ vnet_hdr ] [ multi_queue ]
    
    Where: USER  := { STRING | NUMBER }
           GROUP := { STRING | NUMBER }
    

    veth、tun、tap比对


    这里写图片描述

          tun 是点对点的设备 , 而 tap 是一个普通的以太网卡设备 。 也就是说 ,tun 设备其实完全不需要有物理地址的 。 它收到和发出的包不需要 arp, 也不需要有数据链路层的头 。 而 tap 设备则是有完整的物理地址和完整的以太网帧 。

    TAP (network tap) operates much like TUN however instead of only being able to write and receive layer 3 packets to/from the file descriptor it can do so with raw ethernet packets. You will typically see tap devices used by KVM/Qemu virtualization, where a TAP device is assigned to a virtual guests interface during creation.


    TUN(Tunel)设备模拟网络层设备,处理三层报文,如IP报文。TAP设备模型链路层设备,处理二层报文,比如以太网帧。TUN用于路由,而TAP用于创建网桥。

    示例(来源于网络)

    示例程序

    收到tun设备的数据包之后,只打印出收到了多少字节的数据包,其它的什么都不做

    #include <net/if.h>
    #include <sys/ioctl.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <string.h>
    #include <sys/types.h>
    #include <linux/if_tun.h>
    #include <stdlib.h>
    #include <stdio.h>
    
    int tun_alloc(int flags)
    {
    
        struct ifreq ifr;
        int fd, err;
        char *clonedev = "/dev/net/tun";
    
        if ((fd = open(clonedev, O_RDWR)) < 0) {
            return fd;
        }
    
        memset(&ifr, 0, sizeof(ifr));
        ifr.ifr_flags = flags;
    
        if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0) {
            close(fd);
            return err;
        }
    
        printf("Open tun/tap device: %s for reading...\n", ifr.ifr_name);
    
        return fd;
    }
    
    int main()
    {
    
        int tun_fd, nread;
        char buffer[1500];
    
        /* Flags: IFF_TUN   - TUN device (no Ethernet headers)
         *        IFF_TAP   - TAP device
         *        IFF_NO_PI - Do not provide packet information
         */
        tun_fd = tun_alloc(IFF_TUN | IFF_NO_PI);
    
        if (tun_fd < 0) {
            perror("Allocating interface");
            exit(1);
        }
    
        while (1) {
            nread = read(tun_fd, buffer, sizeof(buffer));
            if (nread < 0) {
                perror("Reading from interface");
                close(tun_fd);
                exit(1);
            }
    
            printf("Read %d bytes from tun/tap device\n", nread);
        }
        return 0;
    }
    

    演示

    #--------------------------第一个shell窗口----------------------
    #将上面的程序保存成tun.c,然后编译
    root@ubuntu:~# gcc tun.c -o tun
    
    #启动tun程序,程序会创建一个新的tun设备,
    #程序会阻塞在这里,等着数据包过来
    root@ubuntu:~# ./tun 
    Open tun/tap device: tun0 for reading...
    Read 84 bytes from tun/tap device
    Read 84 bytes from tun/tap device
    Read 84 bytes from tun/tap device
    Read 84 bytes from tun/tap device
    
    
    
    #--------------------------第二个shell窗口----------------------
    #启动抓包程序,抓经过tun0的包
    root@ubuntu:/home/sunld# tcpdump -i tun0
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes
    02:53:08.840817 IP 192.168.209.138 > 192.168.209.139: ICMP echo request, id 9828, seq 1, length 64
    02:53:09.839871 IP 192.168.209.138 > 192.168.209.139: ICMP echo request, id 9828, seq 2, length 64
    02:53:10.850205 IP 192.168.209.138 > 192.168.209.139: ICMP echo request, id 9828, seq 3, length 64
    02:53:11.851285 IP 192.168.209.138 > 192.168.209.139: ICMP echo request, id 9828, seq 4, length 64
    
    
    #--------------------------第三个shell窗口----------------------
    #./tun启动之后,通过ip link命令就会发现系统多了一个tun设备,
    27: tun0: <POINTOPOINT,MULTICAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 500
        link/none 
    #新的设备没有ip,我们先给tun0配上IP地址
    root@ubuntu:/home/sunld# ip addr add 192.168.209.138/24 dev tun0
    #默认情况下,tun0没有起来,用下面的命令将tun0启动起来
    root@ubuntu:/home/sunld# ip link set dev tun0 up
    
    #尝试ping一下192.168.209.0/24网段的IP,
    #由于我们的程序中收到数据包后,啥都没干,相当于把数据包丢弃了,
    #所以这里的ping根本收不到返回包,
    #但在前两个窗口中可以看到这里发出去的四个icmp echo请求包,
    #说明数据包正确的发送到了应用程序里面,只是应用程序没有处理该包
    root@ubuntu:/home/sunld# ping -c 4 192.168.209.139 -I tun0
    PING 192.168.209.139 (192.168.209.139) from 192.168.209.138 tun0: 56(84) bytes of data.
    

    参考资料

    kernel doc tuntap
    virtual networking devices in linux
    Linux Networking Explained
    Tun/Tap interface tutorial
    TUN, TAP and Veth - Virtual Networking Devices Explained
    虚拟机网卡和linux bridge上tap设备的关系
    Linux虚拟网络设备之tun/tap

    展开全文
  • linux网络设备理解

    千次阅读 2016-11-15 21:16:39
    网络层次linux网络设备驱动与字符设备和块设备有很大的不同。 1. 字符设备和块设备对应/dev下的一个设备文件。而网络设备不存在这样的设备文件。网络设备使用套接字socket访问,虽然也使用read,write系统调用,但...

    网络层次

    linux网络设备驱动与字符设备和块设备有很大的不同。
    1. 字符设备和块设备对应/dev下的一个设备文件。而网络设备不存在这样的设备文件。网络设备使用套接字socket访问,虽然也使用read,write系统调用,但这些调用只作用于软件对象。
    2. 块设备只响应来自内核的请求,而网络驱动程序异步接收来自外部世界的数据包,并向内核请求发送到内核。

    linux内核中网络子系统的设计基于设备无关及协议无关思想。即无论什么网卡驱动、网络协议,都对应统一的驱动程序。

    inux网络协议栈层次有四层:
    网络协议接口层
    网络设备接口层
    设备驱动功能层
    网络设备媒介层

    这里写图片描述

    下面只分析下linux是如何实现网络设备的设备无关性及协议无关性。

    网络协议接口层: 给上层协议提供透明的数据包发送和接收接口。与协议无关
    当要发送数据包时,ARP协议或者IP协议,都将调用这一层中的dev_queue_xmit()函数发送该数据包。
    上层对数据包的接收时,通过netif_rx()接收。具体协议处理在上级网络协议处理。
    这其中需要一个很重要的结构体struct sk_buff.

    网络设备接口层:为千变万化的网络设备定义统一、抽象的数据结构net_device结构体,实现多种硬件在软件层次上的统一。与设备无关。
    net_device结构体在内核中指代一个网络设备,网络设备驱动程序只需通过填充net_device中的具体成员并注册net_device即可实现硬件操作函数与内核的挂接。使用统一的注册、注销及初始化等一系列函数。

    设备驱动功能层:填充net_device中的成员。管理物理网络设备。
    包括设备打开、停止、传输等操作。由于网络包得接收可以由中断产生,所以设备驱动功能层中另一个主题部分是中断处理函数,负责读取硬件上接收的数据包并传送给上层协议。

    网络设备与媒介层:对应于实际的物理设备
    包括设备寄存器的描述,寄存器读写函数等。

    网络协议

    这里主要进行数据包的收发,使用函数原型为:
    dev_queue_xmit(struct sk_buff *skb);int netif_rx(struct sk_buff *skb);
    这里使用了一个skb_buff结构体,定义于include/linux/skbuff.h中,它的含义为“套接字缓冲区”,用于在Linux网络子系统各层间传输数据

    sk_buff中重要的数据成员

    struct device *dev;正在处理该包的设备
    __u32 sadd;r//IP元地址
    __u32 daddr;//IP目的地址
    __u32 raddr;//IP路由器地址
    unsigned char *head;//分配空间的开始
    unsigned char *data;//有效数据的开始
    unsigned char *tail;//有效数据的结束
    unsigned char *end;//分配空间的结束
    unsigned long len;//有效数据的长度

    sk_buff操作
    1) 分配:分配一个sk_buff结构,供协议栈代码使用

    struct sk_buff *alloc_skb(unsigned int len, int priority);
    struct sk_buff *dev_alloc_skb(unsigned int len);

    分配一个缓冲区。alloc_skb函数分配一个缓冲区并初始化skb->data和skb->tail为skb->head。参数len为数据缓冲区的空间大小,通常以L1_CACHE_BYTES字节(对ARM为32)对齐,参数priority为内存分配的优先级。dev_alloc_skb()函数以GFP_ATOMIC优先级进行skb的分配。
    2) 释放:

    void kfree_skb(struct sk_buff *skb);
    void dev_kfree_skb(struct sk_buff *skb); 

    Linux内核内部使用kfree_skb()函数,而网络设备驱动程序中则最好使用dev_kfree_skb()。
    sk_buff中比较重要的成员是指向数据包中数据的指针
    用于寻址数据包中数据的指针,head指向已分配空间开头,data指向有效的octet开头,tail指向有效的octet结尾,而end指向tail可以到达的最大地址。如果不这样做而分配一个大小固定的缓冲区,如果buffer不够用,则要申请一个更大的buffer,拷贝进去再增加,这样降低了性能。
    3)变更
    unsigned char *skb_put(struct sk_buff *skb, int len);
    将taill指针向后移动len长度,并返回tail移动之前的值。用于向skb有效数据区域末尾添加数据。
    unsigned char *skb_push(struct sk_buff *skb, int len);
    将data指针向前移动len长度。并返回移动之后的值。用于向skb有效数据区域前端添加数据(包头)。
    unsigned char *skb_pull(struct sk_buff *skb, int len);
    void skb_reserve(struct sk_buff ×skb, int len);

    网络设备

    系统检测到了PCI设备,并启用它,但它只是一支硬件设备(网卡设备),而Linux的网络协议栈只认得[网络设备]。[网络设备]是一支逻辑设备,由结构net_device表征。也就是说,网络协议栈向[网络设备]发出命令,而[网络设备]的驱动将这些命令传递到PCI[网卡设备]。表3列出了结构net_device的一些重要数据域,

    struct net_device  
    {  
        char *name;  
        unsigned long base_addr;  
        unsigned char addr_len;  
        unsigned char dev_addr[MAX_ADDR_LEN];  
        unsigned char broadcast[MAX_ADDR_LEN];  
        unsigned short hard_header_len;  
        unsigned char irq;  
        int (*open) (struct net_device *dev);  
        int (*stop) (struct net_device *dev);  
        int (*hard_start_xmit) (struct sk_buff *skb,  struct net_device *dev);  
        struct net_device_stats* (*get_stats)(struct net_device *dev);  
        void *priv;  
    };  

    以上是结构net_device部分重要成员,以下简介这些成员的用途:

    • name – 设备的名称。如果名称的第一个字符是null,那么register_netdev分配给它取名为“ethN”,其中N是合适的数字。例如,如果您的系统已经有eth0和eth1,您的设备将被命名的eth2。
    • base_addr – I/O基地址。
    • addr_len – 硬件地址(MAC地址)的长度。以太网接口地址长度为6字节。
    • dev_addr – 硬件地址(以太网地址或MAC地址)
    • broadcast – 设备的广播地址。以太网接口的广播地址是FF:FF:FF:FF:FF:FF
    • hard_header_len – “硬件头的长度”是数据包硬件头的八位位组(octets)的数量。 以太网接口的hard_header_len的值是14
    • IRQ – 分配的中断号
    • open – 这是打开设备函数的指针。这个函数在用ifconfig命令激活设备时被调用,例如“ifconfig eth0 up”。 open函数负责向系统申请所需的系统资源需求(I/O端口,IRQ,DMA等),启用硬件和递增模块的使用计数。
    • stop – 这是停止设备函数的指针。这个函数在用ifconfig命令停用设备时被调用,例如“ifconfig eth0 down”。 stop函数释放所有open函数获得的资源。
    • hard_start_xmit – 此函数在传输线路上发送一个给定的数据包。该函数的第一个参数是指向结构sk_buff指针。结构sk_buff的是通过Linux网络协议栈的数据包。本文并不需要详细了解有关的sk_buff的结构的细节,你可以在下网址获得更多的结构sk_buff的信息:http://www.tldp.org/LDP/khg/HyperNews/get/net/net-intro.html
    • get_stats – 此函数提供了接口统计信息。命令“ifconfig eth0”的很多输出内容来自get_stats。
    • priv – 驱动程序的私有数据域。驱动程序拥有这一数据域,并可以使用它。

    特别注意,net_device没有接收数据包的成员函数,这是因为接收数据包是由设备的[中断处理程序]负责的

    驱动的实现

    1).初始化(init)
    设备探测工作在init方法中进行,一般调用一个称之为probe方法的函数
    初始化的主要工作时检测设备,配置和初始化硬件,最后向系统申请这些资源。此外填充该设备的dev结构,我们调用内核提供的ether_setup方法来设置一些以太网默认的设置。

    2)打开(open)
    open这个方法在网络设备驱动程序里是网络设备被激活时被调用(即设备状态由down变成up)
    实际上很多在初始化的工作可以放到这里来做。比如说资源的申请,硬件的激活。如果dev->open返回非0,则硬件状态还是down,
    注册中断、DMA等;设置寄存器,启动设备;启动发送队列
    一般注册中断都在init中做,但在网卡驱动程序中,注册中断大部分都是放在open中注册,因为要经常关闭和重启网卡

    3)关闭(stop)
    stop方法做和open相反的工作
    可以释放某些资源以减少系统负担
    stop是在设备状态由up转为down时被调用

    4)发送(hard_start_xmit)
    在系统调用的驱动程序的hard_start_xmit时,发送的数据放在一个sk_buff结构中。一般的驱动程序传给硬件发出去。也有一些特殊的设备比如说loopback把数据组成一个接收数据在传送给系统或者dummy设备直接丢弃数据。
    如果发送成功,hard_start_xmit方法释放sk_buff。如果设备暂时无法处理,比如硬件忙,则返回1。

    5) 接收
    驱动程序并存在一个接受方法。当有数据收到时驱动程序调用netif_rx函数将skb交交给设备无关层。
    一般设备收到数据后都会产生一个中断,在中断处理程序中驱动程序申请一块sk_buff(skb)从硬件中读取数据位置到申请号的缓冲区里。
    接下来填充sk_buff中的一些信息。
    中断有可能是收到数据产生也可能是发送完成产生,中断处理程序要对中断类型进行判断,如果是收到数据中断则开始接收数据,如果是发送完成中断,则处理发送完成后的一些操作,比如说重启发送队列。
    接收流程:
    1、分配skb=dev_alloc_skb(pkt->datalen+2)
    2、从硬件中读取数据到skb
    3、调用netif_rx将数据交给协议栈

    展开全文
  • 首先我们看一下一个主流多队列网卡(E1000)跟多核CPU之间的关系图:非多队列:linux的网卡由结构体net_device表示,一个该结构体对应一个可以调度的数据包发送队列。数据包的实体在内核中以结构体sk_buff(skb),...

    首先我们看一下一个主流多队列网卡(E1000)跟多核CPU之间的关系图:


    非多队列:

    linux的网卡由结构体net_device表示,一个该结构体对应一个可以调度的数据包发送队列。

    数据包的实体在内核中以结构体sk_buff(skb),形如:


    多队列:

    一个网卡可以拥有多个队列


    接下来,看看TX引擎是如何工作的(注:对于发送和接收数据包有两个名词,分别应对TX,RX)


    解释:

    函数-dev_queue_xmit():入队一个buffer以传输到网络驱动设备。

    配合该函数的源码来解释上图的传输过程:

    步骤一:可以看到如果设备支持队列,则数据包入设备队列。在入队操作前后,有加锁和释放队列锁的过程。


    步骤二:调出设备的qdisc(该对象是队列的排队规则)


    QDisc(排队规则)是queueingdiscipline的简写,它是理解流量控制(traffic control)的基础。无论何时,内核如果需要通过某个网络接口发送数据包,它都需要按照为这个接口配置的qdisc(排队规则)把数据包加入队列。然后,内核会尽可能多地从qdisc里面取出数据包,把它们交给网络适配器驱动模块。最简单的QDisc是pfifo它不对进入的数据包做任何的处理,数据包采用先入先出的方式通过队列。不过,它会保存网络接口一时无法处理的数据包。

    步骤三:重置skb的队列映射,置为0


    步骤四:tx lock->hard_start_xmit

    到这里,我们好像没有看到tx_lock、hard_start_xmit函数,反而我们在无队列的设备分支中看到了这些:


    Dev_hard_start_xmit的定义:


    很明显我们应该拨开云雾看到一些本质,再次回到设备支持队列的分支中(这才是我们关心的):

    不管怎么样,你总该有发送的函数调用吧,就是下面圈起来的这个:


    果不其然,这是一个封装函数:


    参考:

    http://www.landley.net/kdocs/ols/2007/ols2007v2-pages-305-310.pdf

    http://vger.kernel.org/~davem/davem_nyc09.pdf

    http://www.chineselinuxuniversity.net/kerneldocs/networking/API-dev-queue-xmit.html

    http://apps.hi.baidu.com/share/detail/36206005

    http://cache.baidu.com/c?m=9d78d513d98017f419bc837f7d01d0120e55f0237b8bc7150ec3e54c84145d563164f4cd25351174c4b5777075d95e2cebe74703234460e99492ce0c9fac935b3295776a2d499141658243f4971532c157c304b2ff4ab7e9e732e4ff8f8cc2040d97061832daabc8015c41ca65ed4771a5fdc816424240b8fa3013a4537d2c992742b750f997682858df&p=ce7ddc1187904eac59b5c4710e14d625&user=baidu&fm=sc&query=qdisc%5Frun+%D6%B4%D0%D0%B9%FD%B3%CC&qid=f6ae9cf00151dde4&p1=1

    http://lwn.net/Articles/289137/

    展开全文
  • Linux队列网卡

    千次阅读 2015-01-09 16:10:57
    队列网卡是一种技术,最初是用来解决网络IO QoS (quality of service)问题的,后来随着网络IO的带宽的不断提升,单核CPU不能完全处满足网卡的需求,通过多队列网卡驱动的支持,将各个队列通过中断绑定到不同的...
  • Linux设备驱动之网络设备驱动

    千次阅读 2011-04-14 22:03:00
    网络设备驱动程序的主要功能是:(1)模块加载或内核启动相关的初始化处理(2)清除模块时的处理(3)网络设备的检索和探测(4)网络设备的初始化和注册(5)打开或关闭网络设备(6)发送网络数据(7)接收网络数据(8)中断处理(在
  • LINUX HTB队列规定用户指南

    千次阅读 2017-04-17 08:31:20
    blogId=224 HTB官方网站: http://luxik.cdi.cz/~devik/qos/htb/ LINUX HTB队列规定是LINUX QOS 内容的部分, 主要是配合TC工具进行流量控制的一种算法, 和CBQ 比HTB有它自身的特点, 有关CBQ的资料相...
  • Linux网络设备分析

    千次阅读 2011-12-02 16:31:40
    Linux网络设备分析 潘纲 9811536 浙江大学计算机系 pg@ccnt.zju.edu.cn [摘要] 在本文中,首先概括了网络设备总体特征和工作原理,接着在分析了一个 重要的数据结构device后,重点剖析了网络设备的整个初始...
  •  Linux内核对网络驱动程序使用统一的接口,并且对于网络设备采用面向对象的思想设计。  Linux内核采用分层结构处理网络数据包。分层结构与网络协议的结构匹配,既能简化数据包处理流程,又便于扩展和维护。 ...
  • 虽然对于网络的正式介绍一般都参考了 OSI(Open Systems Interconnection)模型,但是本文对 Linux 中基本网络栈的介绍分为四层的 Internet 模型(如图 1 所示)。 图 1. 网络栈的 Internet 模型 这个栈的最底部...
  • 二.Linux网络子系统  Linux网络子系统的顶部是系统调用接口层。它为用户空间提供的应用程序提供了一种访问内核网络子系统的方法(socket)。位于其下面是一个协议无关层,它提供一种通用的方法来使用传输层协议。...
  • linux网络设备驱动之实质篇

    千次阅读 2012-04-10 18:13:41
    在讲解linux内核自身携带的网络设备驱动程序之前,先讲解一下网络设备模型,知道要做些什么。 对于网络设备驱动,主要目的就是接受数据和发送数据。而人们把网络设备的体系结构就分为上面四层,就是为了更好...
  • Linux kernel中网络设备的管理

    千次阅读 2013-07-21 22:44:36
    kernel中使用net_device结构来描述网络设备,这个结构是网络驱动及接口层中最重要的结构。该结构不仅描述了接口方面的信息,还包括硬件信息,致使该结构很大很复杂。通过这个结构,内核在底层的网络驱动和网络层之间...
  • Linux网络协议栈——网络设备

    千次阅读 2011-01-05 19:56:00
    网络设备(network device)是内核对网络适配器(硬件)的抽象与封装,并为各个协议实例提供统一的接口,它是硬件与内核的接口,它有两个特征: (1) 作为基于硬件的网络适配器与基于软件的协议之间的接口;...
  • linux网络报文接收发送浅析

    千次阅读 2013-05-11 19:55:45
    对于linux内核来说,网络报文由网络设备来进行接收。设备驱动程序从网络设备中读取报文,通过内核提供的网络接口函数,将报文传递到内核中的网络协议栈。报文经过协议栈的处理,或转发、或丢弃、或被传送给某个进程...
  •  内核的初始化过程过程中,与网络相关的工作如下所示:  内核引导时执行start_kernel,start_kernel结束之前会调用rest_init,rest_init初始化内核线程init(在Linux3-12中为kernel_init)。 asmlinkage ...
  • Linux的网络系统主要是基于BSD Unix 的socket机制, 访问网络设备的驱动程序不需要使用设备节点。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据的传递。系统内部支持对发送数据和接收数据的缓存,...
  • 其范围从协议无关层(例如通用 socket 层接口或设备层)到各种具体的网络协议实现。 协议简介 对于网络的理论介绍一般都采用 OSI(Open Systems Interconnection)模型,但是Linux网络栈...
  • Linux网络设备驱动(一) _驱动模型

    千次阅读 2017-03-28 09:51:47
    Linux素来以其强大的网络功能著名,同时, 网络设备也作为三大设备之一, 成为Linux驱动学习中必不可少的设备类型, 此外, 由于历史原因, Linux并没有强制对网络设备贯彻其"一切皆文件"的思想, 网络设备不以/dev下的设备...
  • linux内核对网卡驱动多队列的支持

    万次阅读 2010-02-09 21:11:00
    众所周知,linux的网卡由结构体net_device表示,一 个该结构体对应一个可以调度的数据包发送队列,注意,这里不谈数据包接收,数据包的实体在内核中以结构体sk_buff表示,这样的话,上述文字就可以用 以下图示来表示...
  • linux 网络设备驱动之alloc_etherdev

    千次阅读 2018-11-14 19:39:55
    最近在看网络驱动时,发现这个函数:   struct net_device *netdev; netdev = alloc_etherdev(sizeof(synopGMACPciNetworkAdapter)); 顺着这个函数进行追踪: #define alloc_etherdev(sizeof_priv) alloc_...
  • 首先,Linux网络设备驱动从上到下分为四层: 1、网络协议接口层:向网络协议(ARP&IP)提供统一的数据包发送接口,通过dev_queue_xmit()函数发送数据,并通过netif_rx()函数接收数据。这一层的存在使得上层协议独立于...
  • 说明: 本文主要是分析kernel source code,来贯穿...当上层的APP试图建立一个TCP的链接,或者发送一个封包的时候,在kernel的协议栈部分,在TCP/UDP层会组成一个网络的封包,然后通过IP进行路由选择以及iptables的hokk
  • 一、网络设备的初始化网络设备初始化就是调用具有__init 前缀的net_dev_init函数完成的,网络设备初始化包含两个部分(在linux内核2.4办源代码分析大全一书的第550页有详细说明),就是:在系统初始化期间对系统已知...
  • Linux TC工具之TBF队列

    2020-04-15 14:03:55
    Linux中,流量控制都是通过TC(traffic control)这个工具来完成的。TC通过三种对象控制流量的处理,它们是:qdisc(排队规则)、class(类别)和filter(过滤器)。通常,要对网卡进行流量控制的配置,需要进行如下的步骤...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 43,135
精华内容 17,254
关键字:

linux发送队列网络设备

linux 订阅