kernel 2.6.35 及之前的版本中struct file_operations 一共有3个ioctl :
ioctl,unlocked_ioctl和compat_ioctl
现在只有unlocked_ioctl和compat_ioctl 了

在kernel 2.6.36 中已经完全删除了struct file_operations 中的ioctl 函数指针,取而代之的是unlocked_ioctl 。

这个指针函数变了之后最大的影响是参数中 少了inode , 不过这个不是问题,因为用户程序中的ioctl对应的系统调用接口没有变化,所以用户程序不需要改变,一切都交给内核处理了,如果想在unlocked_ioctl中获得inode 等信息可以用如下方法:
struct inode *inode = file->f_mapping->host;
struct block_device *bdev = inode->i_bdev;
struct gendisk *disk = bdev->bd_disk;
fmode_t mode = file->f_mode;
struct backing_dev_info *bdi;
参数变化参考 file_operations这个结构体,最重要的是这个cmd参数,这个变化很大。

在编写ioctl代码之前,需要选择对应不同命令的编号。为了防止对错误的设备使用正确的命令,命令号应该在系统范围内唯一,这种错误匹配并不是不会发生,程序可能发现自己正在试图对FIFO和audio等这类非串行设备输入流修改波特率,如果每一个ioctl命令都是唯一的,应用程序进行这种操作时就会得到一个EINVAL错误,而不是无意间成功地完成了意想不到的操作。

在驱动程序里, ioctl() 函数上传送的变量 cmd 是应用程序用于区别设备驱动程序请求处理内容的值。cmd除了可区别数字外,还包含有助于处理的几种相应信息。 cmd的大小为 32位,共分 4 个域:

bit31~bit30  2位为 “区别读写” 区,作用是区分是读取命令还是写入命令。
        bit29~bit15  14位为 "数据大小" 区,表示 ioctl() 中的 arg 变量传送的内存大小。
        bit20~bit08  8位为 “魔数"(也称为"幻数")区,这个值用以与其它设备驱动程序的 ioctl 命令进行区别。
        bit07~bit00  8位为 "区别序号" 区,是区分命令的命令顺序序号。

像命令码中的 “区分读写区” 里的值可能是 _IOC_NONE (0值)表示无数据传输,_IOC_READ (读), _IOC_WRITE (写) , _IOC_READ|_IOC_WRITE (双向)。
内核定义了 _IO() , _IOR() , IOW() 和 _IOWR() 这 4 个宏来辅助生成上面的 cmd 。下面分析 _IO() 的实现,其它的类似。

要按Linux内核的约定方法为驱动程序选择ioctl编号,应该首先看看include/asm/ioctl.h和Doucumention/ioctl-number.txt这两个文件。头文件定义了要使用的位字段:类型(幻数)、序数、传送方向以及参数大小等。ioctl-number.txt文件中罗列了内核所使用的幻数,选择自己的幻数要避免和内核冲突。以下是对include/asm/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

/*

* Direction bits.

*/

#define _IOC_NONE     0U     //没有数据传输

#define _IOC_WRITE   1U     //向设备写入数据,驱动程序必须从用户空间读入数据

#define _IOC_READ     2U     //从设备中读取数据,驱动程序必须向用户空间写入数据

/*

*_IOC 宏将dir,type,nr,size四个参数组合成一个cmd参数

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

(((dir)  << _IOC_DIRSHIFT) | \

((type) << _IOC_TYPESHIFT) | \

((nr)   << _IOC_NRSHIFT) | \

((size) << _IOC_SIZESHIFT))

/*

* used to create numbers

*/

//构造无参数的命令编号

#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))

/*

*used to decode ioctl numbers..

*/

//从命令参数中解析出数据方向,即写进还是读出

#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)

/* ...and for the drivers/sound files... */

#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)

以上内容转自:article.phpfans.net/

我的理解:

假如我定义了一个命令MY_CMD:

#define MY_CMD_MAGIC 0xdf              //type字段,由于字段宽度为8 bits,所以不能大于0xff

#define MY_CMD  _IOW(MY_CMD_MAGIC,0,unsigned int)

于是有命令MY_CMD各组成字段(dir,size,type,nr)分别为: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在系统中的"身份证号码"了!

为什么上面我用"身份证号码"作比喻呢?众所周知,一个国家里所有公民的身份证号都是各不相同的。身份证编号有一定的规则:即把身份证号划分成几个字段,各段位数可不等,每个字段编码都有它的实际意义,例如我们现在用的的身份证前N位(不记得具体是多少了)表示一个具体的省、市、等地区,不同地区的人该字段肯定不同了;另外有个表示出生年月的字段(据说以前整个号码最后一位偶数表示男性,奇数表示女性,现在貌似没这个规则了,此为题外话)。

