第1页共49页编号:时间:2021年x月x日书山有路勤为径,学海无涯苦作舟页码:第1页共49页LinuxDeviceDriver书籍(6)并发和竞争情况(2008-09-2816:28)分类:LDD3第6章高级字符驱动操作在第3章,我们建立了一个完整的设备驱动,用户可用来写入和读取.但是一个真正的设备常常提供比同步读和写更多的功能.现在我们已装备有调试工具如果发生错误,并且一个牢固的并发的理解来帮助避免事情进入错误--我们可安全地前进并且创建一个更高级的驱动.本章检查几个你需要理解的概念来编写全特性的字符设备驱动.我们从实现ioctl系统调用开始,它是用作设备控制的普通接口.接着我们进入各种和用户空间同步的方法;在本章结尾,你有一个充分的认识对于如何使进程睡眠(并且唤醒它们),实现非阻塞的I/O,并且通知用户空间当你的设备可用来读或写.我们以查看如何在驱动中实现几个不同的设备存取策略来结束.这里讨论的概念通过scull驱动的几个修改版本来演示.再一次,所有的都使用内存中的虚拟设备来实现,因此你可自己试验这些代码而不必使用任何特别的硬件.到此为止,你可能在想亲手使用真正的硬件,但是那将必须等到第9章.6.1.ioctl接口大部分驱动需要--除了读写设备的能力--通过设备驱动进行各种硬件控制的能力.大部分设备可进行超出简单的数据传输之外的操作;用户空间必须常常能够请求,例如,设备锁上它的门,弹出它的介质,报告错误信息,改变波特率,或者自我销毁.这些操作常常通过ioctl方法来支持,它通过相同名子的系统调用来实现.在用户空间,ioctl系统调用有下面的原型:intioctl(intfd,unsignedlongcmd,...);这个原型由于这些点而凸现于Unix系统调用列表,这些点常常表示函数有数目不定的参数.在实际系统中,但是,一个系统调用不能真正有变数目的参数.系统调用必须有一个很好定义的原型,因为用户程序可存取它们只能通过硬件的"门".因此,原型中的点不表示一个变数目的参数,而是一个单个可选的参数,传统上标识为char*argp.这些点在那里只是为了阻止在编译时的类型检查.第3个参数的实际特点依赖所发出的特定的控制命令(第2个参数).一些命令不用参数,一些用一个整数值,以及一些使用指向其他数据的指针.使用一个指针是传递任意数据到ioctl调用的方法;设备接着可与用户空间交换任何数量的数据.第2页共49页第1页共49页编号:时间:2021年x月x日书山有路勤为径,学海无涯苦作舟页码:第2页共49页ioctl调用的非结构化特性使它在内核开发者中失宠.每个ioctl命令,基本上,是一个单独的,常常无文档的系统调用,并且没有方法以任何类型的全面的方式核查这些调用.也难于使非结构化的ioctl参数在所有系统上一致工作;例如,考虑运行在32-位模式的一个用户进程的64-位系统.结果,有很大的压力来实现混杂的控制操作,只通过任何其他的方法.可能的选择包括嵌入命令到数据流(本章稍后我们将讨论这个方法)或者使用虚拟文件系统,要么是sysfs要么是设备特定的文件系统.(我们将在14章看看sysfs).但是,事实是ioctl常常是最容易的和最直接的选择,对于真正的设备操作.ioctl驱动方法有和用户空间版本不同的原型:int(*ioctl)(structinode*inode,structfile*filp,unsignedintcmd,unsignedlongarg);inode和filp指针是对应应用程序传递的文件描述符fd的值,和传递给open方法的相同参数.cmd参数从用户那里不改变地传下来,并且可选的参数arg参数以一个unsignedlong的形式传递,不管它是否由用户给定为一个整数或一个指针.如果调用程序不传递第3个参数,被驱动操作收到的arg值是无定义的.因为类型检查在这个额外参数上被关闭,编译器不能警告你如果一个无效的参数被传递给ioctl,并且任何关联的错误将难以查找.如果你可能想到的,大部分ioctl实现包括一个大的switch语句来根据cmd参数,选择正确的做法.不同的命令有不同的数值,它们常常被给予符号名来简化编码.符号名通过一个预处理定义来安排.定制的驱动常常声明这样的符号在它们的头文件中;scull.h为scull声明它们.用户程序必须,当然,包含那个头文件来存取这些符号.6.1.1.选择ioctl命令在为ioctl编写代码之前,你需要选择对应命令的数字.许多程序员的第一个本能的反应是选择一组小数从0或1开始,并且从此开始向上....