嵌入式 ioctl函数的实际意义小论

原创 2013年12月02日 14:45:42
函数名: ioctl
头文件:#include
功 能: 控制I/O设备,提供了一种获得设备信息和向设备发送控制参数的手段。用于向设备发控制和配置命令 ,有些命令需要控制参数,这些数据是不能用read /write 读写的,称为Out-of-band数据。也就是说,read / write读写的数据是in-band数据,是I/O操作的主体,而ioctl命令传送的是控制信息,其中的数据是辅助的数据。
用 法: int ioctl(int handle, int cmd,[int *argdx, int argcx]);
返回值:成功为0,出错为-1
usr/include/asm-generic/ioctl.h中定义的宏的注释:
#define _IOC_NRBITS 8 //序数(number)字段的字位宽度,8bits
#define _IOC_TYPEBITS 8 //幻数(type)字段的字位宽度,8bits
#define _IOC_SIZEBITS 14 //大小(size)字段的字位宽度,14bits
#define _IOC_DIRBITS 2 //方向(direction)字段的字位宽度,2bits
#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1)//序数字段的掩码,0x000000FF
#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1)//幻数字段的掩码,0x000000FF
#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1)//大小字段的掩码,0x00003FFF
#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1)//方向字段的掩码,0x00000003
#define _IOC_NRSHIFT 0 //序数字段在整个字段中的位移,0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS) //幻数字段的位移,8
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)//大小字段的位移,16
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS)//方向字段的位移,30
#define _IOC_NONE 0U //没有数据传输
#define _IOC_WRITE 1U //向设备写入数据,驱动程序必须从用户空间读入数据
#define _IOC_READ 2U //从设备中读取数据,驱动程序必须向用户空间写入数据
#define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT))
//构造无参数的命令编号
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
//构造从驱动程序中读取数据的命令编号
#define _IOR(type,nr,size)_IOC(_IOC_READ,(type),(nr),sizeof(size))
//用于向驱动程序写入数据命令
#define _IOW(type,nr,size)_IOC(_IOC_WRITE,(type),(nr),sizeof(size))
//用于双向传输
#define _IOWR(type,nr,size)_IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
//从命令参数中解析出数据方向,即写进还是读出
#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) &_IOC_DIRMASK)
//从命令参数中解析出幻数type
#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) &_IOC_TYPEMASK)
//从命令参数中解析出序数number
#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) &_IOC_NRMASK)
//从命令参数中解析出用户数据大小
#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) &_IOC_SIZEMASK)
#define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT)
#define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT)
#define IOC_INOUT ((_IOC_WRITE|_IOC_READ) <<_IOC_DIRSHIFT)
#define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT)
#define IOCSIZE_SHIFT (_IOC_SIZESHIFT)
程序例:
#include
#include
#include
int main(void) {
..int stat;
..stat = ioctl(0, 8, 0, 0);
..if (!stat)
....printf("Drive %c is removable.\n", getdisk() + 'A');
..else
....printf("Drive %c is not removable.\n", getdisk() + 'A');
..return 0;
}
int ioctl( int fd, int request, ... ) 详解
第三个参数总是一个指针,但指针的类型依赖于request 参数。我们可以把和网络相关的请求划分为6 类:
套接口操作
文件操作
接口操作
ARP 高速缓存操作
路由表操作
流系统
下表列出了网络相关ioctl请求的request 参数以及arg 地址必须指向的数据类型
类别
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换成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控制都是通过这一部分的代码实现的。

相关文章推荐

委托类型协变和逆变的实际意义

设一个委托 TypeA1 DelegateDefine(TypeB1 b)和实际调用的委托方法TypeA2 DelegateInstance(TypeB2 b){}根据面向对象继承使用的原则,Dele...

特征值和特征向量的实际意义

本文转自知乎大牛。 从定义出发,Ax=cx:A为矩阵,c为特征值,x为特征向量。 矩阵A乘以x表示,对向量x进行一次转换(旋转或拉伸)(是一种线性转换),而该转换的效果为常数c乘以向量x(即只进行...

Redis集群~windows下搭建Sentinel环境及它对主从模式的实际意义

回到目录 关于redis-sentinel出现的原因 Redis集群的主从模式有个最大的弊端,就是当主master挂了之前,它的slave从服务器无法提升为主,而在redis-sentin...

名人演讲并没有实际意义、跟名人传一样

一、除了鼓舞人心并没有太多的意义。 二、如果没有具体的指导,那么不会有帮助。 没有必要凑热闹,没有必要走形式。 三、鼓舞人心并不能提高人的能力。 如果不知道学习什么,要么凭着一场热血,不过是摔得更惨而...

数学概念实际意义探究小记

声明:仅个人小记。前言:本文是对一些数学概念的物理或者其他实际意义的延伸,简要记录一些本人遇到的数学概念的实际意义,并不一定是,提供的解析值得思考或者便于理解,或者具有更好的实用价值。1. 行列式  ...

理解双绞线中直通线、交叉线的实际意义

一、什么是双绞线 LAN网络中最常见的数据传输铜介质是双绞线。铜介质优点是可以非常好的传导电信号,缺点是易受外界干扰而产生畸形和信号衰减,导致长距离传输过程中能量损失。为解决上述不足,提高数据传输稳...

高等几何——讨论交比的实际意义

1 相关链接: 参考百度关于交比的解释:点击打开链接 2 定义: 交比(cross ratio) 射影几何学中基本的射影不变量之一。一般是用共线的四个点来定义的,亦称之为调和比。早在...

MyBatis的Cache实际意义不大

转载的一篇文章。 出于新项目一定要求要用jsp,让我这个习惯写窗体应用程序的去搞web开发,让我好生为难。半路出家,遇到问题很多,转载些文章,自己顺便修改些,留备忘。 说意义不大是在于: a、面...

嵌入式 浅谈fcntl与ioctl函数

fcntl: #include ioctl函数提供对连接到fd的设备驱动程序的属性和操作的访问 其原型为 extern int ioctl(int fd,unsigned long int requ...
  • skdkjxy
  • skdkjxy
  • 2013年12月19日 11:20
  • 1391

嵌入式 获取网卡设备以及IP和MAC地址示例以及网络编程之ioctl小结

#include #include #include #include #include #include #include #include #ifdef SOLAR...
  • skdkjxy
  • skdkjxy
  • 2014年04月28日 15:57
  • 1827
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:嵌入式 ioctl函数的实际意义小论
举报原因:
原因补充:

(最多只允许输入30个字)