「深入浅出:Linux字符设备号解析」 (linux 字符设备号)

深入浅出:Linux字符设备号解析

在Linux系统中,设备文件可分为两种类型:块设备和字符设备。其中字符设备,是一种能够一次性读写一个字节或一个字符的设备,如键盘、鼠标、打印机、声卡等。而字符设备号,则用于在Linux系统中唯一标识每个字符设备。本篇文章将深入浅出地解析Linux字符设备号的相关知识。

Linux字符设备号的定义

在Linux系统中,每个字符设备号由两部分组成:主设备号和次设备号。主设备号用于标识设备所属的设备类型,同时也决定了字符设备的主要操作函数;而次设备号则用于标识同一类型设备的不同实例。

一般情况下,主设备号的值是预设的,而次设备号则由内核动态分配。在Linux源码的include/linux/major.h中,我们可以找到一些已经预设好的主设备号,如下所示:

“`

#define MEM_MAJOR 1

#define FLOPPY_MAJOR 2

#define HD_MAJOR 3

#define TTY_MAJOR 4

#define LP_MAJOR 6

“`

其中,4号主设备号的被称为tty设备,即终端设备,这是Linux中最常见的设备类型。其他设备类型如网络设备、声卡、输入设备等,都有自己的主设备号。

次设备号的分配

Linux内核用一套动态分配的方式来为字符设备分配次设备号。这一套机制叫做udev(user space device),是内核态和用户态之间交互的重要组成部分。

当我们插入一台新设备时,udev会首先查询目录/lib/udev/devices,找到一个未使用的次设备号,然后创建一个对应的设备文件,最终将该设备文件与对应的次设备号关联。这样,在用户态通过设备文件访问到设备时,实际上是通过该文件与次设备号关联起来的。

此外,次设备号的分配方式还可以通过内核态ioctl接口进行配置。在实际开发中,我们可以通过下面这段代码来修改设备的次设备号

“`

int ioctl(int fd, unsigned long cmd, unsigned long arg);

“`

其中fd是打开设备文件返回的文件描述符,cmd是需要执行的命令,arg是与命令有关的参数。通过这个接口,我们可以动态分配和修改次设备号,实现更精细的设备管理和控制。

Linux字符设备号的使用

有了主次设备号,我们就可以构造字符设备号了。在Linux内核的include/linux/fs.h中,有一个宏MKDEV,可以用于将主次设备号合并成一个字符设备号。例如:

“`

dev_t devno = MKDEV(2, 1); // devno此时为 0x0201

“`

其中,2表示主设备号,1表示次设备号。需要注意的是,主次设备号的更大取值都为255,在分配前需要充分考虑到主设备号的预设值。

除了MKDEV宏,我们还可以通过字符设备相关头文件的宏定义来获取主设备号或次设备号。

其中,MAJOR宏返回设备号的主设备号,MINOR宏返回设备号的次设备号。我们也可以用来这样直接获取设备号

“`

dev_t devno;

devno = inode->i_rdev; // 获取设备号

unsigned int major = MAJOR(devno);

unsigned int minor = MINOR(devno);

“`

其中,inode是一个Linux内核中的数据结构,表示一个文件的i-node节点。通过i-node节点,我们可以访问到文件的各项属性,包括文件所关联的设备文件。i_rdev即是i-node节点中的设备号信息。

在Linux系统中,字符设备是一种能够一次性读写一个字节或一个字符的设备,如键盘、鼠标、打印机、声卡等。字符设备号则用于在Linux系统中唯一标识每个字符设备。

字符设备号由两部分组成:主设备号和次设备号。其中,主设备号用于标识设备所属的设备类型,次设备号则用于标识同一类型设备的不同实例。主设备号的值是预设的,而次设备号则由内核动态分配。

通过MKDEV宏、MAJOR宏和MINOR宏等宏定义,我们可以方便地获取和操作字符设备号。同时,通过ioctl接口,我们还可以动态分配和修改次设备号,实现更精细的设备管理和控制。

相关问题拓展阅读:

