• 网络驱动移植之例解netdev_priv函数


        开发平台:Ubuntu 11.04

        编译器:gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4)

        内核源码:Linux-2.6.38.8.tar.bz2

        1、如何分配struct net_device结构体以及私有数据

        下面将通过实例来讲解Linux内核是如何通过alloc_netdev_mqs函数分配struct net_device结构体以及私有数据的(因为理解了这一点,就能完全理解netdev_priv函数的实现)。

        首先,编写一个模块,代码如下: 

    [cpp] view plain copy
     
    1. /* tanglinux.c */  
    2. #include <linux/module.h>  
    3. #include <linux/types.h>  
    4. #include <linux/miscdevice.h>  
    5. #include <linux/fs.h>  
    6. #include <linux/netdevice.h>  
    7. #include <linux/etherdevice.h>  
    8. #include <linux/kernel.h>  
    9. #include <linux/ioctl.h>  
    10.   
    11. #define TANGLINUX _IO('T', 1)  
    12.   
    13. struct net_local {  
    14.     int count;  
    15.     char ch;  
    16. };  
    17.   
    18. static int tanglinux_open(struct inode *inode, struct file *file)  
    19. {  
    20.     return nonseekable_open(inode, file);  
    21. }  
    22.   
    23. static long tanglinux_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  
    24. {  
    25.     struct net_device *dev;  
    26.     size_t alloc_size;  
    27.     size_t sizeof_priv = sizeof(struct net_local);  
    28.     struct net_device *p;  
    29.       
    30.     switch (cmd) {  
    31.     case TANGLINUX:  
    32.         alloc_size = sizeof(struct net_device);  
    33.         printk("first: alloc_size = %d ", alloc_size);  
    34.   
    35.         alloc_size += 1; //为验证ALIGN的作用,人为制造net_device结构体的大小不是32位对齐  
    36.   
    37.         if (sizeof_priv) {  
    38.         /* ensure 32-byte alignment of private area */  
    39.         alloc_size = ALIGN(alloc_size, NETDEV_ALIGN); //#define NETDEV_ALIGN    32  
    40.         printk("second: alloc_size = %d ", alloc_size);  
    41.   
    42.         alloc_size += sizeof_priv;  
    43.         printk("third: alloc_size = %d ", alloc_size);  
    44.         }  
    45.         /* ensure 32-byte alignment of whole construct */  
    46.         alloc_size += NETDEV_ALIGN - 1;  
    47.         printk("fourth: alloc_size = %d ", alloc_size);  
    48.       
    49.         p = kzalloc(alloc_size, GFP_KERNEL);  
    50.         if (!p) {  
    51.         printk(KERN_ERR "alloc_netdev: Unable to allocate device. ");  
    52.         return -ENOMEM;  
    53.         }  
    54.         printk("p = %p ", p);  
    55.       
    56.         dev = PTR_ALIGN(p, NETDEV_ALIGN);  
    57.         printk("dev = %p ", dev);  
    58.   
    59.         dev->padded = (char *)dev - (char *)p;  
    60.       
    61.         printk("dev->padded = %d ", dev->padded);  
    62.   
    63.         kfree(p);  
    64.   
    65.         return 0;  
    66.     default:  
    67.         return -ENOTTY;  
    68.     }  
    69. }  
    70.   
    71. static int tanglinux_release(struct inode *inode, struct file *file)  
    72. {  
    73.     return 0;  
    74. }  
    75.   
    76. static const struct file_operations tanglinux_fops = {  
    77.     .owner      = THIS_MODULE,  
    78.     .unlocked_ioctl = tanglinux_ioctl,  
    79.     .open       = tanglinux_open,  
    80.     .release        = tanglinux_release,  
    81. };  
    82.   
    83. static struct miscdevice tanglinux_miscdev = {  
    84.     .minor  = WATCHDOG_MINOR,  
    85.     .name   = "tanglinux",  
    86.     .fops   = &tanglinux_fops,  
    87. };  
    88.   
    89. static int __init tanglinux_init(void)  
    90. {  
    91.     printk("tanglinux driver ");  
    92.   
    93.     return misc_register(&tanglinux_miscdev);  
    94. }  
    95.   
    96. static void __exit tanglinux_exit(void)  
    97. {  
    98.     misc_deregister(&tanglinux_miscdev);  
    99. }  
    100.   
    101. module_init(tanglinux_init);  
    102. module_exit(tanglinux_exit);  
    103.   
    104. MODULE_LICENSE("GPL");  

        然后,编译并加载此模块: 

    [cpp] view plain copy
     
    1. //获得Ubuntu 11.04正在运行的内核版本  
    2. $ cat /proc/version  
    3. Linux version 2.6.38-13-generic (buildd@roseapple) (gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4) ) #53-Ubuntu SMP Mon Nov 28 19:23:39 UTC 2011  
    4.   
    5. //根据上面获得的信息,在Makefile中指定Ubuntu 11.04的内核源码目录为/usr/src/linux-headers-2.6.38-13-generic/  
    6. # Makefile  
    7. KERN_DIR = /usr/src/linux-headers-2.6.38-13-generic/  
    8.   
    9. all:  
    10.     make -C $(KERN_DIR) M=`pwd` modules  
    11.   
    12. clean:  
    13.     make -C $(KERN_DIR) M=`pwd` modules clean  
    14.   
    15. obj-m += tanglinux.o  
    16.   
    17. //编译,并把编译好的模块tanglinux.ko加载到内核中  
    18. $ make  
    19. $ sudo insmod tanglinux.ko  

        最后,通过测试程序获得相关信息: 

    [cpp] view plain copy
     
    1. /* test.c */  
    2. #include <sys/types.h>  
    3. #include <sys/stat.h>  
    4. #include <stdio.h>  
    5. #include <fcntl.h>  
    6. #include <unistd.h>  
    7. #include <sys/ioctl.h>  
    8.   
    9. #define TANGLINUX _IO('T', 1)  
    10.   
    11. int main(void)  
    12. {  
    13.     int fd;  
    14.   
    15.     fd = open("/dev/tanglinux", O_RDWR);  
    16.     if (fd < 0)  
    17.     {  
    18.     printf("can't open /dev/tanglinux ");  
    19.     return -1;  
    20.     }  
    21.   
    22.     ioctl(fd, TANGLINUX);  
    23.       
    24.     return 0;  
    25. }  
    [cpp] view plain copy
     
    1. //编译、执行测试程序,然后通过dmesg命令获得模块输出的信息  
    2. $ make test  
    3. $ sudo ./test  
    4. $ dmesg | tail -7  
    5. [19853.353282] first: alloc_size = 1088  
    6. [19853.353296] second: alloc_size = 1120  
    7. [19853.353306] third: alloc_size = 1128  
    8. [19853.353316] fourth: alloc_size = 1159  
    9. [19853.353348] p = cddf6000  
    10. [19853.353358] dev = cddf6000  
    11. [19853.353369] dev->padded = 0  

        根据Ubuntu 11.04(基于X86硬件平台)中的配置,struct net_device结构体的大小为1088字节,刚好32位对齐,为了验证对齐函数ALIGN的作用,在例子中故意把struct net_device结构体的大小增加了1,所以第二次输出的alloc_size大小为1120个字节,也就是在1089个字节的基础上为了对齐增加了31个字节。

        PTR_ALIGN函数的作用是为了使struct net_device *dev最终得到的内存地址也是32位对齐的。

        上面所讨论的问题都可以通过下面的图示体现出来:

     

        2、如何通过netdev_priv访问到其私有数据

        netdev_priv函数的源代码如下: 

    [cpp] view plain copy
     
    1. static inline void *netdev_priv(const struct net_device *dev)  
    2. {  
    3.     return (char *)dev + ALIGN(sizeof(struct net_device), NETDEV_ALIGN);  
    4. }  
     

        即通过struct net_device *dev首地址加对齐后的偏移量就得到了私有数据的首地址,如上图。

  • 相关阅读:
    通过设置iis在局域网中访问网页
    .NetCore集成Dapr踩坑经历
    Api跨域设置
    Win10安装.net2.0/3.0
    C# MVC 全局错误Application_Error中处理(包括Ajax请求)
    使用SignalR从服务端主动推送警报日志到各种终端(桌面、移动、网页)
    C#设计模式学习笔记:(4)建造者模式
    .NET CORE(C#) WPF简单菜单MVVM绑定
    值类型与引用类型的区别
    [博客美化]给你博客添加一个萌萌的看板娘吧
  • 原文地址:https://www.cnblogs.com/Ph-one/p/6422746.html
Copyright © 2020-2023  润新知