类别 | Request | 说明 | 数据类型 |
套 | SIOCATMARK SIOCSPGRP SIOCGPGRP | 是否位于带外标记 设置套接口的进程ID 或进程组ID 获取套接口的进程ID 或进程组ID | int int int |
文 | FIONBIO FIOASYNC FIONREAD FIOSETOWN FIOGETOWN | 设置/ 清除非阻塞I/O 标志 设置/ 清除信号驱动异步I/O 标志 获取接收缓存区中的字节数 设置文件的进程ID 或进程组ID 获取文件的进程ID 或进程组ID | int int int int int |
接 | SIOCGIFCONF SIOCSIFADDR SIOCGIFADDR SIOCSIFFLAGS SIOCGIFFLAGS SIOCSIFDSTADDR SIOCGIFDSTADDR SIOCGIFBRDADDR SIOCSIFBRDADDR SIOCGIFNETMASK SIOCSIFNETMASK SIOCGIFMETRIC SIOCSIFMETRIC SIOCGIFMTU SIOCxxx | 获取所有接口的清单 设置接口地址 获取接口地址 设置接口标志 获取接口标志 设置点到点地址 获取点到点地址 获取广播地址 设置广播地址 获取子网掩码 设置子网掩码 获取接口的测度 设置接口的测度 获取接口MTU (还有很多取决于系统的实现) | struct ifconf struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq struct ifreq |
ARP | SIOCSARP SIOCGARP SIOCDARP | 创建/ 修改ARP 表项 获取ARP 表项 删除ARP 表项 | struct arpreq struct arpreq struct arpreq |
路 | SIOCADDRT SIOCDELRT | 增加路径 删除路径 | struct rtentry struct rtentry |
流 | I_xxx | | |
套接口操作: 明确用于套接口操作的ioctl请求有三个, 它们都要求ioctl的第三个参数是指向某个整数的一个指针。 SIOCATMARK: 如果本套接口的的度指针当前位于带外标记,那就通过由第三个参数指向的整数返回一个非0 值;否则返回一个0 值。POSIX 以函数sockatmark 替换本请求。 SIOCGPGRP : 通过第三个参数指向的整数返回本套接口的进程ID 或进程组ID ,该ID 指定针对本套接口的SIGIO 或SIGURG 信号的接收进程。本请求和fcntl 的F_GETOWN 命令等效,POSIX 标准化的是fcntl 函数。 SIOCSPGRP : 把本套接口的进程ID 或者进程组ID 设置成第三个参数指向的整数,该ID 指定针对本套接口的SIGIO 或SIGURG 信号的接收进程,本请求和fcntl 的F_SETOWN 命令等效,POSIX 标准化的是fcntl 操作。
文件操作: 以下5 个请求都要求ioctl的第三个参数指向一个整数。 FIONBIO : 根据ioctl的第三个参数指向一个0 或非0 值分别清除或设置本套接口的非阻塞标志。本请求和O_NONBLOCK 文件状态标志等效,而该标志通过fcntl 的F_SETFL 命令清除或设置。 FIOASYNC : 根据iocl 的第三个参数指向一个0 值或非0 值分别清除或设置针对本套接口的信号驱动异步I/O 标志,它决定是否收取针对本套接口的异步I/O 信号(SIGIO )。本请求和O_ASYNC 文件状态标志等效,而该标志可以通过fcntl 的F_SETFL 命令清除或设置。 FIONREAD : 通过由ioctl的第三个参数指向的整数返回当前在本套接口接收缓冲区中的字节数。本特性同样适用于文件,管道和终端。 FIOSETOWN : 对于套接口和SIOCSPGRP 等效。 FIOGETOWN : 对于套接口和SIOCGPGRP 等效。
编辑本段定义
ioctl是
设备驱动程序中对设备的
I/O通道进行管理的函数。所谓对I/O通道进行管理,就 是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的调用个数 如下: int ioctl(int fd, int cmd, …); 其中fd就是用户程序打开设备时使用open函数返回的文件标示符,cmd就是用户程序对设 备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和 cmd的意义相关的。 ioctl函数是文件结构中的一个属性分量,就是说如果你的驱动程序提供了对ioctl的支 持,用户就能在用户程序中使用ioctl函数控制设备的I/O通道。
编辑本段必要性
如果不用IOCTL的话,也能实现对设备I/O通道的控制,但那就是蛮拧了。例如,我们可 以在驱动程式中实现WRITE的时候检查一下是否有特别约定的数据流通过,如果有的话, 那么后面就跟着控制命令(一般在SOCKET编程中常常这样做)。不过如果这样做的话,会 导致代码分工不明,程式结构混乱,程式员自己也会头昏眼花的。 所以,我们就使用IOCTL来实现控制的功能。要记住,用户程式所作的只是通过命令码告 诉驱动程式他想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程式要 做的事情。
编辑本段实现操作
这是个非常麻烦的问题,我是能省则省。要说清晰他,没有四五千字是不行的,所以我这 里是不可能把他说得非常清晰了,不过如果有读者对用户程式怎么和驱动程式联系起来感 兴趣的话,能看我前一阵子写的《write的奥秘》。读者只要把write换成ioctl,就知 道用户程式的ioctl是怎么和驱动程式中的ioctl实现联系在一起的了。 我这里说一个大概思路,因为我觉得《Linux设备驱动程式》这本书已说的非常清晰 了,不过得花一些时间来看。 在驱动程式中实现的ioctl函数体内,实际上是有一个switch{case}结构,每一个case对 应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程式员自己的事 情,因为设备都是特定的,这里也没法说。关键在于怎么样组织命令码,因为在ioctl中 命令码是唯一联系用户程式命令和驱动程式支持的途径。 命令码的组织是有一些讲究的,因为我们一定要做到命令和设备是一一对应的,这样才不 会将正确的命令发给错误的设备,或是把错误的命令发给正确的设备,或是把错误的 命令发给错误的设备。这些错误都会导致不可预料的事情发生,而当程式员发现了这些奇 怪的事情的时候,再来调试程式查找错误,那将是非常困难的事情。 所以在Linux核心中是这样定义一个命令码的: ____________________________________ | 设备类型 | 序列号 | 方向 |数据尺寸| |----------|--------|------|--------| | 8 bit | 8 bit |2 bit |8~14 bit| |----------|--------|------|--------| 这样一来,一个命令就变成了一个整数形式的命令码。不过命令码非常的不直观,所以 Linux Kernel中提供了一些宏,这些宏可根据便于理解的字符串生成命令码,或是从 命令码得到一些用户能理解的字符串以标明这个命令对应的设备类型、设备序列号、数 据传送方向和数据传输尺寸。 这些宏我就不在这里解释了,具体的形式请读者察看Linux核心原始码中的和,文件里给 除了这些宏完整的定义。这里我只多说一个地方,那就是"幻数"。 幻数是个字母,数据长度也是8,所以就用一个特定的字母来标明设备类型,这和用一 个数字是相同的,只是更加利于记忆和理解。就是这样,再没有更复杂的了。 更多的说了也没有,读者还是看一看原始码吧,推荐各位阅读《Linux 设备驱动程式》所 带原始码中的short一例,因为他比较短小,功能比较简单,能看明白ioctl的功能和细 节。
编辑本段cmd参数怎么得出
这里确实要说一说,cmd参数在用户程式端由一些宏根据设备类型、序列号、传送方向、 数据尺寸等生成,这个整数通过
系统调用传递到内核中的驱动程式,再由驱动程式使用解 码宏从这个整数中得到设备的类型、序列号、传送方向、数据尺寸等信息,然后通过 switch{case}结构进行相应的操作。 要透彻理解,只能是通过阅读原始码,我这篇文章实际上只是个引子。Cmd参数的组织 还是比较复杂的,我认为要搞熟他还是得花不少时间的,不过这是值得的,驱动程式中最 难的是对中断的理解。 五、 小结 ioctl其实没有什么非常难的东西需要理解,关键是理解cmd命令码是怎么在用户程式里生成 并在驱动程式里解析的,程式员最主要的工作量在switch{case}结构中,因为对设备的 I/O控制都是通过这一部分的代码实现的。