Linux 字符设备驱动结构(二)—— 自动创建设备节点

上一篇我们介绍到创建设备文件的方法,利用cat /proc/devices查看申请到的设备名,设备号。

第一种是使用mknod手工创建:mknod filename type major minor

第二种是自动创建设备节点:利用udev(mdev)来实现设备文件的自动创建,首先应保证支持udev(mdev),由busybox配置。

      具体udev相关知识这里不详细阐述,可以移步Linux 文件系统与设备文件系统 —— udev 设备文件系统,这里主要讲使用方法。

     

    在驱动用加入对udev 的支持主要做的就是:在驱动初始化的代码里调用class_create(...)为该设备创建一个class,再为每个设备调用device_create(...)创建对应的设备

    内核中定义的struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用 device_create(…)函数来在/dev目录下创建相应的设备节点。

     这样,加载模块的时候,用户空间中的udev会自动响应 device_create()函数,去/sysfs下寻找对应的类从而创建设备节点。


下面是两个函数的解析:

1、class_create(...) 函数

功能:创建一个类;

下面是具体定义:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #define class_create(owner, name)       \  
  2. ({                      \  
  3.     static struct lock_class_key __key; \  
  4.     __class_create(owner, name, &__key);    \  
  5. })  

owner:THIS_MODULE
name  : 名字

__class_create(owner, name, &__key)源代码如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. struct class *__class_create(struct module *owner, const char *name,  
  2.                  struct lock_class_key *key)  
  3. {  
  4.     struct class *cls;  
  5.     int retval;  
  6.   
  7.     cls = kzalloc(sizeof(*cls), GFP_KERNEL);  
  8.     if (!cls) {  
  9.         retval = -ENOMEM;  
  10.         goto error;  
  11.     }  
  12.   
  13.     cls->name = name;  
  14.     cls->owner = owner;  
  15.     cls->class_release = class_create_release;  
  16.   
  17.     retval = __class_register(cls, key);  
  18.     if (retval)  
  19.         goto error;  
  20.   
  21.     return cls;  
  22.   
  23. error:  
  24.     kfree(cls);  
  25.     return ERR_PTR(retval);  
  26. }  
  27. EXPORT_SYMBOL_GPL(__class_create);  

销毁函数:void class_destroy(struct class *cls)

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. void class_destroy(struct class *cls)  
  2. {  
  3.     if ((cls == NULL) || (IS_ERR(cls)))  
  4.         return;  
  5.   
  6.     class_unregister(cls);  
  7. }  


2、device_create(...) 函数

struct device *device_create(struct class *class, struct device *parent,
                 dev_t devt, void *drvdata, const char *fmt, ...)

功能:创建一个字符设备文件

参数:

      struct class *class  :类
      struct device *parent:NULL
     dev_t devt  :设备号
     void *drvdata  :null、
     const char *fmt  :名字

返回:

    struct device *

下面是源码解析:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. struct device *device_create(struct class *classstruct device *parent,  
  2.                  dev_t devt, void *drvdata, const char *fmt, ...)  
  3. {  
  4.     va_list vargs;  
  5.     struct device *dev;  
  6.   
  7.     va_start(vargs, fmt);  
  8.     dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);  
  9.     va_end(vargs);  
  10.     return dev;  
  11. }  

device_create_vargs(class, parent, devt, drvdata, fmt, vargs)解析如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. struct device *device_create_vargs(struct class *classstruct device *parent,  
  2.                    dev_t devt, void *drvdata, const char *fmt,  
  3.                    va_list args)  
  4. {  
  5.     return device_create_groups_vargs(class, parent, devt, drvdata, NULL,  
  6.                       fmt, args);  
  7. }  
现在就不继续往下跟了,大家可以继续往下跟;


下面是一个实例:

