IOCTL函数

来公司实习看代码的时候经常会看到IOCTL这个函数,以前没见过,也问过mentor一回,但一直没搞懂,今天就拿出一点时间查了一下。
首先看一下ioctl的函数原型:
                int ioctl(int fd, int cmd, [int *argdx, int *argcx])
                   返回值:成功为0,出错为-1。

第一个参数fd:是用户打开的文件描述符。
第二个参数cmd:是用户对这个文件描述符对应的设备的控制命令,这个参数是理解ioctl的关键。
剩下的参数是配合第二个参数cmd使用的,如果你是自己在内核中定义了自己的ioctl函数,那么这些剩下的参数就是内核ioctl的入参了,这样你就可以扩展出自己的功能。举个例子,我们项目中内核有很多不同类型的数据需要向用户空间传,这时候如果每种类型定义一个文件的话太多了,所以我们在cmd后面增加了一个参数,用来区别我们要读取的参数是哪个类型的数据,内核中的ioctl函数根据这个参数来进行相应的配置就行了,然后read函数中再根据不同的配置定位不同的内存。


现在讲cmd,这个主要来自于网上对别人博客的参考。
cmd的大小是32位,这32位被分成了4部分,结构入下:
|区别读写|数据大小|魔    数|区别序号|
|31    31|29    16|15    08|07    00|
  bit31~bit30  2位为 “区别读写” 区,作用是区分是读取命令还是写入命令。
 bit29~bit16  14位为 "数据大小" 区,表示 ioctl() 中的 arg 变量传送的内存大小。
 bit15~bit08  8位为 “魔数"(也称为"幻数")区,这个值用以与其它设备驱动程序的 ioctl 命令进行区别。
 bit07~bit00  8位为 "区别序号" 区,是区分命令的命令顺序序号。

如果每个cmd都要一个一个算着位然后生成,肯定是件费力不讨好的事情,聪明的程序员肯定都不愿意这么干,所以内核定义了四个函数用来生产cmd。这四个函数是:
_IO、_IOR、_IOW、_IOWR。
我们来看_IO,其余的相似。

#define _IO(type,nr)             _IOC(_IOC_NONE,(type),(nr ),0) 

type其实对应的是cmd中的魔数,用来标识命令的类型,注意不同的命令可以使用相同的魔数,那么魔数相同系统怎么区分两条命令呢,这时就要用第二个参数nr了,这个参数对应cmd中的区别序号,这两个参数一起来标识一条命令。这两个参数都是8位。

下面来看_IOC:

#define _IOC(dir,type,nr,size) \

       (((dir)  << _IOC_DIRSHIFT) | \

        ((type) << _IOC_TYPESHIFT) | \

        ((nr)   << _IOC_NRSHIFT) | \

        ((size) << _IOC_SIZESHIFT))


可以看出来,这个宏主要是对_IOC的几个参数进行移位,具体移多少位呢?
看看下面就知道了。

#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_NRBITS          8                               //序数(number)字段的字位宽度,8bits

#define         _IOC_TYPEBITS      8                               //幻数(type)字段的字位宽度,8bits

#define         _IOC_SIZEBITS       14                              //大小(size)字段的字位宽度,14bits

#define         _IOC_DIRBITS         2                               //方向(direction)字段的字位宽度,2bits


所以, (dir)  << _IOC_DIRSHIFT)  表是 dir 往左移 30 位,即移到 bit31~bit30 两位上,得到方向(读写)的属性;
             (size) << _IOC_SIZESHIFT)  位左移 16 位得到“数据大小”区;
             (type) << _IOC_TYPESHIFT)  左移 8位得到"魔数区" ;
            (nr)   << _IOC_NRSHIFT)       左移 0 位( bit7~bit0) 。

这样就得到了各个部分的值。
我们只要利用这个宏,并且对相应的参数赋值,就可以构造出一个cmd来了。、

现在我们来构造一个:

#define MY_MAGIC   0xdf
#define MY_CMD _IOW(MY_MAGIC, 2 , int)

于是有命令MY_CMD各组成字段dirsizetypenr)分别为:01(=_IOC_WRITE ),00 0000 0000 0100(=sizeof(unsigned int)),1101 1111(=MY_CMD_MAGIC),0000 0000(=0)。用十六进制表示即0x4004df00。这个32 bits"整数"就是该命令的编号(LDD3原文是command number),也就是命令MY_CMD在系统中的"身份证号码"了!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值