在Linux内核中,注册字符设备驱动程序的函数是?

字符设备驱动程序框架 1、写出open、write函数 2、告诉内核 1)、定义一个struct file_operations结构并填充好 static struct file_operations first_drv_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = first_drv_open, .write = first_drv_write, }; 2)、把struct file_operations结构体告诉内核 major = register_chrdev(0, “first_drv”, &first_drv_fops); // 注册, 告诉内核相关参数:之一个,设备号,0自动分配主设备号,否则为主设备号0-255 第二个:设备名第二个:struct file_operations结构体 4)、register_chrdev由谁调用(入口函数调用) static int first_drv_init(void) 5)、入口函数须使用内核宏来修饰 module_init(first_drv_init); module_init会定义一个结构体,这个结构体里面有一个函数指针指向first_drv_init这个函数,当我们加载或安装一个驱动时,内核会自动找到这个结构体,然后调用里面的函数指针,这个函数指针指向first_drv_init这个函数,first_drv_init这个函数就是把struct file_operations结构体告诉内核 6)、有入口函数就有出口函数 module_exit(first_drv_exit); 最后加上协议 MODULE_LICENSE(“GPL”); 3、mdev根据系统信息自动创建设备节点: 每次写驱动都要手动创建设备文件过于麻烦,使用设备管理文件系统则方便很多。在2.6的内核以前一直使用的是devfs,但是它存在许多缺陷。它创建了大量的设备文件,其实这些设备更本不存在。而且设备与设备文件的映射具有不确定性,比如U盘即可能对应sda,又可能对应sdb。没有足够的主/辅设备号。2.6之后的内核引入了sysfs文件系统,它挂载在/sys上,配合udev使用,可以很好的完成devfs的功能,并弥补了那些缺点。(这里说一下,当今内核已经使用netlink了)。 udev是用户空间的一个应用程序,在嵌入式中用的是mdev,mdev在busybox中。mdev是udev的精简版。首先在busybox中添加支持mdev的选项: Linux System Utilities —> mdev Support /etc/mdev.conf Support subdirs/symlinks Support regular expressions substitutions when renaming device Support command execution at device addition/removal 然后修改/etc/init.d/rcS: echo /in/mdev > /proc/sys/kernel/hotplug /in/mdev -s 执行mdev -s :以‘-s’为参数调用位于 /in目录写的mdev(其实是个链接,作用是传递参数给/bin目录下的busybox程序并调用它),mdev扫描 /sys/class 和 /sys/block 中所有的类设备目录,如果在目录中含有名为“dev”的文件,且文件中包含的是设备号,则mdev就利用这些信息为这个设备在/dev 下创建设备节点文件。一般只在启动时才执行一次 “mdev -s”。热插拔事件:由于启动时运行了命 令:echo /in/mdev > /proc/sys/kernel/hotplug ,那么当有热插拔事件产生时,内核就会调用位于 /in目录的mdev。这时mdev通过环境变量中的 ACTION 和 DEVPATH,来确定此次热插拔事件的动作以及影响了/sys中的那个目录。接着会看看这个目录中是否“dev”的属性文件,如果有就利用这些信息为 这个设备在/dev 下创建设备节点文件重新打包文件系统,这样/sys目录,/dev目录就有东西了下面是create_class的原型: #define class_create(owner, name) / ({ / static struct lock_class_key __key; / __class_create(owner, name, &__key); / }) extern struct class * __must_check __class_create(struct module *owner, const char *name, struct lock_class_key *key); class_destroy的原型如下: extern void class_destroy(struct class *cls); device_create的原型如下: extern struct device *device_create(struct class *cls, struct device *parent, dev_t devt, void *drvdata, const char *fmt, …) __attribute__((format(printf, 5, 6))); device_destroy的原型如下: extern void device_destroy(struct class *cls, dev_t devt); 具体使用如下,可参考后面的实例: static struct class *firstdrv_class; static struct class_device *firstdrv_class_dev; firstdrv_class = class_create(THIS_MODULE, “firstdrv”); firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, “xyz”); /* /dev/xyz */ class_device_unregister(firstdrv_class_dev); class_destroy(firstdrv_class); 下面再来看一下应用程序如何找到这个结构体的在应用程序中我们使用open打开一个设备:如:open(/dev/, O_RDWR); 有一个属性,如字符设备为c,后面为读写权限,还有主设备名、次设备名,我们注册时 通过register_chrdev(0, “first_drv”, &first_drv_fops)(有主设备号,设备名,struct file_operations结构体)将first_drv_fops结构体注册到内核数组chrdev中去的,结构体中有open,write函数,那么应用程序如何找到它的,事实上是根据打开的这个文件的属性中的设备类型及主设备号在内核数组chrdev里面找到我们注册的first_drv_fops,实例代码: #include #include #include #include #include #include #include #include #include #include static struct class *firstdrv_class; static struct class_device *firstdrv_class_dev; volatile unsigned long *gpfcon = NULL; volatile unsigned long *gpfdat = NULL; static int first_drv_open(struct inode *inode, struct file *file) { //printk(“first_drv_open\n”); /* 配置GPF4,5,6为输出 */ *gpfcon &= ~((0x3); return 0; } if (strcmp(argv, “on”) == 0) { val = 1; } else { val = 0; } write(fd, &val, 4); return 0; }