hello.c

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #include <linux/module.h>  
  2. #include <linux/fs.h>  
  3. #include <linux/cdev.h>  
  4. #include <linux/device.h>  
  5.   
  6. static int major = 250;  
  7. static int minor=0;  
  8. static dev_t devno;  
  9. static struct class *cls;  
  10. static struct device *test_device;  
  11.   
  12. static int hello_open (struct inode *inode, struct file *filep)  
  13. {  
  14.     printk("hello_open \n");  
  15.     return 0;  
  16. }  
  17. static struct file_operations hello_ops=  
  18. {  
  19.     .open = hello_open,  
  20. };  
  21.   
  22. static int hello_init(void)  
  23. {  
  24.     int ret;      
  25.     printk("hello_init \n");  
  26.   
  27.   
  28.     devno = MKDEV(major,minor);  
  29.     ret = register_chrdev(major,"hello",&hello_ops);  
  30.   
  31.     cls = class_create(THIS_MODULE, "myclass");  
  32.     if(IS_ERR(cls))  
  33.     {  
  34.         unregister_chrdev(major,"hello");  
  35.         return -EBUSY;  
  36.     }  
  37.     test_device = device_create(cls,NULL,devno,NULL,"hello");//mknod /dev/hello  
  38.     if(IS_ERR(test_device))  
  39.     {  
  40.         class_destroy(cls);  
  41.         unregister_chrdev(major,"hello");  
  42.         return -EBUSY;  
  43.     }     
  44.     return 0;  
  45. }  
  46. static void hello_exit(void)  
  47. {  
  48.     device_destroy(cls,devno);  
  49.     class_destroy(cls);   
  50.     unregister_chrdev(major,"hello");  
  51.     printk("hello_exit \n");  
  52. }  
  53. MODULE_LICENSE("GPL");  
  54. module_init(hello_init);  
  55. module_exit(hello_exit);  

test.c

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #include <sys/types.h>  
  2. #include <sys/stat.h>  
  3. #include <fcntl.h>  
  4. #include <stdio.h>  
  5.   
  6.   
  7. main()  
  8. {  
  9.     int fd;  
  10.   
  11.   
  12.     fd = open("/dev/hello",O_RDWR);  
  13.     if(fd<0)  
  14.     {  
  15.         perror("open fail \n");  
  16.         return ;  
  17.     }  
  18.   
  19.   
  20.     close(fd);  
  21. }  

makefile

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. ifneq  ($(KERNELRELEASE),)  
  2. obj-m:=hello.o  
  3. $(info "2nd")  
  4. else  
  5. KDIR := /lib/modules/$(shell uname -r)/build  
  6. PWD:=$(shell pwd)  
  7. all:  
  8.     $(info "1st")  
  9.     make -C $(KDIR) M=$(PWD) modules  
  10. clean:  
  11.     rm -f *.ko *.o *.symvers *.mod.c *.mod.o *.order  
  12. endif  

下面可以看几个class几个名字的对应关系:




上一篇我们介绍到创建设备文件的方法,利用cat /proc/devices查看申请到的设备名,设备号。

第一种是使用mknod手工创建:mknod filename type major minor

第二种是自动创建设备节点:利用udev(mdev)来实现设备文件的自动创建,首先应保证支持udev(mdev),由busybox配置。

      具体udev相关知识这里不详细阐述,可以移步Linux 文件系统与设备文件系统 —— udev 设备文件系统,这里主要讲使用方法。

     

    在驱动用加入对udev 的支持主要做的就是:在驱动初始化的代码里调用class_create(...)为该设备创建一个class,再为每个设备调用device_create(...)创建对应的设备

    内核中定义的struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用 device_create(…)函数来在/dev目录下创建相应的设备节点。

     这样,加载模块的时候,用户空间中的udev会自动响应 device_create()函数,去/sysfs下寻找对应的类从而创建设备节点。


下面是两个函数的解析:

1、class_create(...) 函数

功能:创建一个类;

下面是具体定义:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #define class_create(owner, name)       \  
  2. ({                      \  
  3.     static struct lock_class_key __key; \  
  4.     __class_create(owner, name, &__key);    \  
  5. })  

owner:THIS_MODULE
name  : 名字

__class_create(owner, name, &__key)源代码如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. struct class *__class_create(struct module *owner, const char *name,  
  2.                  struct lock_class_key *key)  
  3. {  
  4.     struct class *cls;  
  5.     int retval;  
  6.   
  7.     cls = kzalloc(sizeof(*cls), GFP_KERNEL);  
  8.     if (!cls) {  
  9.         retval = -ENOMEM;  
  10.         goto error;  
  11.     }  
  12.   
  13.     cls->name = name;  
  14.     cls->owner = owner;  
  15.     cls->class_release = class_create_release;  
  16.   
  17.     retval = __class_register(cls, key);  
  18.     if (retval)  
  19.         goto error;  
  20.   
  21.     return cls;  
  22.   
  23. error:  
  24.     kfree(cls);  
  25.     return ERR_PTR(retval);  
  26. }  
  27. EXPORT_SYMBOL_GPL(__class_create);  

