scull设计和如何动态分配主设备
一、scull设计解析
1.什么是scull
一个操作内存区域的字符设备驱动程序,这片内存区域相当于一个设备。
2.scull优点
1.在于它不和硬件相关 2.只是操作从内核中分配的一些内存。任何人都可以编译和运行scull 3.还可以将scull移植到Linux支持的计算机平台上, 4.展示内核和字符驱动之间的接口并且让用户运行某些测试。scull设备做不了任何事
3.scull设计
编写驱动程序的第一步就是定义驱动程序未用户程序提供的功能(机制) scull的源代码实现了下列的设备,模块实现每一种设备称作一种 “类型”:
- scull0~scull3
这四个设备分别由一个全局且持久的内存区域组成,全局指的是:如果设备被多次打开,则打开她的所有文件描述符可共享该设备所包含的数据。“持久”是指 设备关闭在打开数据不丢失。 - scullpipe0~scullpipe3
一个进程读取由另一个进程写入的数据。如果多进程读取同一个设备。他们会产生竞争关系。scullpipe的内部实现将说明不借助与中断的情况下如何实现阻塞时和非阻塞式的读/写 - scullsingle
一次只允许一个进程使用该驱动程序 - scullpriv
而scullpriv对每个虚拟控制台式私有的。这是因为每个控制台/终端上的进程将获取不同的内存区域 - sculluid、scullwuid
sculluid和scullwuid可被多次打开,但每次只能由一个用户打开;如果另一个用户锁定该设备。sculluid将返回 “Device Busy”的错误,而scullwuid则实现了阻塞时open.这些scull设备的变种混淆了机制和策略,
如何动态分配主设备号
一个字符设备或者块设备都有一个主设备号和次设备号。主设备号和次设备号统称为设备号。主设备号用来表示一个特定的驱动程序。次设备号用来表示使用该驱动程序的各设备 注:区分字符驱动设备和块驱动设备 字符设备驱动程序的设备文件可通过ls -l 命令输出的第一行的“c”来识别。块设备也出现在/dev ,标识用"b"标识
案例解析
如果执行ls -l命令,则可在设备文件项的最后修改日期前两个数(用逗号分隔),这个位置通常现实的时文件的长度; 而对设备文件,这两个数就是相应设备的主设备号和次设备号。主设备号是1,4,7,10和次设备号是1、3、5、64、65和129 通常而言、主设备号标识设备对应的驱动程序。例如,/dev/null和/dev/zero由驱动程序1管理。而虚拟控制台和串口终端由驱动程序4管理,类似地,vcsl和vcsal设备都有驱动程序7管理。,一个主设备号对应一个驱动程序的原则
次设备号
在内核中 dev_t 用来保持设备编号, 包括主从设备号,dev_t是一个32位的数,其中的12位用来标识主设备数,而其余20位用来标识次设备号。应该始终使用<linux/kdev_t.h>中定义的宏。比如获得dev_t的主从设备号应使用:
MAJOR(dev_t dev);
MINOR(dev_t dev);
如果需要把主从设备号换称dev_t类型则使用
MKDEV(int major, int minor);
分配和释放设备编号
在建立一个字符设备之前,我们的驱动程序首先要做的事情就是获得一个或多个设备编号。完成该工作的必要函数是register_chrdev_region,该函数在<linux/fs.h>中声明
int register_chrdev_region(dev_t first,unsigned int count, char *name);
其中,first是要分配的设备编号范围的起始值。first的次设备号经常被设置位0,
count是锁清秋的联系设备编号的个数,如果count非常大,则所请求的返回可能和
下一个主设备号重叠。 name是该编号范围关联的设备名称,它将出现在/proc/devices和sysfs中
int alloc_chrdev_region(dev_t *dev,unsigned int firstminor, unsigned int count, char * name);
dev是仅用于输出的参数,在调用完成后将保持已分配防范围的第一个编号。
firstminor应该是要使用的被生气的第一个次设备号,
它通常是0.count和name参数与register_chrdev_region函数一样的
void unregister_chrdev_region(dev_t first,unsigned int count);
我们在模块的清除函数中调用unregister_chrdev_region函数
动态分配主设备号
由于分配的主设备号不能保证始终一致,所以驱动预先创建设备节点 下面这个名scull_load的脚本是scull发布的一部分。使用以模块形式发布的驱动程序的用户可以在系统的rc.local文件中调用这个脚本。或是在需要模块是手工调用。
#!/bin/sh
module="scull"
device="scull"
modu="664"
/sbin/insmod ./$module.ko $* || exit 1
rm -f /dev/${device}[0-3]
major=${awk "\$2= =\"$module\" {print \$1}" /proc/devices}
mknod /dev/${device}0 c $major 0
mknod /dev/${device}1 c $major 1
mknod /dev/${device}2 c $major 2
mknod /dev/${device}3 c $major 3
qroup="staff"
grep -q '^staff:' /etc/group || group="wheel"
chgrp $group /dev/${device}[0-3]
chmod $mode /dev/${device}[0-3]
这个脚本同样可以适用其他驱动程序,只要重新定义变量并调整mknod那几行语句就可以了,该脚本创建了四个设备,应为scull得源码默认创建四个设备。
为什么要改变设备得组和访问模式呢,原因在于这个脚本必须有超级用户允许,所以新创建得设备文件自然属于root,默认得权限只允许root对其有写访问前线。我们得脚本默认地把访问权赋予给一个用户组。
处理适用这一对装载和卸载模块脚本外,我们还可以把编写一个init脚本,并将其保持再发行版适用得init脚本目录中,最为scull源码一部分,我们提供了相当详尽和可配置得init脚本范例。
分配主设备号得最佳方式:默认采用动态分配,同时保留在加载甚至时编译时指定主设备号得余地。scull的实现采用这种方式;它适用了一个全局变量scull_major,迎来保留选择的设备号,该变量的初始化只时SCULL_MAJOR,这个宏定义在scull.h中。SCULL__MAJOR默认取0,即选择动态分配。用户可以适用这个默认值或选择某个特定的主设备号, 也可用insmod命令行指定scull_major的值。
scull.c中用来获取主设备号的代码
if(scull_major){
? dev = MKDEV(scull_major,scull_minor);
result = register_chrdev_region(&dev,scull_minor,scull_nr_devs,"scull");
}else{
? result = alloc_chrdev_region(&dev,scull_minor,scull_nr_devs,"scull");
? scull_major = MAJOR(dev);
}
|