日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2024/9/21 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 构造IOCTL命令的学习心得-----_IO, _IOR, _IOW, _IOWR 幻数的理解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在編寫ioctl代碼之前,需要選擇對應不同命令的編號。為了防止對錯誤的設備使用正確的命令,命令號應該在系統范圍內唯一,這種錯誤匹配并不是不會發生,程序可能發現自己正在試圖對FIFOaudio等這類非串行設備輸入流修改波特率,如果每一個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.hDoucumention/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?宏將dirtypenrsize四個參數組合成一個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各組成字段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在系統中的"身份證號碼"了!

?????為什么上面我用"身份證號碼"作比喻呢?眾所周知,一個國家里所有公民的身份證號都是各不相同的。身份證編號有一定的規則:即把身份證號劃分成幾個字段,各段位數可不等,每個字段編碼都有它的實際意義,例如我們現在用的的身份證前N位(不記得具體是多少了)表示一個具體的省、市、等地區,不同地區的人該字段肯定不同了;另外有個表示出生年月的字段(據說以前整個號碼最后一位偶數表示男性,奇數表示女性,現在貌似沒這個規則了,此為題外話)。

?????類似地,我們要為系統里所有的IOCTL命令編號。我們身份證用的是15(上一代是18位)位十進制數編碼(最后一位可能是拉丁字母);我們用32位二進制數為IOCTL命令編碼,把它劃分成4個字段,每段也有它的實際意義。要保證每個命令編號為系統唯一,主要靠命令的typenr字段。我們稱type字段內容為magic number,即幻數,它表示命令的類型

????看到上段紅色這句話,我想可能有細心的人會問:"那么命令到底有哪些類型呢?"老實說我也不知道正確答案。大概因為大家都知道基本數據類型有整形、浮點型、字符型...人的性格類型有外向型、內向型...這些我們常見的類型都是可以用文字來枚舉描述的,所以潛意識就覺得有類型就應該有文字可描述吧。回到正題,我想每個magic number,就像上面的宏定義中:#define MY_CMD_MAGIC 0xdfMY_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命令的宏,這其中的參數nrIOCTL命令編號中的一個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 幻数的理解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。