• linux回环网卡驱动设计


    回环网卡驱动
    1.回环网卡和普通网卡的区别是他是虚拟的不是实际的物理网卡,它相当于把普通网卡的发送端和接收端短接在一起。

    2.在内核源代码里的回环网卡程序(drivers/net/loopback.c)不是以一个模块的形式给出,但是他的初始化(loopback_net_init)和退出函数(loopback_dev_free)会被内核的其他部分调用到。

    3.参照网卡初始化的流程图进行设计驱动程序,其中分配net_device结构不能用alloc_etherdev函数,因为该函数是分配以太网卡的结构体的,要用alloc_netdev函数来给回环网卡分配结构体。参考内核源代码别人如何用使用这个函数alloc_netdev(0, "lo", loopback_setup);第一个0表示net_device这个结构体的私有成员的大小,一般选择0,第二个表示网卡名字,ifconfig显示的名称,第三个就是具体的网卡结构体的初始化函数指针。
    struct net_device *dev;
    dev = alloc_netdev(0, "lo", loopback_setup);
    最终会调用到loopback_setup函数。(至于在将函数指针作为参数的时候如何传递形参,就要复习C语言了)
    4.回环网卡不需要初始化硬件。所以直接注册回环网卡的结构体到内核(第三步)。最后指定回环网卡注册到网络子系统。
    net->loopback_dev = dev;这一步很关键。

    5.具体的初始化(loopback_setup)(第二步)
    (1)基地址,MAC地址以及中断号都用不着,主要是netdev_ops这个结构体,他包含了这个网卡支持的操作。

    (2)表示回环网卡支持的最大的接收数据的包的大小,除了正式数据,还有相关网络协议的头部分。
    dev->mtu          = (16 * 1024) + 20 + 20 + 12;有效数据一般定义为16KB。

    (3)加上表示回环网卡专有的标志。
    dev->flags          = IFF_LOOPBACK;

    (4)加上构造报头的结构体指针,这个结构体指针指向的结构体成员是众多构造以太网报头的函数指针。
    dev->header_ops          = &eth_header_ops;
    理想查找可看到
    extern const struct header_ops eth_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);
    #define HAVE_HEADER_CACHE
         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);
    };
    6.数据发送

    static int loopback_net_xmit(struct sk_buff *skb,struct net_device *dev)
    {
    skb->protocol = eth_type_trans(skb,dev);

    packets++;
    bytes += skb->len;

    netif_rx(skb);

    return 0;
    }


    }
    (1)第一个参数是协议栈传送给回环网卡的包数据,第二个参数是回环网卡的结构体。

    (2)停止发送队列
    通知上层暂停送数据,好让txd发送已送达的数据,但是不涉及硬件,所以在回环网卡可忽略。相应的,将数据写入寄存器和唤醒再次发送以及释放队列就可忽略。

    (3)信息统计,表明上层送下来的包的协议
    skb->protocol = eth_type_trans(skb, dev);

    (4)统计发送过来的数据大小以及包的个数。

    bytes += skb->len;

    netif_rx(skb);


    7.由于从协议栈来的数据包(skb)存放到txd,而且txd不需要往外发送,txd和rxd“连”在一起,所以直接在发送部分调用普通网卡的接收部分的netif_rx(skb)函数,所以发送的同时就完成了接收。同时我们更清楚地看到在发送的时候不能释放skb,否则没有可接收的数据。

    8.实现获取网卡状态的函数

    static struct net_device_stats *loopback_get_stats(struct net_device *dev)
    {
    struct net_device_stats *stats = &dev->stats;

    stats->rx_packets = packets;
    stats->tx_packets = packets;
    stats->rx_bytes = bytes;
    stats->tx_bytes = bytes;

    return stats;
    }


    从这个结构体可以获取网卡状态信息
    /* The main device statistics structure */
    struct rtnl_link_stats64 {
         __u64     rx_packets;          /* total packets received     */
         __u64     tx_packets;          /* total packets transmitted     */
         __u64     rx_bytes;          /* total bytes received      */
         __u64     tx_bytes;          /* total bytes transmitted     */
         __u64     rx_errors;          /* bad packets received          */
         __u64     tx_errors;          /* packet transmit problems     */
         __u64     rx_dropped;          /* no space in linux buffers     */
         __u64     tx_dropped;          /* no space available in linux     */
         __u64     multicast;          /* multicast packets received     */
         __u64     collisions;

         /* detailed rx_errors: */
         __u64     rx_length_errors;
         __u64     rx_over_errors;          /* receiver ring buff overflow     */
         __u64     rx_crc_errors;          /* recved pkt with crc error     */
         __u64     rx_frame_errors;     /* recv'd frame alignment error */
         __u64     rx_fifo_errors;          /* recv'r fifo overrun          */
         __u64     rx_missed_errors;     /* receiver missed packet     */

         /* detailed tx_errors */
         __u64     tx_aborted_errors;
         __u64     tx_carrier_errors;
         __u64     tx_fifo_errors;
         __u64     tx_heartbeat_errors;
         __u64     tx_window_errors;

         /* for cslip etc */
         __u64     rx_compressed;
         __u64     tx_compressed;
    };
    主要是这四个成员
      stats->rx_packets = packets;
         stats->tx_packets = packets;
         stats->rx_bytes   = bytes;
         stats->tx_bytes   = bytes;
    。实际上自己可以重写这个获取状态的函数,因为struct net_device *dev有一个成员就是struct net_device_stats     stats;所以可以在重写的时候定义一个struct net_device_stats     stats指向形参传递进来的回环网卡结构体的stats成员,同样只需要注意stats成员的上述四个成员即可。

    9.在退出该驱动的时候就是取消注册结构体。达到注销网卡的目的。

    static __net_exit void loopback_net_exit(struct net *net)
    {
    struct net_device *dev = net->loopback_dev;

    unregister_netdev(dev);
    }

    下面为范例代码

    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/types.h>
    #include <linux/errno.h>
    #include <linux/init.h>
    #include <linux/netdevice.h>
    #include <linux/etherdevice.h>
    #include <linux/skbuff.h>
    #include <linux/if_ether.h>    /* For the statistics structure. */
    
    
    unsigned long bytes = 0;
    unsigned long packets = 0;
    
    
    static int loopback_xmit(struct sk_buff *skb, struct net_device *dev)
    {
    
        skb->protocol = eth_type_trans(skb,dev);   //标明数据包的协议---以太网协议
    
    
        bytes += skb->len;  //发送包的长度
        packets++;  //包的数目
    
        netif_rx(skb);
    
        return 0;
    }
    
    static struct net_device_stats *loopback_get_stats(struct net_device *dev)
    {
        struct net_device_stats *stats = &dev->stats;
        
        stats->rx_packets = packets;
        stats->tx_packets = packets;
        stats->rx_bytes = bytes;
        stats->tx_bytes = bytes;
        return stats;
    }
    
    static const struct net_device_ops loopback_ops = {
        .ndo_start_xmit= loopback_xmit,
        .ndo_get_stats = loopback_get_stats,  //获取网卡状态信息
    };
    
    /*
     * The loopback device is special. There is only one instance
     * per network namespace.
     */
    static void loopback_setup(struct net_device *dev)
    {
        dev->mtu        = (16 * 1024) + 20 + 20 + 12; //网卡能接收的最大数据包的大小 tcp头 20 以太网头 20 ip头 12
        dev->flags        = IFF_LOOPBACK;   //回环网卡标志位
        dev->header_ops        = &eth_header_ops;  //数据头的构造函数(以太网头构造函数) 直接使用内核自带的函数 内核自动调用该函数
        dev->netdev_ops        = &loopback_ops;  //网卡操作函数集
    
    }
    
    /* Setup and register the loopback device. */
    static __net_init int loopback_net_init(struct net *net)
    {
        struct net_device *dev;
        int err;
        err = -ENOMEM;
        dev = alloc_netdev(0, "lo", loopback_setup);  //分配net_device结构-alloc_etherdev(以太网)  setup初始化netdev
        if (!dev)
            goto out;
    
        err = register_netdev(dev);
        if (err)
            goto out_free_netdev;
    
        net->loopback_dev = dev;  //将分配好的dev告诉网络(回环网卡特有)
        return 0;
    
    
    out_free_netdev:
        free_netdev(dev);
    out:
        if (net == &init_net)
            panic("loopback: Failed to register netdevice: %d
    ", err);
        return err;
    }
    
    static __net_exit void loopback_net_exit(struct net *net)
    {
        struct net_device *dev = net->loopback_dev;
    
        unregister_netdev(dev);
    }
    
    /* Registered in net/core/dev.c */
    struct pernet_operations __net_initdata loopback_net_ops = {
           .init = loopback_net_init,
           .exit = loopback_net_exit,
    };
  • 相关阅读:
    java常用设计模式链接
    常用设计模式汇总
    转:面试的角度诠释Java工程师(二)
    转:面试的角度诠释Java工程师(一)
    神经网络与深度学习_邱锡鹏——无监督学习和信息论
    智能推荐算法在直播场景中的应用
    经纬度地点之间距离测算
    <3>Transformer 到bert 模型
    <4>从bert到XLnet
    <2>自然语言难在哪? ----刘知远教授
  • 原文地址:https://www.cnblogs.com/chd-zhangbo/p/5159949.html
Copyright © 2020-2023  润新知