1. 程式人生 > >構造IOCTL命令的學習心得-----_IO, _IOR, _IOW, _IOWR 幻數的理解

構造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)

#defineIOC_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(即序號)欄位嘛!真[email protected]#&*$對國內的書越來越失望了!沒有多少自己的東西就罷了!連翻譯的都翻不好!誤人子弟!~

相關推薦

構造IOCTL命令學習心得-----_IO, _IOR, _IOW, _IOWR 理解

在編寫ioctl程式碼之前,需要選擇對應不同命令的編號。為了防止對錯誤的裝置使用正確的命令,命令號應該在系統範圍內唯一,這種錯誤匹配並不是不會發生,程式可能發現自己正在試圖對FIFO和audio等這類非序列裝置輸入流修改波特率,如果每一個ioctl命令都是唯一的,應用程式

_IO, _IOR, _IOW, _IOWR 宏的用法與解析

內存 返回值 bits 分辨 上傳 正是 ron 了解 asm 今天在寫字符驅動驗證程序的時候要用到ioctl函數,其中有一個cmd參數,搞了半天也不了解是什麽意思,那個cmd還有什麽命令碼了什麽的,還好google下,覺得這篇文章寫的不錯,就轉來看看:在驅動程序裏, io

Linux內核中_IO,_IOR,_IOW,_IOWR宏的用法

應用程序 round 判斷 fine 包含 區分 font 用法 原因 #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) #define _IOR(type,nr,size) _IOC(_IOC_REA

openstack學習心得:keystone 常用命令(M版)

face pan sig rom 用戶 目錄 change 創建 email 查看用戶列表 openstack user list 查看用戶具體信息 usage: openstack user show [-h] [-f

linux find命令用法 linux學習心得體會

txt linux ali 學習心得 訪問 lin atime str 輸出 find命令用來在指定目錄下查找文件的命令,和locate不同,find命令是實時精確查找,不做模糊匹配,但是可以使用通配符查找,相對更精確。任何位於參數之前的字符串都將被視為欲查找的目錄名。如果

linux命令列引數解析學習心得

轉載出處:blog.csdn.net/bailyzheng/article/details/8048733 最近用到一個命令列工具,之前也一直說想把命令列引數解析看一下,今天算是做一個小的總結。 命令列引數解析分類:單個字元的引數(-a -b),還有字串引數(--vide

STL初探——第一級配置 __malloc_alloc_template的學習心得

exception template 定義 stl 空間 似的 strong cep 對象   在第一級配置器中,一開始就定義了內存分配出錯的宏接口,如下: #ifndef __THROW_BAD_ALLOC # if defined(__STL_NO_BAD_ALLO

STL初探——第二級配置器 __default_alloc_template的學習心得

空間配置 def 管理 使用 函數 效率 需求 typename []   SGI STL 第二級配置器使用的是memory pool,即內存池,相比較於第一級空間配置器,第二級空間配置器多了許多限制,主要是為了防止申請小額區塊過多而造成內存碎片。當然小額區塊在配置時實際上

linux命令學習(6):ps命令

bytes 釋放 ice cti width kthread hellip 名稱 pts Linux中的ps命令是Process Status的縮寫。ps命令用來列出系統中當前運行的那些進程。ps命令列出的是當前那些進程的快照,就是執行ps命令的那個時刻的那些進程,如果想要

與WCAG相關的一些學習心得

mis text 閃爍 wave 什麽 inf blog 行動 ... 1.什麽是 WCAG? WCAG全稱Web Content Accessibility Guidelines 網頁內容無障礙瀏覽準則,簡單的說就是為了方便殘障人士(包括低視患者,盲人,聾人,學習障礙,行

HashMap 學習心得

常見 常常 .cn 寫代碼 出現 等於 心得 cnblogs png 1.構造 HashMap 底層數據結構線性數組,HashMap有一個靜態內部類Entry,Entry有四個屬性,key,value,next,hash Entry就是HashMap鍵值對實現的一個基礎b

構建之法第六章學習心得

效率 用戶 當前 決定 復雜 技術 原則 核心 back 這周我學習了構建之法第六章敏捷流程,本章主要介紹了敏捷流程及其原則,Backlog、Burn-down、Sprint、Scrum方法論。以及什麽時候選擇敏捷的開發方法,什麽時候選擇其他方法。.敏捷開發的原則是盡早並持

構建之法第七章學習心得

思想 studio 開發 咨詢服務 生活 int bsp partner har 構建之法第七章學習心得 這周我學習了構建之法第七章MSF的介紹。MSF有9個基本原則,針對信息共享,團隊內部運營,市場,還有客戶。同樣是強調效率,人性,靈活,還有前景。 MSF對信息共享和溝通

關於ES6學習心得

reference blog 語法 -1 聲明變量 循環 需要 變量 tro 已經不記得什麽適合開始學習阮老師的《ECMAScript6入門》,中途一直斷斷續續的看,到目前了也是沒完全看完,很是慚愧!剛好借“兒童節”寫個小結。 看來我真的不適合自學,一直都是理論看起來是枯燥

Verilog學習心得(一)

== padding 並行執行 printf enter pad port 網線 學習心得 ?         作為一個萌新,第一次讓我學習Verilog我是拒絕的,連數電都沒有學習過學Verilog真是一種折磨...不過自己選擇的路不管怎麽樣都要走下去.

第二組第八周學習心得

自己 發現 gui 一個 知識 進步 編程 比較 通過 通過幾周的學習,我們對構建之法的知識有了一定的掌握,能在大腦裏對軟工編程有一定的認識,這已經有了一定的進步了,但是還不夠,還需要繼續學習,去形成一個比較系統的認識。對於之前的編程作業,我們發現還是有一定的難

Linux命令學習(17):ifconfig命令

廣播 參考 vip 統計 協議 cnblogs 還需要 pro 網絡 版權聲明更新:2017-05-22博主:LuckyAlan聯系:[email protected]/* */聲明:吃水不忘挖井人,轉載請註明出處! 1 文章介紹 我們知道,在windows中,

QT樹形控件學習心得

目錄 樹形控件 亂碼 行程 中文 rom 聯動 程序 事件 1.中文亂碼問題。QT使用的是UTF-8,而vs使用的是GB2312。所以會出現亂碼。解決方法是 QString::fromLocal8Bit("中文") 。 2.vs編程成功後可以運行程序,但是單獨運行exe時,

python學習心得

sta true red alt 用戶輸入 generator start rgs call 一,高級特性: 1,切片:[start:stop:step] >>>l=range(6) >>>l[3,-1,2] resulte is [3

Redis命令學習-?Transaction(事務)

list lib targe 失敗 reference ret con margin bold ?DISCARD ?DISCARD :取消事務,放棄運行事務塊內的全部代碼。假設在使用WATCH命令監視某個key。則取消監視,等同於UNWATCH。 ?