2025/12/24 19:29:03
网站建设
项目流程
个人账号密码网站建设,网站开发的在淘宝上是什么类目,网络舆情监测软件,关于茶叶的网站模板一 、简介字符设备与杂项设备的区别#xff1a;
#xff08;1#xff09;杂项设备的主设备号是固定为10#xff0c;学习字符类设备就需要自己或者系统来分配
#xff08;2#xff09;杂项设备可以自动生成设备节点#xff0c;字符设备需要自己生成设备节点二、申请设备号…一 、简介字符设备与杂项设备的区别1杂项设备的主设备号是固定为10学习字符类设备就需要自己或者系统来分配2杂项设备可以自动生成设备节点字符设备需要自己生成设备节点二、申请设备号注册字符类设备号的两个方法第一种静态分配一个设备号第二中2.1 静态分配设备号静态分配一个设备号需要知道哪些设备号被用了哪些设备号没有被用register_chrdev_region(dev_t, unsigned, const char *);头文件函数原型函数功能函数参数返回值头文件#inclduelinux/fs.h函数原型int register_chrdev_region(dev_t from, unsigned count, const char *name)函数功能静态分配一段连续的字符设备号供字符设备驱动程序使用函数参数param1 from:要分配的起始设备号包含主次设备号(类型为dev_t数据类型)param2 连续分配的设备号数量param3 设备名称出现在/proc/devices中返回值成功返回0失败返回错误码头文件#inclduelinux/types.h宏定义typedef u32 __kernel_dev_t;typedef __kernel_dev_t dev_t;作用dev_t 是用来保存设备号的是一个32位数高12为用来保存主设备号低20位用来保存次设备号LInux 提供了几个宏定义来操作设备号头文件#inclduelinux/kdev_t.h#define MINORBITS 20 //次设备号位数一共20位 #define MINORMASK ((1U MINORBITS) - 1) //次设备号掩码 #define MAJOR(dev) ((unsigned int) ((dev) MINORBITS))//在dev_t中获取主设备号 #define MINOR(dev) ((unsigned int) ((dev) MINORMASK)) //在dev_t中获取次设备号 #define MKDEV(ma,mi) (((ma) MINORBITS) | (mi)) //将获取的主设备号和次设备号组成一个dev_t类型第一个参数是住设备号第二个参数是次设备号2.2 动态分配设备号头文件#inclduelinux/fs.h函数原型int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *)函数功能动态分配设备号函数参数param1 dev_t * 保存生成的起始设备号param2 unsigned 请求的第一个次设备号通常为0param3 unsigned 连续申请的设备号个数param4 const char *name设备名称出现在/proc/devices中返回值成功返回0失败返回错误码使用动态分配会优先使用255~234之间的设备号2.3 注销设备号头文件#inclduelinux/fs.h函数原型void unregister_chrdev_region(dev_t, unsigned);函数功能释放注册过的设备号函数参数param1 dev_t * 要释放的起始设备号param2 unsigned 要释放的连续设备号数量2.4 代码部分建议使用动态分配设备号可通过终端命令cat /proc/devices查看设备号是否存在#include linux/init.h #include linux/module.h #includelinux/fs.h #includelinux/kdev_t.h #define DEV_NUM 1 //定义分配连续申请的设备号数量 #define MION_DEV_NUM 0 //定义请求的第一个次设备号 #define DEV_SNAME schrdev //定义静态分配的设备号名称 #define DEV_ANAME achrdev //定义动态分配的设备号名称 static int major_num,minor_num;//定义两个变量用于接受主次设备号 dev_t dev_num;//定义dev_t变量用于接收设备设备号 module_param(major_num,int,0644); module_param(minor_num,int,0644); static int chrdev_init(void) { int ret; if(major_num)//静态分配设备号 { printk(major_num%d\n,major_num); printk(minor_num%d\n,minor_num); dev_num MKDEV(major_num,minor_num);//合成设备号 retregister_chrdev_region(dev_num,DEV_NUM,DEV_SNAME); if(ret!0) { printk(register_chrdev_region failed!\n); return -1; } printk(register_chrdev_region succeed!\n); } else { retalloc_chrdev_region(dev_num,MION_DEV_NUM,DEV_NUM,DEV_ANAME); if(ret!0) { printk(alloc_chrdev_region succeed!\n); return -1; } major_numMAJOR(dev_num);//取出主设备号 minor_numMINOR(dev_num);//取出此设备号 printk(major_num%d\n,major_num); printk(minor_num%d\n,minor_num); } return 0; } static void chrdev_exit(void) { unregister_chrdev_region(dev_num,DEV_NUM); printk(param exit rmmod succeed!\n); } module_init(chrdev_init); module_exit(chrdev_exit); MODULE_LICENSE(GPL);三、注册设备到内核步骤一定义一个cdev结构体cdev结构体头文件#include linux/cdev.hstruct cdev { struct kobject kobj; // 1. 内核对象基础结构 struct module *owner; // 2. 所属模块 const struct file_operations *ops; // 3. 设备操作函数集 struct list_head list; // 4. 链表节点 dev_t dev; // 5. 设备号 unsigned int count; // 6. 设备数量 };步骤二初始化一个字符设备结构体cdev头文件#include linux/cdev.h函数原型void cdev_init(struct cdev *, const struct file_operations *);函数功能初始化一个字符设备结构体cdev将其与指定的文件操作函数集fops关联为后续添加到内核系统做准备函数参数param1:指向要初始化的字符设备结构体cdev的指针param2:指向设备对应的文件操作函数集file_operations的指针步骤三将初始化好的字符设备注册到内核头文件#include linux/cdev.h函数原型int cdev_add(struct cdev *cdev, dev_t dev, unsigned count)函数功能将初始化好的字符设备注册到内核使其生效函数参数param1cdev已初始化的字符设备结构体param2dev起始设备号param3count连续设备号的数量返回值成功返回0失败返回错误码负数注销字符设备头文件#include linux/cdev.h函数原型void cdev_del(struct cdev *cdev)函数功能从内核中移除字符设备函数参数param1cdev要删除的字符设备结构体注意该函数在代码中的执行顺序字符设备注册完以后会自动生成设备节点但是字符类设备经过设备号申请、注册设备到内核、这来两步是没有办法生成设备节点的需要通过mknod命令手动添加设备节点格式mknod 名称 类型 主设备号 次设备号eg:mknod /dev/test c 247 0 将主设备号为247次设备号为0的字符类设备生成/dev/test节点代码驱动层#include linux/init.h #include linux/module.h #includelinux/fs.h #includelinux/kdev_t.h #include linux/cdev.h #define DEV_NUM 1 //定义分配连续申请的设备号数量 #define MION_DEV_NUM 0 //定义请求的第一个次设备号 #define DEV_SNAME schrdev //定义静态分配的设备号名称 #define DEV_ANAME achrdev //定义动态分配的设备号名称 static int major_num,minor_num;//定义两个变量用于接受主次设备号 struct cdev cdev; //定义一个cdev类型结构体 static int cdev_open(struct inode *inode, struct file *file) { printk(open this cdev succeed\n); return 0; } struct file_operations cdev_fops { .owner THIS_MODULE, .opencdev_open }; //定义文件操作集结构体 module_param(major_num,int,0644); module_param(minor_num,int,0644); static int chrdev_init(void) { int ret1,ret2; dev_t dev_num;//定义dev_t变量用于接收设备设备号 /*申请设备号*/ if(major_num)//静态分配设备号 { printk(major_num%d\n,major_num); printk(minor_num%d\n,minor_num); dev_num MKDEV(major_num,minor_num);//合成设备号 ret1register_chrdev_region(dev_num,DEV_NUM,DEV_SNAME); if(ret1!0) { printk(register_chrdev_region failed!\n); return -1; } printk(register_chrdev_region succeed!\n); } else { ret1alloc_chrdev_region(dev_num,MION_DEV_NUM,DEV_NUM,DEV_ANAME); if(ret1!0) { printk(alloc_chrdev_region succeed!\n); return -1; } major_numMAJOR(dev_num);//取出主设备号 minor_numMINOR(dev_num);//取出此设备号 printk(major_num%d\n,major_num); printk(minor_num%d\n,minor_num); } /*将设备号注册到内核*/ cdev.ownerTHIS_MODULE; cdev_init(cdev,cdev_fops);//初始化一个字符设备结构体cdev将其与指定的文件操作函数集fops关联 ret2cdev_add(cdev,dev_num,DEV_NUM);//注册设备到内核 if(ret2!0) { printk(cdev_add failed\n); return -2; } return 0; } static void chrdev_exit(void) {//注意注销顺序 unregister_chrdev_region(MKDEV(major_num,minor_num),DEV_NUM);//注销设备号 cdev_del(cdev);//注销设备到内核 printk(param exit rmmod succeed!\n); } module_init(chrdev_init); module_exit(chrdev_exit); MODULE_LICENSE(GPL);手动生成设备节点应用层#include stdio.h #include fcntl.h #include unistd.h #include sys/stat.h #include sys/types.h int main(int argc, char *argv[]) { int fd; fd open(/dev/my_cdev, O_RDWR); //打开驱动对应的设备文件 if(fd 0) { printf(my_cdev device open failed\n); return -1; } printf(app layer device open success\n); close(fd); return 0; }四、自动创建设备节点1.问题怎么自动创建一个设备节点在嵌入式Linux中使用mdev来实现设备节点文件的自动创建和删除2.什么是mdev?mdev是udev的简化版是busybox中所带的程序最适合用在嵌入式系统3.什么是udev?udev是一种工具它能够根据系统中的银行间设备状态动态更新设备文件包括文件的创建删除等。设备文件通常放在/dev目录下。使用udev后在/dev目录下就只包含系统中真正存在的设备。udev一般用在PC上的linux中相对mdev来说要复杂一些4.怎么自动创建设备节点自动创建设备节点分为两个步骤步骤一使用class_create函数出创建一个类步骤二使用device_create函数在创建的类下面创建一个设备4.1创建和删除类函数创建类头文件#includelinux/device.hextern struct class * __must_check __class_create(struct module *owner, const char *name, struct lock_class_key *key); #define class_create(owner, name) \ ({ \ static struct lock_class_key __key; \ __class_create(owner, name, __key); \ })功能创建的类参数param1 owner 一般为THIS_MODULEparam2 name 创建的类的名字在/sys/class下生成对应名字的类返回值指向结构体class的指针头文件#includelinux/device.hvoid class_destroy(struct class *cls);函数参数cls 要删除的类函数功能删除创建的类在创建的类下面创建设备节点头文件#includelinux/device.hstruct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...);函数功能在类下面创建设备节点函数参数param1 cls 设备要创建哪个类下面param2 parent 父设备一般为NULL也就是没有父设备param3 devt 设备号param4 drvdata 是设备可能会使用的一些数据一般为NULLparam5 fmt 设备节点名称若是fmtxxx则会生成/dev/xxx这个设备文件返回值创建好的设备头文件#includelinux/device.hvoid device_destroy(struct class *cls, dev_t devt);函数功能删除设备节点函数参数param1 cls 设备节点所在的类param2 devt 设备号4.2 代码驱动代码#include linux/init.h #include linux/module.h #includelinux/fs.h #includelinux/kdev_t.h #include linux/cdev.h #include linux/device.h #define DEV_NUM 1 //定义分配连续申请的设备号数量 #define MION_DEV_NUM 0 //定义请求的第一个次设备号 #define DEV_SNAME schrdev //定义静态分配的设备号名称 #define DEV_ANAME achrdev //定义动态分配的设备号名称 #define DEV_CLASS_NAME chrdev_class //定义创建的类的名字 #define DEV_NODE_NAME my_cdev static int major_num,minor_num;//定义两个变量用于接受主次设备号 struct cdev cdev; //定义一个cdev类型结构体 struct class*class; //定义一个class类型的结构体指针 struct device*device; // 定义一个device类型的结构体指针 static int cdev_open(struct inode *inode, struct file *file) { printk(open this cdev succeed\n); return 0; } struct file_operations cdev_fops { .ownerTHIS_MODULE, .opencdev_open }; //定义文件操作集结构体 module_param(major_num,int,0644); module_param(minor_num,int,0644); static int chrdev_init(void) { int ret1,ret2; dev_t dev_num;//定义dev_t变量用于接收设备设备号 /*申请设备号*/ if(major_num)//静态分配设备号 { printk(major_num%d\n,major_num); printk(minor_num%d\n,minor_num); dev_num MKDEV(major_num,minor_num);//合成设备号 ret1register_chrdev_region(dev_num,DEV_NUM,DEV_SNAME); if(ret1!0) { printk(register_chrdev_region failed!\n); return -1; } printk(register_chrdev_region succeed!\n); } else//动态分配设备号 { ret1alloc_chrdev_region(dev_num,MION_DEV_NUM,DEV_NUM,DEV_ANAME); if(ret1!0) { printk(alloc_chrdev_region succeed!\n); return -1; } major_numMAJOR(dev_num);//取出主设备号 minor_numMINOR(dev_num);//取出次设备号 printk(major_num%d\n,major_num); printk(minor_num%d\n,minor_num); } /*将设备号注册到内核*/ cdev.ownerTHIS_MODULE; cdev_init(cdev,cdev_fops);//初始化一个字符设备结构体cdev将其与指定的文件操作函数集fops关联 ret2cdev_add(cdev,dev_num,DEV_NUM);//注册设备到内核 if(ret2!0) { printk(cdev_add failed\n); return -2; } /*自动生成设备节点*/ classclass_create(THIS_MODULE,DEV_CLASS_NAME); //创建类 devicedevice_create(class,NULL,dev_num,NULL,DEV_NODE_NAME);//生成设备节点 return 0; } static void chrdev_exit(void) { unregister_chrdev_region(MKDEV(major_num,minor_num),DEV_NUM);//注销设备号 cdev_del(cdev);//注销设备到内核 device_destroy(class,MKDEV(major_num,minor_num));//注销设备节点 class_destroy(class);//注销创建的类 printk(param exit rmmod succeed!\n); } module_init(chrdev_init); module_exit(chrdev_exit); MODULE_LICENSE(GPL);应用层#include stdio.h #include fcntl.h #include unistd.h #include sys/stat.h #include sys/types.h int main(int argc, char *argv[]) { int fd; fd open(/dev/my_cdev, O_RDWR); //打开驱动对应的设备文件 if(fd 0) { printf(my_cdev device open failed\n); return -1; } printf(app layer device open success\n); close(fd); return 0; }4.3 结果通过 cat /proc/devices查看申请的设备号以设备号名称通过ls /sys/class查看生成的类通过ls /dev/命令查看生成的设备节点