销毁函数:void class_destroy(struct class *cls)

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. void class_destroy(struct class *cls)  
  2. {  
  3.     if ((cls == NULL) || (IS_ERR(cls)))  
  4.         return;  
  5.   
  6.     class_unregister(cls);  
  7. }  


2、device_create(...) 函数

struct device *device_create(struct class *class, struct device *parent,
                 dev_t devt, void *drvdata, const char *fmt, ...)

功能:创建一个字符设备文件

参数:

      struct class *class  :类
      struct device *parent:NULL
     dev_t devt  :设备号
     void *drvdata  :null、
     const char *fmt  :名字

返回:

    struct device *

下面是源码解析:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. struct device *device_create(struct class *classstruct device *parent,  
  2.                  dev_t devt, void *drvdata, const char *fmt, ...)  
  3. {  
  4.     va_list vargs;  
  5.     struct device *dev;  
  6.   
  7.     va_start(vargs, fmt);  
  8.     dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);  
  9.     va_end(vargs);  
  10.     return dev;  
  11. }  

device_create_vargs(class, parent, devt, drvdata, fmt, vargs)解析如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. struct device *device_create_vargs(struct class *classstruct device *parent,  
  2.                    dev_t devt, void *drvdata, const char *fmt,  
  3.                    va_list args)  
  4. {  
  5.     return device_create_groups_vargs(class, parent, devt, drvdata, NULL,  
  6.                       fmt, args);  
  7. }  
现在就不继续往下跟了,大家可以继续往下跟;


下面是一个实例:

hello.c

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #include <linux/module.h>  
  2. #include <linux/fs.h>  
  3. #include <linux/cdev.h>  
  4. #include <linux/device.h>  
  5.   
  6. static int major = 250;  
  7. static int minor=0;  
  8. static dev_t devno;  
  9. static struct class *cls;  
  10. static struct device *test_device;  
  11.   
  12. static int hello_open (struct inode *inode, struct file *filep)  
  13. {  
  14.     printk("hello_open \n");  
  15.     return 0;  
  16. }  
  17. static struct file_operations hello_ops=  
  18. {  
  19.     .open = hello_open,  
  20. };  
  21.   
  22. static int hello_init(void)  
  23. {  
  24.     int ret;      
  25.     printk("hello_init \n");  
  26.   
  27.   
  28.     devno = MKDEV(major,minor);  
  29.     ret = register_chrdev(major,"hello",&hello_ops);  
  30.   
  31.     cls = class_create(THIS_MODULE, "myclass");  
  32.     if(IS_ERR(cls))  
  33.     {  
  34.         unregister_chrdev(major,"hello");  
  35.         return -EBUSY;  
  36.     }  
  37.     test_device = device_create(cls,NULL,devno,NULL,"hello");//mknod /dev/hello  
  38.     if(IS_ERR(test_device))  
  39.     {  
  40.         class_destroy(cls);  
  41.         unregister_chrdev(major,"hello");  
  42.         return -EBUSY;  
  43.     }     
  44.     return 0;  
  45. }  
  46. static void hello_exit(void)  
  47. {  
  48.     device_destroy(cls,devno);  
  49.     class_destroy(cls);   
  50.     unregister_chrdev(major,"hello");  
  51.     printk("hello_exit \n");  
  52. }  
  53. MODULE_LICENSE("GPL");  
  54. module_init(hello_init);  
  55. module_exit(hello_exit);  

test.c

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #include <sys/types.h>  
  2. #include <sys/stat.h>  
  3. #include <fcntl.h>  
  4. #include <stdio.h>  
  5.   
  6.   
  7. main()  
  8. {  
  9.     int fd;  
  10.   
  11.   
  12.     fd = open("/dev/hello",O_RDWR);  
  13.     if(fd<0)  
  14.     {  
  15.         perror("open fail \n");  
  16.         return ;  
  17.     }  
  18.   
  19.   
  20.     close(fd);  
  21. }  

