By: 潘云登
Date: 2009-6-4
Email: intrepyd@gmail.com
Homepage: http://blog.youkuaiyun.com/intrepyd
Copyright: 该文章版权由潘云登所有。可在非商业目的下任意传播和复制。
对于商业目的下对本文的任何行为需经作者同意。
写在前面
1. 本文内容对应《linux设备驱动程序》第六章。
2. 修改scull_load脚本中的mode为“666”,使普通用户具有设备读写权限。
3. 希望本文对您有所帮助,也欢迎您给我提意见和建议。
这里描述的设备访问控制是在文件系统许可位机制之下的特定设备访问策略,只与驱动程序的实现相关,而与内核无关。
进入ldd3源码的scull目录,执行make编译模块,并调用scull_load脚本加载模块,以及在/dev目录下创建设备文件。它们分别是scullsingle、sculluid、scullwuid和scullpriv。
独享设备
这是最生硬的访问控制方法,一次只允许一个进程打开设备。驱动程序通过维护一个原子变量,用以标识设备是否已经打开。当进程已被一个进程独享时,open方法返回-EBUSY,提示设备已经被进程使用。
利用tail –f命令显示设备文件,它调用驱动程序的open方法,而不执行release释放设备。
pydeng@pydeng-laptop:~$ tail -f /dev/scullsingle |
打开另外一个终端,用cat访问同一个设备,提示设备正忙。
pydeng@pydeng-laptop:~$ cat /dev/scullsingle cat: /dev/scullsingle: Device or resource busy |
限制每次只有一个用户访问设备
该策略允许单个用户在多个进程中打开设备,但是每次只允许一个用户打开设备。驱动程序需要维护两个数据项,一个用于打开计数,一个记录设备属主的UID。这些数据项通常应该保存在设备结构内部。当进程打开设备时,驱动程序通过进程结构中的uid进行访问控制,如current->uid。
现在来测试一下sculluid设备,感受这种访问控制的效果。首先,以pydeng用户保持设备的打开状态。
pydeng@pydeng-laptop:/etc$ tail -f /dev/sculluid |
在另外一个终端中,以cying用户访问设备,结果提示设备正忙。
cying@pydeng-laptop:~$ cat /dev/sculluid cat: /dev/sculluid: Device or resource busy |
这时,用su切换回pydeng用户,则可以成功打开设备,并进行正常读写。
cying@pydeng-laptop:~$ su pydeng pydeng@pydeng-laptop:/# ls > /dev/sculluid pydeng@pydeng-laptop:/# cat /dev/sculluid bin boot cdrom dev etc ... |
阻塞型打开设备
当设备不能访问设备时返回-EBUSY,通常是最合理的方式,但是有些情况下可能需要让进程休眠,以等待设备。如何使进程休眠,可参看上一篇学习笔记《设备驱动程序学习笔记(5)-休眠与唤醒》。
首先,用相同的方式,以pydeng用户保持设备的打开状态。
pydeng@pydeng-laptop:/etc$ tail -f /dev/scullwuid |
在另外一个终端中,以cying用户访问设备,结果并未提示“Device or resource busy”。可以看到,终端中光标在闪烁,进程进入休眠。
cying@pydeng-laptop:/$ ls / > /dev/scullwuid _ |
用Ctrl+c结束pydeng用户的tail进程。这时,刚才休眠的进程成功返回,并成功地对设备进行了写操作。
cying@pydeng-laptop:/$ cat /dev/scullwuid bin boot cdrom dev etc ... |
在打开时复制设备
另一个实现设备访问控制的方法是,在进程打开设备时创建设备的不同私有副本。这种方法只有在设备没有绑定到某个硬件对象时才能实现,如/dev/tty。在scullpriv的实现中,驱动程序使用进程控制终端的次设备号作为创建设备副本的键值。当然,可以使用uid为每个用户创建不同的私有副本,或者使用pid为每个访问该设备的进程创建副本。
在两个终端中,分别对scullpriv设备进行读写,可以看到设备数据是不同的。一个终端中的设备写入了数据,而另一个终端中的设备数据仍然为空。
pydeng@pydeng-laptop:/etc$ ls / > /dev/scullpriv pydeng@pydeng-laptop:/etc$ cat /dev/scullpriv bin boot cdrom dev etc ... |
pydeng@pydeng-laptop:/etc$ cat /dev/scullpriv pydeng@pydeng-laptop:/etc$ |