Linux驱动与设备节点简介 & Android内核与Linux内核的区别

驱动是内核的一部分,作为直接访问物理硬件的一个软件层,用于应用程序与物理硬件设备通信。内核包含多种驱动,如WIFI、USB、Audio、蓝牙、相机、显示驱动。

(1)设备驱动程序三类:字符设备驱动程序、块设备驱动程序、网络设备驱动程序;

(2)对应Linux三类设备:字符设备、块设备、网络设备;

(3)常见字符设备:鼠标、键盘、串口、控制台等;

(4)常见块设备:各种硬盘、flash磁盘、RAM磁盘等;

(5)网络设备(网络接口):eth0、eth1,注:网络设备没有设备节点,应用程序通过Socket访问网络设备。由于网络设备面向报文,较难实现相关read、write等文件读写函数,所以驱动的实现也与字符设备和块设备不同。

Linux使用对文件一样的管理方式来管理设备,所有设备都以文件的形式存放在/dev目录下,系统中的每个字符设备或者块设备都必须为其创建一个设备文件,它包含了该设备的设备类型(块设备或字符设备)、设备号(主设备号和次设备号)以及设备访问控制属性等。设备节点通过

mknod

命令创建,也可以由Udev用户工具软件在系统启动后根据/sys目录下每个设备的实际信息创建,使用后一种方式可以为每个设备动态分配设备号。

Linux中设备节点通过“mknod”命令创建,创建时需要指定主设备号和次设备号,即指定对应的驱动程序和对应的物理设备(访问设备节点时就相当于通过其设备号访问驱动程序进而间接访问到物理设备)。主设备号用来区分不同种类的设备,而次设备号用来区分同一类型的多个设备。对于常用设备,Linux有约定俗成的编号,如硬盘的主设备号是3

理解:应用程序通过访问设备节点读取主设备号和次设备号,通过主设备号找对应的驱动,通过次设备号对应到具体物理设备。注:1个驱动对应一类设备,并用唯一主设备号标识。

Linux支持的各种设备的主设备号定义在include/linux/major.h文件中,已经在官方注册的主设备号和次设备号在Documentation/devices.txt文件中。

Android系统更底层是Linux,并且在中间加上了一个Dalvik / ART的Java虚拟机,从表面层看是Android运行库。每个Android应用都运行在自己的进程上,享有Dalvik / ART虚拟机为它分配的专有实例,并支持多个虚拟机在同一设备上高效运行,虚拟机执行的是专有格式的可执行文件(.dex) – 该格式经过优化,以将内存好用降到更低。

Android内核和Linux内核的差别主要体现在如下11个方面:

关于linux 字符设备号的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。


数据运维技术 » 「深入浅出:Linux字符设备号解析」 (linux 字符设备号)