makefile

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. ifneq  ($(KERNELRELEASE),)  
  2. obj-m:=hello.o  
  3. $(info "2nd")  
  4. else  
  5. KDIR := /lib/modules/$(shell uname -r)/build  
  6. PWD:=$(shell pwd)  
  7. all:  
  8.     $(info "1st")  
  9.     make -C $(KDIR) M=$(PWD) modules  
  10. clean:  
  11.     rm -f *.ko *.o *.symvers *.mod.c *.mod.o *.order  
  12. endif  

下面可以看几个class几个名字的对应关系:




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402121.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 字符设备驱动结构(一)—— cdev 结构体、设备号相关知识解析

一、字符设备基础知识 1、设备驱动分类 linux系统将设备分为3类&#xff1a;字符设备、块设备、网络设备。使用驱动程序&#xff1a; 字符设备&#xff1a;是指只能一个字节一个字节读写的设备&#xff0c;不能随机读取设备内存中的某一数据&#xff0c;读取数据需要按照先后数…

Linux 驱动开发之内核模块开发(四)—— 符号表的导出

Linux内核头文件提供了一个方便的方法用来管理符号的对模块外部的可见性,因此减少了命名空间的污染(命名空间的名称可能会与内核其他地方定义的名称冲突),并且适当信息隐藏。 如果你的模块需要输出符号给其他模块使用,应当使用下面的宏定义: EXPORT_SYMBOL(name); EXPORT_SYMBO…

Linux 驱动开发之内核模块开发 (三)—— 模块传参

一、module_param() 定义 通常在用户态下编程&#xff0c;即应用程序&#xff0c;可以通过main()的来传递命令行参数&#xff0c;而编写一个内核模块&#xff0c;则通过module_param() 来传参。 module_param()宏是Linux 2.6内核中新增的&#xff0c;该宏被定义在include/linux…

Exynos4412 文件系统制作(二)—— 文件系统简介

一、Linux磁盘分区和目录 Linux发行版本之间的差别很少&#xff0c;差别主要表现在系统管理的特色工具以及软件包管理方式的不同。目录结构基本上都是一样的。 Windows的文件结构是多个并列的树状结构&#xff0c;最顶部的是不同的磁盘&#xff08;分区&#xff09;&#xff0c…

MM引擎新应用——爱车加油记

基于MM应用引擎开发的EXTJS应用&#xff0c;车主每次加完汽油后&#xff0c;记录加油时的里程数以及加油金额和汽油价格&#xff0c;就可计算出上次加油后行驶的里程数、上次加油的平均油耗。点击刷新按钮&#xff0c;即可计算有记录以来的行驶公里数和再次期间加油金额和平均油…

### 阅读之痕-2013/11

2019独角兽企业重金招聘Python工程师标准>>> 阅读之痕-2013/11 Andy erpingwugmail.com 2013/11/01-2013/11/30 2013/11/01-2013/11/30 The story of rocksdb - Embedded key-value store for Flash and RAM http://rocksdb.org/intro.pdf High Performance Network…

Exynos4412 内核移植(六)—— 设备树解析

一、描述ARM Device Tree起源于OpenFirmware (OF)&#xff0c;在过去的Linux中&#xff0c;arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码&#xff0c;相当多数的代码只是在描述板级细节&#xff0c;而这些板级细节对于内核来讲&#xff0c;不过是垃圾&#xff…

Github Page创建个人主页以及绑定域名

2019独角兽企业重金招聘Python工程师标准>>> 在github中 我们可以通过github page创建个人主页 以及绑定域名 据说有300m空间 以及无限流量 不过只能支持静态html 以及一些脚本语言 顺便吐槽一下 阿里云最低配那个云空间服务器 512m内存 启动web服务器后 mys…

Exynos4412 内核移植(五)—— 驱动的移植

以移植自己制作的驱动&#xff0c;学习内核移植中的驱动移植&#xff0c;及 驱动程序的动态编译和静态编译 硬件环境&#xff1a; Linux 内核版本&#xff1a;Linux 3.14 主机&#xff1a;Ubuntu 12.04发行版 目标机&#xff1a;FS4412平台 交叉编译工具&#xff1a;arm-none-l…

