• 设备模型(自动mknod)


    一、起源

    仅devfs,导致开发不方便以及一些功能难以支持:

    1. 热插拔(U盘……)

    2. 不支持一些针对所有设备的统一操作(如电源管理

    3. 不能自动mknod

    4. 用户查看不了设备信息

    5. 设备信息硬编码,导致驱动代码通用性差,即没有分离设备和驱动

    二、新方案

    uevent机制:sysfs + uevent + udevd(上层app)

    2.1 sysfs: 一种用内存模拟的文件系统,系统启动时mount到/sys目录

    sysfs用途:(类似于windows的设备管理器)

    1. 建立系统中总线、驱动、设备三者之间的桥梁
    2. 向用户空间展示内核中各种设备的拓扑图
    3. 提供给用户空间对设备获取信息和操作的接口,部分取代ioctl功能
    sysfs在内核中的组成要素在用户空间/sys下的显示
    内核对象(kobject)目录
    对象属性(attribute)文件
    对象关系(relationship)链接(Symbolic Link)

    四个基本结构

    类型所包含的内容内核数据结构对应/sys项
    设备(Devices)设备是此模型中最基本的类型,以设备本身的连接按层次组织struct device/sys/devices/?/?/…/
    驱动(Drivers)在一个系统中安装多个相同设备,只需要一份驱动程序的支持struct device_driver/sys/bus/pci/drivers/?/
    总线(Bus)在整个总线级别对此总线上连接的所有设备进行管理struct bus_type/sys/bus/?/
    类别(Classes)这是按照功能进行分类组织的设备层次树;如 USB 接口和 PS/2 接口的鼠标都是输入设备,都会出现在/sys/class/input/下struct class/sys/class/?/

    目录组织结构:

    /sys下的子目录所包含的内容
    /sys/devices这是内核对系统中所有设备的分层次表达模型,也是/sys文件系统管理设备的最重要的目录结构;
    /sys/dev这个目录下维护一个按字符设备和块设备的主次号码(major:minor)链接到真实的设备(/sys/devices下)的符号链接文件;
    /sys/bus这是内核设备按总线类型分层放置的目录结构, devices 中的所有设备都是连接于某种总线之下,在这里的每一种具体总线之下可以找到每一个具体设备的符号链接,它也是构成 Linux 统一设备模型的一部分;
    /sys/class这是按照设备功能分类的设备模型,如系统所有输入设备都会出现在/sys/class/input 之下,而不论它们是以何种总线连接到系统。它也是构成 Linux 统一设备模型的一部分;
    /sys/kernel这里是内核所有可调整参数的位置,目前只有 uevent_helper, kexec_loaded, mm, 和新式的slab 分配器等几项较新的设计在使用它,其它内核可调整参数仍然位于sysctl(/proc/sys/kernel) 接口中;
    /sys/module这里有系统中所有模块的信息,不论这些模块是以内联(inlined)方式编译到内核映像文件(vmlinuz)中还是编译为外部模块(ko文件),都可能会出现在/sys/module 中
    /sys/power这里是系统中电源选项,这个目录下有几个属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令让机器关机、重启等。

    2.2 uevent

    在这里插入图片描述

    三、代码中自动mknod

    struct class *class_create(struct module *owner, const char *name);
    /*
     * 功能:在/sys/class生成一个目录,目录名由name指定
     * 参数:
    	struct module *owner - THIS_MODULE
    	const char *name - 目录名
     * 返回值  成功:class指针   失败:NULL
    */
    /*
    辅助接口:可以定义一个struct class 的指针变量cls来接受返回值,然后通过IS_ERR(cls)判断是否失败;
    IS_ERR(cls);成功----------------->0
    IS_ERR(cls);失败----------------->非0
    PTR_ERR(cls);来获得失败的返回错误码;
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    void class_destroy(struct class *cls)
    /*
    * 功能:删除class_create生成目录
    * 参数:
     	struct class *cls - class指针
    * 返回值
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    struct device *device_create(struct class *class, struct device *parent,
    			     dev_t devt, void *drvdata, const char *fmt, ...)
    /*
     * 功能:在/sys/class目录下class_create生成目录再生成一个子目录与该设备相对应,发uevent让应用程序udevd创建设备文件
     * 参数:
     	struct class *class - class指针
     	struct device *parent - 父对象,一般NULL
     	dev_t devt - 设备号
     	void *drvdata - 驱动私有数据,一般NULL
     	const char *fmt - 字符串的格式
     	 ... - 不定参数
     * 返回值
     	成功:device指针
     	失败:NULL
     */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    void device_destroy(struct class *class, dev_t devt)
    /*
     * 功能:删除device_create生成目录
     * 参数:
     	struct class *class - class指针
     	dev_t devt - 设备号
     * 返回值
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    示例:

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define BUF_LEN 100
    
    int major = 11;					//主设备号
    int minor = 0;					//次设备号
    int char_num = 1;				//设备号数量
    
    struct mysecond_dev 
    {
    	struct cdev mydev;
    	char mydev_buf[BUF_LEN];
    	int curlen;
    	struct class *pcls;
    	struct device *pdev;
    };
    struct mysecond_dev gmydev;
    
    int mysecond_open (struct inode *pnode, struct file *pfile)//打开设备
    {
    	return 0;
    }
    
    int mysecond_close(struct inode *pnode, struct file *pfile)//关闭设备
    {
    	return 0;
    }
    
    struct file_operations myops = {
    	.owner = THIS_MODULE,
    	.open = mysecond_open,
    	.release = mysecond_close,
    };
    
    int __init mysecond_init(void) 
    {
    	int ret = 0;
    	dev_t devno = MKDEV(major, minor);
    
    	/* 手动申请设备号 */
    	ret = register_chrdev_region(devno, char_num, "mysecond");
    	if (ret) {
    		/* 动态申请设备号 */
    		ret = alloc_chrdev_region(&devno, minor, char_num, "mysecond");
    		if(ret){
    			printk("get devno failed\n");
    			return -1;
    		}
    		/*申请成功 更新设备号*/
    		major = MAJOR(devno);
    	}
    	
    	/* 给struct cdev对象指定操作函数集 */
    	cdev_init(&gmydev.mydev, &myops);
    
    	/* 将struct cdev对象添加到内核对应的数据结构中 */
    	gmydev.mydev.owner = THIS_MODULE;
    	cdev_add(&gmydev.mydev, devno, char_num);
    
    	/* 在/sys/class生成一个目录,目录名: mysecond */
    	gmydev.pcls = class_create(THIS_MODULE, "mysecond");
    	if(IS_ERR(gmydev.pcls)) {
    		printk("class_create failed\n");
    		class_destroy(gmydev.pcls);
    		cdev_del(&gmydev.mydev);
    		unregister_chrdev_region(devno, char_num);
    		return -1;
    	}
    
    	/* 在/sys/class目录下class_create生成目录再生成一个子目录与该设备相对应 : mysec */
    	gmydev.pdev = device_create(gmydev.pcls, NULL, devno, NULL, "mysec");
    	if(NULL == gmydev.pdev) {
    		printk("device_create failed\n");
    		class_destroy(gmydev.pcls);
    		cdev_del(&gmydev.mydev);
    		unregister_chrdev_region(devno, char_num);
    		return -1;
    	}
    	return 0;
    }
    
    void __exit mysecond_exit(void) 
    {
    
    	dev_t devno = MKDEV(major, minor);
    	printk("exit %d\n", devno);
    	
    	/* 删除device_create生成目录 */
    	device_destroy(gmydev.pcls, devno);
    
    	/* 删除class_create生成目录 */
    	class_destroy(gmydev.pcls);
    
    	/* 从内核中移除一个字符设备 */
    	cdev_del(&gmydev.mydev);
    
    	/* 回收设备号 */
    	unregister_chrdev_region(devno, char_num);
    
    }
    
    MODULE_LICENSE("GPL");
    module_init(mysecond_init);
    module_exit(mysecond_exit);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
  • 相关阅读:
    Git与Git常用命令速查
    代码随想录算法训练营 动态规划part06
    YOLOv5、YOLOv8改进:Decoupled Head解耦头
    antd pro form 数组套数组 form数组动态赋值 shouldUpdate 使用
    java毕业设计网站swing mysql实现的仓库商品管理系统[包运行成功]
    小程序中计算距离信息
    【Try to Hack】vulnhub narak
    [项目管理] IT软件交付项目流程阶段
    Java多线程-线程同步简述
    【云原生】内存数据库如何发挥内存优势
  • 原文地址:https://blog.csdn.net/qq_36091214/article/details/132730241