类似地,我们要为系统里所有的IOCTL命令编号。我们身份证用的是15(上一代是18位)位十进制数编码(最后一位可能是拉丁字母);我们用32位二进制数为IOCTL命令编码,把它划分成4个字段,每段也有它的实际意义。要保证每个命令编号为系统唯一,主要靠命令的type和nr字段。我们称type字段内容为magic number,即幻数,它表示命令的类型。

看到上段红色这句话,我想可能有细心的人会问:"那么命令到底有哪些类型呢?"老实说我也不知道正确答案。大概因为大家都知道基本数据类型有整形、浮点型、字符型...人的性格类型有外向型、内向型...这些我们常见的类型都是可以用文字来枚举描述的,所以潜意识就觉得有类型就应该有文字可描述吧。回到正题,我想每个magic number,就像上面的宏定义中:#define MY_CMD_MAGIC 0xdf,MY_CMD_MAGIC就是类型名了吧!不知道我的想法对不对?!反正大家知道一个magic number就对应唯一一种命令类型就是了。LDD原文中有一段:

type
The magic number. Just choose one number (after consulting ioctl-number.txt)
and use it throughout the driver. This field is eight bits wide (_IOC_TYPEBITS).

原文是说type的内容叫幻数(magic number),强调它是一个8位二进制数(number)。

因此不同type的命令就有不同的magic number,因此命令编码自然就不同了。但如果两个命令type相同,它们的magic number就相同,于是就不能仅靠type字段区分了。于是nr字段就起作用了:

number
The ordinal (sequential) number. It's eight bits (_IOC_NRBITS) wide.

nr(number)即序号,一般地我们从0开始编号。由于nr字段为8位二进制数,所以nr的取值范围为0~255。同一种type的命令每个nr值对应唯一一个命令。

其他两个字段在这里不作深究。

值得一提的是LDD3里这么一段话:

The header also defines macros that may be used in your driver to decode the num-
bers: _IOC_DIR(nr), _IOC_TYPE(nr), _IOC_NR(nr), and _IOC_SIZE(nr). We won't go
into any more detail about these macros because the header file is clear, and sample
code is shown later in this section.

根据ioctl.h文件的定义,很明显_IOC_DIR(nr), _IOC_TYPE(nr), _IOC_NR(nr),_IOC_SIZE(nr)这几个宏里面的参数就是一个IOCTL命令,即一个32位的二进制数,而文件中参数居然用nr表示!当然用nr表示不是逻辑上的错误。但是别忘了文件中还有_IOW(type,nr,size)这样的定义IOCTL命令的宏,这其中的参数nr是IOCTL命令编号中的一个nr字段,一个8位的二进制数!我想很多新人都会对此产生莫大的疑惑!所以我认为把_IOC_DIR(nr), _IOC_TYPE(nr), _IOC_NR(nr),_IOC_SIZE(nr)这几个解码宏的参数改用cmd表示更恰当!
但是,我知道这不是LDD3作者的错,因为内核头文件里面也是这么表示的。我想内核开发者不可能没意识到这个问题。因此,我猜测这其中肯定有个历史原因:大概以前版本的命令不管type是否一样,nr字段的值都是唯一的,于是仅靠nr字段就可以解码出一个IOCTL命令的其他字段吧?!但即使这样_IOC_DIR(nr), _IOC_TYPE(nr), _IOC_NR(nr),_IOC_SIZE(nr)也没必要保留这种写法啊!到底谁可以告诉我真相?

LDD3没有对_IOC_DIR(nr), _IOC_TYPE(nr), _IOC_NR(nr),_IOC_SIZE(nr)里面的nr作任何解释,只是实例中有如下用法:

if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC) return -ENOTTY;
if (_IOC_NR(cmd) > SCULL_IOC_MAXNR) return -ENOTTY;

可见那个nr参数完全就是我所说的32位的IOCTL命令编码。靠,既然这样好歹也对着个confusion作一下简单的解释啊!如果LDD3一书那"既不承认,也不否认"的暧昧态度让我真让我哭笑不得的话,那么国内某书(具体哪本我就不说了)简直令我抓狂,我摘书中的两段话如下:

_IO(type,nr):定义一个没有数据传输的命令编号。type为幻数,nr为命令编号。...
...
_IOC_DIR(nr): 获得命令编号的命令传输方向(direction)。这个宏的参数就是命令编号。

晕了没有?红色标记处的nr分明就是命令编号的nr(即序号)字段嘛!真XX@#¥&*$!