FTP文件共传输服务

FTP文件共传输服务一&#xff0c;vsftpd服务基础&#xff08;1&#xff09;&#xff0c;FTP服务概述FTP&#xff08;File Transfer Protocol&#xff0c;文件传输协议&#xff09;是典型的C/S结构的应用层协议&#xff0c;需要由服务端软件、客户端软件共同实现文件传达输功能…

Exynos4412 内核移植(四)—— MMU 相关知识解析

一、MMU的产生 许多年以前&#xff0c;当人们还在使用DOS或是更古老的操作系统的时候&#xff0c;计算机的内存还非常小&#xff0c;一般都是以K为单位进行计算&#xff0c;相应的&#xff0c;当时的程序规模也不大&#xff0c;所以内存容量虽然小&#xff0c;但还是可以容纳当…

Mysql limit 子查询

为什么80%的码农都做不了架构师&#xff1f;>>> &#xff08;1&#xff09;mysql limit 不支持子查询像下面这条语句无法执行 SELECT * FROM b_c_s1 where CT_ID IN (SELECT DISTINCT CT_ID FROM b_c_s1 LIMIT 0,2); &#xff08;2&#xff09;应该在只查询外面再…

Exynos4412 内核移植(二)—— 内核编译过程分析

内核的编译同样是从Makefile 来分析&#xff1a; 一、内核源码结构 Linux内核文件数目近2万&#xff0c;出去其他架构CPU的相关文件&#xff0c;他们分别位于顶层目录下的17个子目录&#xff0c;各个目录功能独立&#xff0c;下面是常用目录&#xff1a; arch:体系结构相关代码…

深入理解Java:注解(Annotation)

2019独角兽企业重金招聘Python工程师标准>>> 一、概述 1.什么是注解&#xff08;Annotation&#xff09; Annotation&#xff08;注解&#xff09;就是Java提供了一种元程序中的元素关联任何信息和着任何元数据&#xff08;metadata&#xff09;的途径和方法。Ann…

Exynos4412 内核移植(三)—— 内核启动过程分析

内核启动所用函数如下&#xff1a; 与移植U-Boot 的过程相似&#xff0c;在移植Linux 之前&#xff0c;先了解它的启动过程。Linux 的过程可以分为两部分&#xff1a;架构/开发板相关的引导过程、后续的通用启动过程。对于uImage、zImage ,它们首先进行自解压得到vmlinux &…

开源自己用python封装的一个Windows GUI(UI Automation)自动化工具,支持MFC,Windows Forms,WPF,Metro,Qt...

首先&#xff0c;大家可以看下这个链接 Windows GUI自动化测试技术的比较和展望 。 这篇文章介绍了Windows中GUI自动化的三种技术&#xff1a;Windows API, MSAA - Microsoft Active Accessibility, UIAutomation 用脚本语言AutoIT实现自动化就是第一种技术Windows API, 查找窗…

Exynos4412 Uboot 移植(六)—— 相关知识补充

Uboot版本&#xff1a;u-boot-2013.01 一、gd结构体的定义与使用 gd_t 和 bd_t 是u-boot中两个重要的数据结构&#xff0c;在初始化操作很多都要靠这两个数据结构来保存或传递。 gd_t 定义在/u-boot-2013.01/arch/arm/include/asm/global_data.h bd_t 定义在 ./include/asm-ar…

Exynos4412 Uboot 移植(五)—— Uboot 移植过程

Uboot 版本&#xff1a;u-boot-2013.01 开发板&#xff1a;FS_4412 平台&#xff08;Exynos4412,可以根据自己的板子修改&#xff0c;只要是4412的过程都是一样的&#xff09; 一、建立自己的平台 1、下载源码 我们可以在下面这个网站上下载最新的和以前任一版本的uboot ftp://…

Nagios 安装及常见错误

一、实验环境监控服务器&#xff08;nagios服务器--192.168.1.100&#xff09;CentOS5.4 nagios-3.2.1 nagios-plugins-1.4.14 nrpe-2.12被监控客户端&#xff08;linux客户端--192.168.1.200&#xff09;CentOS5.4 nagios-plugins-1.4.14 nrpe-2.12二、nrpe插件1、nrpe插…