加载中…
正文 字体大小:

netdev_priv

(2013-01-07 10:17:41)
标签:

数据结构

也就是

末端

文件

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函数的实现)。

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

  1.   
  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\n"alloc_size);  
  34.   
  35.         alloc_size += 1; //为验证ALIGN的作用,人为制造net_device结构体的大小不是32位对齐  
  36.   
  37.         if (sizeof_priv)  
  38.           
  39.         alloc_size ALIGN(alloc_size, NETDEV_ALIGN); //#define NETDEV_ALIGN    32  
  40.         printk("second: alloc_size %d\n"alloc_size);  
  41.   
  42.         alloc_size += sizeof_priv;  
  43.         printk("third: alloc_size %d\n"alloc_size);  
  44.          
  45.           
  46.         alloc_size += NETDEV_ALIGN 1;  
  47.         printk("fourth: alloc_size %d\n"alloc_size);  
  48.       
  49.         kzalloc(alloc_size, GFP_KERNEL);  
  50.         if (!p)  
  51.         printk(KERN_ERR "alloc_netdev: Unable to allocate device.\n");  
  52.         return -ENOMEM;  
  53.          
  54.         printk("p %p\n"p);  
  55.       
  56.         dev PTR_ALIGN(p, NETDEV_ALIGN);  
  57.         printk("dev %p\n"dev);  
  58.   
  59.         dev->padded (char *)dev (char *)p;  
  60.       
  61.         printk("dev->padded %d\n"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\n");  
  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");  

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

  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  

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

  1.   
  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\n");  
  19.     return -1;  
  20.      
  21.   
  22.     ioctl(fd, TANGLINUX);  
  23.       
  24.     return 0;  
  25.  
  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] cddf6000  
  10. [19853.353358] dev cddf6000  
  11. [19853.353369] dev->padded  

    根据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函数的源代码如下: 

  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首地址加对齐后的偏移量就得到了私有数据的首地址,如上图。 


1. include/linux/netdevice.h
#define NETDEV_ALIGN        32
#define NETDEV_ALIGN_CONST  (NETDEV_ALIGN - 1)
static inline void *netdev_priv(struct net_device *dev)
{
    return (char *)dev + ((sizeof(struct net_device)
                    + NETDEV_ALIGN_CONST)
                & ~NETDEV_ALIGN_CONST);
}
2. net/core/dev.c
struct net_device *alloc_netdev(int sizeof_priv, const char *name,
        void (*setup)(struct net_device *))
{
    ......
   
    alloc_size = (sizeof(*dev) + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST;
    alloc_size += sizeof_priv + NETDEV_ALIGN_CONST;
    p = kzalloc(alloc_size, GFP_KERNEL);    // 对分配的内核内存清0
    ......
    if (sizeof_priv)
        dev->priv = netdev_priv(dev);
    ......
}
linux/driver/net网卡驱动程序中充满了类似这样的代码:
sturct nic *nic = netdev_priv(dev);
从文件2中可以看出net_device和网卡私有数据结构是一起分配的,要想获得网卡私有数据结构的地址,文件1中的函数netdev_priv直接返回了net_device结构末端地址,也就是网卡私有数据结构的起始地址。当然其中考虑了字节对齐的问题。至于为什么不直接返回:
sturct nic *nic = dev->priv;
《Linux Devcie Drivers》中说是为了性能和灵活性方面的考虑。


0

阅读 评论 收藏 禁止转载 喜欢 打印举报
  • 评论加载中,请稍候...
发评论

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

      

    新浪BLOG意见反馈留言板 电话:4006900000 提示音后按1键(按当地市话标准计费) 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有