新内核版本ioctl的变化 _IO, _IOR, _IOW, _IOWR 幻数的理解相关推荐

  1. 构造IOCTL命令的学习心得-----_IO, _IOR, _IOW, _IOWR 幻数的理解

    在编写ioctl代码之前,需要选择对应不同命令的编号.为了防止对错误的设备使用正确的命令,命令号应该在系统范围内唯一,这种错误匹配并不是不会发生,程序可能发现自己正在试图对FIFO和audio等这类非 ...

  2. 构造IOCTL命令的学习心得-----_IO, _IOR, _IOW, _IOWR 幻数的理解

    在编写ioctl代码之前,需要选择对应不同命令的编号.为了防止对错误的设备使用正确的命令,命令号应该在系统范围内唯一,这种错误匹配并不是不会发生,程序可能发现自己正在试图对FIFO和audio等这类非 ...

  3. IOCTL命令-_IO, _IOR, _IOW, _IOWR 幻数的理解

    有一年半时间没有一行一行的写过linux驱动程序了,有的东西会发现遗忘了,今天就当是复习一下吧. 在编写ioctl代码之前,需要选择对应不同命令的编号.为了防止对错误的设备使用正确的命令,命令号应该在 ...

  4. linux 内核驱动编程 简单例子 与_IO, _IOR, _IOW, _IOWR 宏解析

    一._IO, _IOR, _IOW, _IOWR 宏的用法与解析 在驱动程序里, ioctl() 函数上传送的变量 cmd 是应用程序用于区别设备驱动程序请求处理内容的值.cmd除了可区别数字外,还包 ...

  5. _IO, _IOR, _IOW, _IOWR 宏用法解析

    _IO, _IOR, _IOW, _IOWR 宏的用法与解析 在驱动程序里, ioctl() 函数上传送的变量 cmd 是应用程序用于区别设备驱动程序请求处理内容的值.cmd除了可区别数字外,还包含有 ...

  6. _IO,_IOR,_IOW,_IOWR

    _IO,_IOR,_IOW,_IOWR 宏的用法与解析< DOCTYPE HTML PUBLIC -WCDTD HTML TransitionalEN> _IO, _IOR, _IOW, ...

  7. 转:linux内核驱动中_IO, _IOR, _IOW, _IOWR 宏的用法与解析

    在驱动程序里, ioctl() 函数上传送的变量 cmd 是应用程序用于区别设备驱动程序请求处理内容的值.cmd除了可区别数字外,还包含有助于处理的几种相应信息. cmd的大小为 32位,共分 4 个 ...

  8. Linux内核中_IO,_IOR,_IOW,_IOWR宏的用法与解析

    在驱动程序里, ioctl()函数上传送的变量 cmd是应用程序用于区别设备驱动程序请求处理内容的值.cmd除了可区别数字外,还包含有助于处理的几种相应信息. cmd的大小为 32位,共分 4 个域: ...

  9. 驱动中有关ioctl[宏]_IO, _IOR, _IOW, _IOWR 宏的用法与解析

    在驱动程序里, ioctl() 函数上传送的变量 cmd 是应用程序用于区别设备驱动程序请求处理内容的值.cmd除了可区别数字外,还包含有助于处理的几种相应信息. cmd的大小为 32位,共分 4 个 ...

最新文章

  1. js,jquery 根据对象某一属性进行排序
  2. Linux安装软件时缺少依赖包的简单较完美解决方法!
  3. python培训出来的有公司要吗-参加Python培训后真的能找到工作吗?上海Python培训...
  4. python中的装饰器有哪些-Python中的@函数装饰器到底是什么?
  5. 过河 2005年NOIP全国联赛提高组(离散化+dp)
  6. 离散数学范式c语言实验报告,离散数学实验报告-利用真值表法求主析取范式及主合取范式的实现...
  7. 面试mysql中怎么创建索引_阿里面试:MySQL如何设计索引更高效?
  8. 热带雨林绘画软件测试,儿童画教程|色彩练习与思维创想——好热闹的热带雨林!...
  9. VARIANT变体类型数据
  10. 一款无需写任何代码即可一键生成前后端代码的开源工具
  11. myeclipse下载_资源共享:常用的编程软件下载链接分享
  12. 【Magento】magento 1.7.0.2 paypal支付方式,网站标准支付
  13. 2021年最新独立版橙色去水印微信小程序-更新2021.8.31
  14. vivo手机打开手机测试页面,查看电池损耗代码
  15. C语言谭浩强(第五版)答案6、8章
  16. 爬取网易云音乐50000+首歌曲
  17. python html做界面_如何用EEL(python库)打开一个新的HTML页面?
  18. win11恢复win10的右击菜单,自测可用
  19. 全球多地爆发!传染性极强!鄂尔多斯人近期外出一定要注意…
  20. 国密SM3加密算法工具类(非对称)

热门文章

  1. this和super的区别:
  2. 计算机设备管理器驱动,什么我的电脑设备管理器里没有非即插即用驱动程序着一项...
  3. 区位码、国标码、机内码、GBK
  4. NotePad++ 查询结果窗口不见了,找回办法
  5. 【嵌入式】旋转编码器原理
  6. 安装matlab 2016b(Windows11系统)报错
  7. FPGA实现SPI协议
  8. DjangoChannels_vue前端搭建简易版的网页聊天功能
  9. 构建 PHP运行环境
  10. mybatis错误:bad SQL grammar [];error occurred while setting parameters;involve defaultParameterMap处理