日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
Linux的ioctl命令及其相關(guān)用法詳解(linux的ioctl)

Linux操作系統(tǒng)是廣泛使用的一種操作系統(tǒng),由于其開(kāi)源、穩(wěn)定、安全等特點(diǎn),被廣泛應(yīng)用于服務(wù)器、桌面、移動(dòng)設(shè)備等領(lǐng)域。Linux系統(tǒng)提供了豐富的命令集,其中ioctl命令是其中之一,本文將就linux的ioctl命令及其相關(guān)用法進(jìn)行詳解。

成都創(chuàng)新互聯(lián)主要業(yè)務(wù)有網(wǎng)站營(yíng)銷策劃、成都做網(wǎng)站、成都網(wǎng)站制作、微信公眾號(hào)開(kāi)發(fā)、小程序定制開(kāi)發(fā)H5頁(yè)面制作、程序開(kāi)發(fā)等業(yè)務(wù)。一次合作終身朋友,是我們奉行的宗旨;我們不僅僅把客戶當(dāng)客戶,還把客戶視為我們的合作伙伴,在開(kāi)展業(yè)務(wù)的過(guò)程中,公司還積累了豐富的行業(yè)經(jīng)驗(yàn)、成都營(yíng)銷網(wǎng)站建設(shè)資源和合作伙伴關(guān)系資源,并逐漸建立起規(guī)范的客戶服務(wù)和保障體系。 

一、概述

ioctl是指I/O控制(input/output control)命令,它是在文件描述符上執(zhí)行控制操作的一種通用方法,可以用于設(shè)備、進(jìn)程間通信、網(wǎng)絡(luò)等多種方面。在Linux系統(tǒng)中,ioctl函數(shù)由系統(tǒng)調(diào)用提供,可以對(duì)文件描述符執(zhí)行一些控制操作,是一種通用的、靈活的系統(tǒng)調(diào)用。ioctl命令通常用于驅(qū)動(dòng)程序和設(shè)備之間的通信,它可以設(shè)置或訪問(wèn)特殊設(shè)備或文件的屬性,通過(guò)讀取或?qū)懭胩囟ǖ目刂萍拇嫫鱽?lái)實(shí)現(xiàn)對(duì)設(shè)備的控制。

二、用法

ioctl命令的語(yǔ)法如下:

“`c

int ioctl(int fd, unsigned long cmd, …);

“`

參數(shù)說(shuō)明:

– fd:文件描述符

– cmd:控制命令,用于指定控制要執(zhí)行的操作

– …:可選參數(shù),用于傳遞指定命令所需的參數(shù)

ioctl命令的使用場(chǎng)景很多,我們可以通過(guò)ioctl命令來(lái)獲取或設(shè)置特定設(shè)備的信息、修改設(shè)備的狀態(tài)等。下面將介紹一些常用的控制命令及其使用方法。

1. FIONBIO

FIONBIO命令用于設(shè)置或獲取文件描述符的非阻塞模式。如果在非阻塞模式下打開(kāi)文件,則I/O操作不會(huì)被阻斷。在阻塞模式下,則會(huì)一直等待操作完成。

“`c

int flag = 1; //設(shè)置非阻塞模式

ioctl(sockfd, FIONBIO, &flag); //使sockfd為非阻塞模式

“`

2. FIONREAD

FIONREAD命令用于獲取接收緩沖區(qū)中可讀的字節(jié)數(shù)。

“`c

int nread;

ioctl(sockfd, FIONREAD, &nread); //獲取接收緩沖區(qū)中可讀的字節(jié)數(shù)

“`

3. SIOCGIFCONF

SIOCGIFCONF命令用于獲取系統(tǒng)中所有網(wǎng)絡(luò)接口的列表信息。

“`c

struct ifconf ifconf;

struct ifreq ifr[10];

ifconf.ifc_len = sizeof(ifr);

ifconf.ifc_req = ifr;

ioctl(sockfd, SIOCGIFCONF, &ifconf); //獲取系統(tǒng)中所有網(wǎng)絡(luò)接口的列表信息

“`

4. SIOCGIFADDR

SIOCGIFADDR命令用于獲取指定網(wǎng)絡(luò)接口的IP地址。

“`c

struct ifreq ifr;

struct sockaddr_in sin;

char * interface_name = “eth0”;

memset(&ifr, 0, sizeof(ifr));

strcpy(ifr.ifr_name, interface_name);

ioctl(sockfd, SIOCGIFADDR, &ifr); //獲取指定網(wǎng)絡(luò)接口的IP地址

memcpy(&sin, &ifr.ifr_addr, sizeof(sin));

“`

5. SIOCSIFADDR

SIOCSIFADDR命令用于設(shè)置指定網(wǎng)絡(luò)接口的IP地址。

“`c

struct ifreq ifr;

struct sockaddr_in sin;

char * interface_name = “eth0”;

memset(&ifr, 0, sizeof(ifr));

strcpy(ifr.ifr_name, interface_name);

sin.sin_family = AF_INET;

sin.sin_addr.s_addr = inet_addr(“192.168.1.100”);

memcpy(&ifr.ifr_addr, &sin, sizeof(sin));

ioctl(sockfd, SIOCSIFADDR, &ifr); //設(shè)置指定網(wǎng)絡(luò)接口的IP地址

“`

6. FIONCLEX

FIONCLEX命令用于關(guān)閉指定的文件描述符的文件描述符標(biāo)志FD_CLOEXEC。

“`c

int fd = open(“file.txt”, O_RDWR);

ioctl(fd, FIONCLEX, 0); //關(guān)閉fd的FD_CLOEXEC標(biāo)志

“`

7. TIOCGWINSZ

TIOCGWINSZ命令用于獲取終端窗口的大小。

“`c

struct winsize ws;

ioctl(0, TIOCGWINSZ, &ws); //獲取終端窗口的大小

printf(“columns=%d, lines=%d\n”, ws.ws_col, ws.ws_row);

“`

8. TIOCSWINSZ

TIOCSWINSZ命令用于設(shè)置終端窗口的大小。

“`c

struct winsize ws;

ws.ws_col = 80;

ws.ws_row = 24;

ioctl(0, TIOCSWINSZ, &ws); //設(shè)置終端窗口的大小為80*24

“`

三、

本文詳細(xì)介紹了Linux操作系統(tǒng)中的ioctl命令及其相關(guān)用法,包括常用的控制命令和示例代碼,讀者可以根據(jù)需要選擇相應(yīng)的命令進(jìn)行使用。作為L(zhǎng)inux系統(tǒng)開(kāi)發(fā)人員,熟練掌握ioctl命令可幫助我們更好地完成對(duì)設(shè)備的控制操作,提高系統(tǒng)的運(yùn)行效率。

相關(guān)問(wèn)題拓展閱讀:

  • ioctl()函數(shù)的參數(shù)和作用

ioctl()函數(shù)的參數(shù)和作用

ioctl 接口

大部分驅(qū)動(dòng)需要 — 除了讀寫(xiě)設(shè)備的能力 — 通過(guò)設(shè)備驅(qū)動(dòng)進(jìn)行各種硬件控制的能力. 大部分設(shè)備可進(jìn)行超出簡(jiǎn)單的數(shù)據(jù)中讓傳輸之外的操作; 用戶空間必須常常能夠請(qǐng)求, 例如, 設(shè)備鎖上它的門(mén), 彈出它的介質(zhì), 報(bào)告錯(cuò)誤信息, 改變波特率, 或者自我銷毀. 這些操作常常通過(guò) ioctl 方法來(lái)支持, 它通過(guò)相同名子的系統(tǒng)調(diào)用來(lái)實(shí)現(xiàn).

在用戶空間, ioctl 系統(tǒng)調(diào)用有下面的原型:

int ioctl(int fd, unsigned long cmd, …);

這個(gè)原型由于這些點(diǎn)而凸現(xiàn)于 Unix 系統(tǒng)調(diào)用列表, 這些點(diǎn)常常表示函數(shù)有數(shù)目不定的參數(shù). 在實(shí)際系統(tǒng)中, 但是, 一個(gè)系統(tǒng)調(diào)用不能真正有變數(shù)目的參數(shù). 系統(tǒng)調(diào)用必須有一個(gè)很好定義的原型, 因?yàn)橛脩舫绦蚩纱嫒∷鼈冎荒芡ㄟ^(guò)硬件的”門(mén)”. 因此, 原型中的點(diǎn)不表示一個(gè)變數(shù)目的參數(shù), 而是一個(gè)單個(gè)可選的參數(shù), 傳統(tǒng)上標(biāo)識(shí)為 char *argp. 這些點(diǎn)在那里只是為了阻止在編譯時(shí)的類型檢查. 第 3 個(gè)參數(shù)的實(shí)際特點(diǎn)依賴所發(fā)出的特定的控制命令( 第 2 個(gè)參數(shù) ). 一些命令不用參數(shù), 一些用一個(gè)整數(shù)值, 以及一些使用指向其他數(shù)據(jù)的指針. 使用一個(gè)指針是傳遞任意數(shù)據(jù)到 ioctl 調(diào)用的方法; 設(shè)備接著可與用戶空間交換任何數(shù)量的數(shù)據(jù).

ioctl 調(diào)用的非結(jié)構(gòu)化特性使它在內(nèi)核開(kāi)發(fā)者中失寵或悶. 每個(gè) ioctl 命令, 基本上, 是一個(gè)單獨(dú)的, 常常無(wú)文檔的系統(tǒng)調(diào)用, 并且沒(méi)有方法以任何類型的全面的方式核查這些調(diào)用. 也難于使非結(jié)構(gòu)化的 ioctl 參數(shù)在所有系統(tǒng)上一致工作; 例如, 考慮運(yùn)衫培彎行在 32-位模式的一個(gè)用戶進(jìn)程的 64-位 系統(tǒng). 結(jié)果, 有很大的壓力來(lái)實(shí)現(xiàn)混雜的控制操作, 只通過(guò)任何其他的方法. 可能的選擇包括嵌入命令到數(shù)據(jù)流(本章稍后我們將討論這個(gè)方法)或者使用虛擬文件系統(tǒng), 要么是 sysfs 要么是設(shè)備特定的文件系統(tǒng). (我們將在 14 章看看 sysfs). 但是, 事實(shí)是 ioctl 常常是最容易的和最直接的選擇,對(duì)于真正的設(shè)備操作.

ioctl 驅(qū)動(dòng)方法有和用戶空間版本不同的原型:

int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);

因?yàn)橛脩魧尤鐢《x弊譽(yù)它是租橡段個(gè)變參函數(shù)

ioctl (int __fd, unsigned long int __request, …)

跟printf似的

ioctl 接口

大部分驅(qū)動(dòng)需要 — 除了讀寫(xiě)設(shè)備的能力 — 通過(guò)設(shè)備驅(qū)動(dòng)進(jìn)行各種硬件控制的能力. 大部分設(shè)備可進(jìn)行超出簡(jiǎn)單的數(shù)據(jù)傳輸之外的操作; 用戶空間必須常常能夠請(qǐng)求, 例如, 設(shè)備鎖上它的門(mén), 彈出它的介質(zhì), 報(bào)告錯(cuò)誤信息, 改變波特率, 或者自我銷毀. 這些操作常常通過(guò) ioctl 方法來(lái)支持, 它通過(guò)相同名子的系統(tǒng)調(diào)用來(lái)實(shí)現(xiàn).

在用戶空間, ioctl 系統(tǒng)調(diào)用有下面的原型:

int ioctl(int fd, unsigned long cmd, …);

這個(gè)原型由于這些點(diǎn)而凸現(xiàn)于 Unix 系統(tǒng)鬧戚調(diào)用列表, 這些點(diǎn)常常表示函數(shù)有數(shù)目不定的參數(shù). 在實(shí)際系統(tǒng)中, 但是, 一個(gè)系統(tǒng)調(diào)用不能真正有變數(shù)目的參數(shù). 系統(tǒng)調(diào)用必須有一個(gè)很好定義的原型, 因?yàn)橛脩舫绦蚩纱嫒∷鼈冎荒芡ㄟ^(guò)硬件的”門(mén)”. 因此, 原型中的點(diǎn)不表示一個(gè)變數(shù)目的參數(shù), 而是一個(gè)單個(gè)可選的參數(shù), 傳統(tǒng)上標(biāo)識(shí)為 char *argp. 這些點(diǎn)在那里只是為了阻止在編譯時(shí)的類型檢查. 第 3 個(gè)參數(shù)的實(shí)際特點(diǎn)依賴所發(fā)出的特定的控制命令( 第 2 個(gè)參數(shù) ). 一些命令不用參數(shù), 一些用一個(gè)整數(shù)值, 以及一些使用指向其他數(shù)據(jù)的指針. 使用一個(gè)指針是傳遞任意數(shù)據(jù)到 ioctl 調(diào)用的方法; 設(shè)備接著可與用戶空間交換任何數(shù)量的數(shù)據(jù).

ioctl 調(diào)用的非結(jié)構(gòu)化特性使它在內(nèi)核開(kāi)發(fā)者中失寵. 每個(gè) ioctl 命令, 基本上, 是一個(gè)單獨(dú)的, 常常無(wú)文檔的系統(tǒng)調(diào)用, 并且沒(méi)有方法以任何類型的全面的方式核查這些調(diào)用. 也難于使非結(jié)構(gòu)化的 ioctl 參數(shù)在所有系統(tǒng)上一致工作; 例如, 考慮運(yùn)行在 32-位模式的一個(gè)用戶進(jìn)程的 64-位 系統(tǒng). 結(jié)果, 有很大的壓力來(lái)實(shí)現(xiàn)混雜的控制操作, 只通過(guò)任何其他的方法. 可能的選擇包括嵌入命令到數(shù)據(jù)流(本章稍后我們將討論這個(gè)方法)或者使用虛擬文件系統(tǒng), 要么是 sysfs 要么是設(shè)備特定的文件系統(tǒng). (我們將在 14 章看看 sysfs). 但是, 事實(shí)嫌物是 ioctl 常常是最容易的和最直接的選擇,對(duì)于真正的設(shè)備操作.

ioctl 驅(qū)動(dòng)方法有和用戶空間版本不同的原型:

int (*ioctl) (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);

inode 和 filp 指針是對(duì)應(yīng)應(yīng)用程序傳遞的文件描述符 fd 的值, 和傳遞給 open 方法的相同參數(shù). cmd 參數(shù)從用戶那里不改變地傳下來(lái), 并且可選的參數(shù) arg 參數(shù)以一個(gè) unsigned long 的形式傳遞, 不管它是否由用戶給定為一個(gè)整數(shù)或一個(gè)指針. 如果調(diào)用程序不傳遞第 3 個(gè)參數(shù), 被驅(qū)動(dòng)操作收到的 arg 值是無(wú)定義的. 因?yàn)轭愋蜋z查在這個(gè)額外參數(shù)上被關(guān)閉, 編譯器不能警告你如果一個(gè)無(wú)效的參數(shù)被傳遞給 ioctl, 并且任何關(guān)聯(lián)的錯(cuò)誤將難以查找.

如果你可能想到的, 大部分 ioctl 實(shí)現(xiàn)包括一個(gè)大的 switch 語(yǔ)句來(lái)根據(jù) cmd 參數(shù), 選擇正確的做法. 不同的命令有不同的數(shù)值, 它們常常被給予符號(hào)名來(lái)簡(jiǎn)化編碼. 符號(hào)名通過(guò)一個(gè)預(yù)處理定義來(lái)安排. 定制的驅(qū)動(dòng)常常聲明這樣的符號(hào)在它們的頭文件中; scull.h 為 scull 聲明它們. 用戶程序必須, 當(dāng)然, 包含那個(gè)頭文件來(lái)存液者陵取這些符號(hào).

1. 選擇 ioctl 命令

在為 ioctl 編寫(xiě)代碼之前, 你需要選擇對(duì)應(yīng)命令的數(shù)字. 許多程序員的之一個(gè)本能的反應(yīng)是選擇一組小數(shù)從0或1開(kāi)始, 并且從此開(kāi)始向上. 但是, 有充分的理由不這樣做. ioctl 命令數(shù)字應(yīng)當(dāng)在這個(gè)系統(tǒng)是唯一的, 為了阻止向錯(cuò)誤的設(shè)備發(fā)出正確的命令而引起的錯(cuò)誤. 這樣的不匹配不會(huì)不可能發(fā)生, 并且一個(gè)程序可能發(fā)現(xiàn)它自己試圖改變一個(gè)非串口輸入系統(tǒng)的波特率, 例如一個(gè) FIFO 或者一個(gè)音頻設(shè)備. 如果這樣的 ioctl 號(hào)是唯一的, 這個(gè)應(yīng)用程序得到一個(gè) EINVAL 錯(cuò)誤而不是繼續(xù)做不應(yīng)當(dāng)做的事情.

為幫助程序員創(chuàng)建唯一的 ioctl 命令代碼, 這些編碼已被劃分為幾個(gè)位段. Linux 的之一個(gè)版本使用 16-位數(shù): 高 8 位是關(guān)聯(lián)這個(gè)設(shè)備的”魔”數(shù), 低 8 位是一個(gè)順序號(hào), 在設(shè)備內(nèi)唯一. 這樣做是因?yàn)?Linus 是”無(wú)能”的(他自己的話); 一個(gè)更好的位段劃分僅在后來(lái)被設(shè)想. 不幸的是, 許多驅(qū)動(dòng)仍然使用老傳統(tǒng). 它們不得不: 改變命令編碼會(huì)破壞大量的二進(jìn)制程序,并且這不是內(nèi)核開(kāi)發(fā)者愿意見(jiàn)到的.

根據(jù) Linux 內(nèi)核慣例來(lái)為你的驅(qū)動(dòng)選擇 ioctl 號(hào), 你應(yīng)當(dāng)首先檢查 include/a/ioctl.h 和 Documentation/ioctl-number.txt. 這個(gè)頭文件定義你將使用的位段: type(魔數(shù)), 序號(hào), 傳輸方向, 和參數(shù)大小. ioctl-number.txt 文件列舉了在內(nèi)核中使用的魔數(shù), 因此你將可選擇你自己的魔數(shù)并且避免交疊. 這個(gè)文本文件也列舉了為什么應(yīng)當(dāng)使用慣例的原因.

定義 ioctl 命令號(hào)的正確方法使用 4 個(gè)位段, 它們有下列的含義. 這個(gè)列表中介紹的新符號(hào)定義在 .

type

魔數(shù). 只是選擇一個(gè)數(shù)(在參考了 ioctl-number.txt之后)并且使用它在整個(gè)驅(qū)動(dòng)中. 這個(gè)成員是 8 位寬(_IOC_TYPEBITS).

number

序(順序)號(hào). 它是 8 位(_IOC_NRBITS)寬.

direction

數(shù)據(jù)傳送的方向,如果這個(gè)特殊的命令涉及數(shù)據(jù)傳送. 可能的值是 _IOC_NONE(沒(méi)有數(shù)據(jù)傳輸), _IOC_READ, _IOC_WRITE, 和 _IOC_READ|_IOC_WRITE (數(shù)據(jù)在2個(gè)方向被傳送). 數(shù)據(jù)傳送是從應(yīng)用程序的觀點(diǎn)來(lái)看待的; _IOC_READ 意思是從設(shè)備讀, 因此設(shè)備必須寫(xiě)到用戶空間. 注意這個(gè)成員是一個(gè)位掩碼, 因此 _IOC_READ 和 _IOC_WRITE 可使用一個(gè)邏輯 AND 操作來(lái)抽取.

size

涉及到的用戶數(shù)據(jù)的大小. 這個(gè)成員的寬度是依賴體系的, 但是常常是 13 或者 14 位. 你可為你的特定體系在宏 _IOC_SIZEBITS 中找到它的值. 你使用這個(gè) size 成員不是強(qiáng)制的 – 內(nèi)核不檢查它 — 但是它是一個(gè)好主意. 正確使用這個(gè)成員可幫助檢測(cè)用戶空間程序的錯(cuò)誤并使你實(shí)現(xiàn)向后兼容, 如果你曾需要改變相關(guān)數(shù)據(jù)項(xiàng)的大小. 如果你需要更大的數(shù)據(jù)結(jié)構(gòu), 但是, 你可忽略這個(gè) size 成員. 我們很快見(jiàn)到如何使用這個(gè)成員.

頭文件 , 它包含在 中, 定義宏來(lái)幫助建立命令號(hào), 如下: _IO(type,nr)(給沒(méi)有參數(shù)的命令), _IOR(type, nre, datatype)(給從驅(qū)動(dòng)中讀數(shù)據(jù)的), _IOW(type,nr,datatype)(給寫(xiě)數(shù)據(jù)), 和 _IOWR(type,nr,datatype)(給雙向傳送). type 和 number 成員作為參數(shù)被傳遞, 并且 size 成員通過(guò)應(yīng)用 sizeof 到 datatype 參數(shù)而得到.

這個(gè)頭文件還定義宏, 可被用在你的驅(qū)動(dòng)中來(lái)解碼這個(gè)號(hào): _IOC_DIR(nr), _IOC_TYPE(nr), _IOC_NR(nr), 和 _IOC_SIZE(nr). 我們不進(jìn)入任何這些宏的細(xì)節(jié), 因?yàn)轭^文件是清楚的, 并且在本節(jié)稍后有例子代碼展示.

這里是一些 ioctl 命令如何在 scull 被定義的. 特別地, 這些命令設(shè)置和獲得驅(qū)動(dòng)的可配置參數(shù).

/* Use ‘k’ as magic number */

#define SCULL_IOC_MAGIC ‘k’

/* Please use a different 8-bit number in your code */

#define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0)

/*

* S means “Set” through a ptr,

* T means “Tell” directly with the argument value

* G means “Get”: reply by setting through a pointer

* Q means “Query”: response is on the return value

* X means “eXchange”: switch G and S atomically

* H means “sHift”: switch T and Q atomically

*/

#define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1, int)

#define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC, 2, int)

#define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC, 3)

#define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC, 4)

#define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC, 5, int)

#define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC, 6, int)

#define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7)

#define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC, 8)

#define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)

#define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC,10, int)

#define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC, 11)

#define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC, 12)

#define SCULL_IOC_MAXNR 14

真正的源文件定義幾個(gè)額外的這里沒(méi)有出現(xiàn)的命令.

我們選擇實(shí)現(xiàn) 2 種方法傳遞整數(shù)參數(shù): 通過(guò)指針和通過(guò)明確的值(盡管, 由于一個(gè)已存在的慣例, ioclt 應(yīng)當(dāng)通過(guò)指針交換值). 類似地, 2 種方法被用來(lái)返回一個(gè)整數(shù)值:通過(guò)指針和通過(guò)設(shè)置返回值. 這個(gè)有效只要返回值是一個(gè)正的整數(shù); 如同你現(xiàn)在所知道的, 在從任何系統(tǒng)調(diào)用返回時(shí), 一個(gè)正值被保留(如同我們?cè)?read 和 write 中見(jiàn)到的), 而一個(gè)負(fù)值被看作一個(gè)錯(cuò)誤并且被用來(lái)在用戶空間設(shè)置 errno.

“exchange”和”shift”操作對(duì)于 scull 沒(méi)有特別的用處. 我們實(shí)現(xiàn)”exchange”來(lái)顯示驅(qū)動(dòng)如何結(jié)合獨(dú)立的操作到單個(gè)的原子的操作, 并且”shift”來(lái)連接”tell”和”query”. 有時(shí)需要象這樣的原子的測(cè)試-和-設(shè)置操作, 特別地, 當(dāng)應(yīng)用程序需要設(shè)置和釋放鎖.

命令的明確的序號(hào)沒(méi)有特別的含義. 它只用來(lái)區(qū)分命令. 實(shí)際上, 你甚至可使用相同的序號(hào)給一個(gè)讀命令和一個(gè)寫(xiě)命令, 因?yàn)閷?shí)際的 ioctl 號(hào)在”方向”位是不同的, 但是你沒(méi)有理由這樣做. 我們選擇在任何地方不使用命令的序號(hào)除了聲明中, 因此我們不分配一個(gè)返回值給它. 這就是為什么明確的號(hào)出現(xiàn)在之前給定的定義中. 這個(gè)例子展示了一個(gè)使用命令號(hào)的方法, 但是你有自由不這樣做.

除了少數(shù)幾個(gè)預(yù)定義的命令(馬上就討論), ioctl 的 cmd 參數(shù)的值當(dāng)前不被內(nèi)核使用, 并且在將來(lái)也很不可能. 因此, 你可以, 如果你覺(jué)得懶, 避免前面展示的復(fù)雜的聲明并明確聲明一組調(diào)整數(shù)字. 另一方面, 如果你做了, 你不會(huì)從使用這些位段中獲益, 并且你會(huì)遇到困難如果你曾提交你的代碼來(lái)包含在主線內(nèi)核中. 頭文件 是這個(gè)老式方法的例子, 使用 16-位的調(diào)整值來(lái)定義 ioctl 命令. 那個(gè)源代碼依靠調(diào)整數(shù)因?yàn)槭褂媚莻€(gè)時(shí)候遵循的慣例, 不是由于懶惰. 現(xiàn)在改變它可能導(dǎo)致無(wú)理由的不兼容.

2. 返回值

ioctl 的實(shí)現(xiàn)常常是一個(gè) switch 語(yǔ)句, 基于命令號(hào). 但是當(dāng)命令號(hào)沒(méi)有匹配一個(gè)有效的操作時(shí)缺省的選擇應(yīng)當(dāng)是什么? 這個(gè)問(wèn)題是有爭(zhēng)議的. 幾個(gè)內(nèi)核函數(shù)返回 -ENIVAL(“Invalid argument”), 它有意義是因?yàn)槊顓?shù)確實(shí)不是一個(gè)有效的. POSIX 標(biāo)準(zhǔn), 但是, 說(shuō)如果一個(gè)不合適的 ioctl 命令被發(fā)出, 那么 -ENOTTY 應(yīng)當(dāng)被返回. 這個(gè)錯(cuò)誤碼被 C 庫(kù)解釋為”設(shè)備的不適當(dāng)?shù)?ioctl”, 這常常正是程序員需要聽(tīng)到的. 然而, 它仍然是相當(dāng)普遍的來(lái)返回 -EINVAL, 對(duì)于響應(yīng)一個(gè)無(wú)效的 ioctl 命令.

3. 預(yù)定義的命令

盡管 ioctl 系統(tǒng)調(diào)用最常用來(lái)作用于設(shè)備, 內(nèi)核能識(shí)別幾個(gè)命令. 注意這些命令, 當(dāng)用到你的設(shè)備時(shí), 在你自己的文件操作被調(diào)用之前被解碼. 因此, 如果你選擇相同的號(hào)給一個(gè)你的 ioctl命令, 你不會(huì)看到任何的給那個(gè)命令的請(qǐng)求, 并且應(yīng)用程序獲得某些不期望的東西, 因?yàn)樵?ioctl 號(hào)之間的沖突.

預(yù)定義命令分為 3 類:

可對(duì)任何文件發(fā)出的(常規(guī), 設(shè)備, FIFO, 或者 socket) 的那些.

只對(duì)常規(guī)文件發(fā)出的那些.

對(duì)文件系統(tǒng)類型特殊的那些.

最后一類的命令由宿主文件系統(tǒng)的實(shí)現(xiàn)來(lái)執(zhí)行(這是 chattr 命令如何工作的). 設(shè)備驅(qū)動(dòng)編寫(xiě)者只對(duì)之一類命令感興趣, 它們的魔數(shù)是 “T”. 查看其他類的工作留給讀者作為練習(xí); ext2_ioctl 是最有趣的函數(shù)(并且比預(yù)期的要容易理解), 因?yàn)樗鼘?shí)現(xiàn) append-only 標(biāo)志和 immutable 標(biāo)志.

下列 ioctl 命令是預(yù)定義給任何文件, 包括設(shè)備特殊的文件:

FIOCLEX

設(shè)置 close-on-exec 標(biāo)志(File IOctl Close on EXec). 設(shè)置這個(gè)標(biāo)志使文件描述符被關(guān)閉, 當(dāng)調(diào)用進(jìn)程執(zhí)行一個(gè)新程序時(shí).

FIONCLEX

清除 close-no-exec 標(biāo)志(File IOctl Not CLose on EXec). 這個(gè)命令恢復(fù)普通文件行為, 復(fù)原上面 FIOCLEX 所做的. FIOASYNC 為這個(gè)文件設(shè)置或者復(fù)位異步通知(如同在本章中”異步通知”一節(jié)中討論的). 注意直到 Linux 2.2.4 版本的內(nèi)核不正確地使用這個(gè)命令來(lái)修改 O_SYNC 標(biāo)志. 因?yàn)閮蓚€(gè)動(dòng)作都可通過(guò) fcntl 來(lái)完成, 沒(méi)有人真正使用 FIOASYNC 命令, 它在這里出現(xiàn)只是為了完整性.

FIOQSIZE

這個(gè)命令返回一個(gè)文件或者目錄的大小; 當(dāng)用作一個(gè)設(shè)備文件, 但是, 它返回一個(gè) ENOTTY 錯(cuò)誤.

FIONBIO

“File IOctl Non-Blocking I/O”(在”阻塞和非阻塞操作”一節(jié)中描述). 這個(gè)調(diào)用修改在 filp->f_flags 中的 O_NONBLOCK 標(biāo)志. 給這個(gè)系統(tǒng)調(diào)用的第 3 個(gè)參數(shù)用作指示是否這個(gè)標(biāo)志被置位或者清除. (我們將在本章看到這個(gè)標(biāo)志的角色). 注意常用的改變這個(gè)標(biāo)志的方法是使用 fcntl 系統(tǒng)調(diào)用, 使用 F_SETFL 命令.

列表中的最后一項(xiàng)介紹了一個(gè)新的系統(tǒng)調(diào)用, fcntl, 它看來(lái)象 ioctl. 事實(shí)上, fcntl 調(diào)用非常類似 ioctl, 它也是獲得一個(gè)命令參數(shù)和一個(gè)額外的(可選地)參數(shù). 它保持和 ioctl 獨(dú)立主要是因?yàn)闅v史原因: 當(dāng) Unix 開(kāi)發(fā)者面對(duì)控制 I/O 操作的問(wèn)題時(shí), 他們決定文件和設(shè)備是不同的. 那時(shí), 有 ioctl 實(shí)現(xiàn)的唯一設(shè)備是 ttys, 它解釋了為什么 -ENOTTY 是標(biāo)準(zhǔn)的對(duì)不正確 ioctl 命令的回答. 事情已經(jīng)改變, 但是 fcntl 保留為一個(gè)獨(dú)立的系統(tǒng)調(diào)用.

4. 使用 ioctl 參數(shù)

在看 scull 驅(qū)動(dòng)的 ioctl 代碼之前, 我們需要涉及的另一點(diǎn)是如何使用這個(gè)額外的參數(shù). 如果它是一個(gè)整數(shù), 就容易: 它可以直接使用. 如果它是一個(gè)指針, 但是, 必須小心些.

當(dāng)用一個(gè)指針引用用戶空間, 我們必須確保用戶地址是有效的. 試圖存取一個(gè)沒(méi)驗(yàn)證過(guò)的用戶提供的指針可能導(dǎo)致不正確的行為, 一個(gè)內(nèi)核 oops, 系統(tǒng)崩潰, 或者安全問(wèn)題. 它是驅(qū)動(dòng)的責(zé)任來(lái)對(duì)每個(gè)它使用的用戶空間地址進(jìn)行正確的檢查, 并且返回一個(gè)錯(cuò)誤如果它是無(wú)效的.

在第 3 章, 我們看了 copy_from_user 和 copy_to_user 函數(shù), 它們可用來(lái)安全地移動(dòng)數(shù)據(jù)到和從用戶空間. 這些函數(shù)也可用在 ioctl 方法中, 但是 ioctl 調(diào)用常常包含小數(shù)據(jù)項(xiàng), 可通過(guò)其他方法更有效地操作. 開(kāi)始, 地址校驗(yàn)(不傳送數(shù)據(jù))由函數(shù) access_ok 實(shí)現(xiàn), 它定義在 :

int access_ok(int type, const void *addr, unsigned long size);

之一個(gè)參數(shù)應(yīng)當(dāng)是 VERIFY_READ 或者 VERIFY_WRITE, 依據(jù)這個(gè)要進(jìn)行的動(dòng)作是否是讀用戶空間內(nèi)存區(qū)或者寫(xiě)它. addr 參數(shù)持有一個(gè)用戶空間地址, size 是一個(gè)字節(jié)量. 例如, 如果 ioctl 需要從用戶空間讀一個(gè)整數(shù), size 是 sizeof(int). 如果你需要讀和寫(xiě)給定地址, 使用 VERIFY_WRITE, 因?yàn)樗?VERIRY_READ 的超集.

不象大部分的內(nèi)核函數(shù), access_ok 返回一個(gè)布爾值: 1 是成功(存取沒(méi)問(wèn)題)和 0 是失敗(存取有問(wèn)題). 如果它返回假, 驅(qū)動(dòng)應(yīng)當(dāng)返回 -EFAULT 給調(diào)用者.

關(guān)于 access_ok有多個(gè)有趣的東西要注意. 首先, 它不做校驗(yàn)內(nèi)存存取的完整工作; 它只檢查看這個(gè)內(nèi)存引用是在這個(gè)進(jìn)程有合理權(quán)限的內(nèi)存范圍中. 特別地, access_ok 確保這個(gè)地址不指向內(nèi)核空間內(nèi)存. 第2, 大部分驅(qū)動(dòng)代碼不需要真正調(diào)用 access_ok. 后面描述的內(nèi)存存取函數(shù)為你負(fù)責(zé)這個(gè). 但是, 我們來(lái)演示它的使用, 以便你可見(jiàn)到它如何完成.

scull 源碼利用了 ioclt 號(hào)中的位段來(lái)檢查參數(shù), 在 switch 之前:

int err = 0, tmp;

int retval = 0;

/*

* extract the type and number bitfields, and don’t decode

* wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()

*/

if (_IOC_TYPE(cmd) != SCULL_IOC_MAGIC)

return -ENOTTY;

if (_IOC_NR(cmd) > SCULL_IOC_MAXNR)

return -ENOTTY;

/*

* the direction is a bitmask, and VERIFY_WRITE catches R/W

* transfers. `Type’ is user-oriented, while

* access_ok is kernel-oriented, so the concept of “read” and

* “write” is reversed

*/

if (_IOC_DIR(cmd) & _IOC_READ)

err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));

else if (_IOC_DIR(cmd) & _IOC_WRITE)

err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));

if (err)

return -EFAULT;

在調(diào)用 access_ok 之后, 驅(qū)動(dòng)可安全地進(jìn)行真正的傳輸. 加上 copy_from_user 和 copy_to_user_ 函數(shù), 程序員可利用一組為被最多使用的數(shù)據(jù)大小(1, 2, 4, 和 8 字節(jié))而優(yōu)化過(guò)的函數(shù). 這些函數(shù)在下面列表中描述, 它們定義在 :

put_user(datum, ptr)

__put_user(datum, ptr)

這些宏定義寫(xiě) datum 到用戶空間; 它們相對(duì)快, 并且應(yīng)當(dāng)被調(diào)用來(lái)代替 copy_to_user 無(wú)論何時(shí)要傳送單個(gè)值時(shí). 這些宏已被編寫(xiě)來(lái)允許傳遞任何類型的指針到 put_user, 只要它是一個(gè)用戶空間地址. 傳送的數(shù)據(jù)大小依賴 prt 參數(shù)的類型, 并且在編譯時(shí)使用 sizeof 和 typeof 等編譯器內(nèi)建宏確定. 結(jié)果是, 如果 prt 是一個(gè) char 指針, 傳送一個(gè)字節(jié), 以及對(duì)于 2, 4, 和 可能的 8 字節(jié).

put_user 檢查來(lái)確保這個(gè)進(jìn)程能夠?qū)懭虢o定的內(nèi)存地址. 它在成功時(shí)返回 0, 并且在錯(cuò)誤時(shí)返回 -EFAULT. __put_user 進(jìn)行更少的檢查(它不調(diào)用 access_ok), 但是仍然能夠失敗如果被指向的內(nèi)存對(duì)用戶是不可寫(xiě)的. 因此, __put_user 應(yīng)當(dāng)只用在內(nèi)存區(qū)已經(jīng)用 access_ok 檢查過(guò)的時(shí)候.

作為一個(gè)通用的規(guī)則, 當(dāng)你實(shí)現(xiàn)一個(gè) read 方法時(shí), 調(diào)用 __put_user 來(lái)節(jié)省幾個(gè)周期, 或者當(dāng)你拷貝幾個(gè)項(xiàng)時(shí), 因此, 在之一次數(shù)據(jù)傳送之前調(diào)用 access_ok 一次, 如同上面 ioctl 所示.

get_user(local, ptr)

__get_user(local, ptr)

這些宏定義用來(lái)從用戶空間接收單個(gè)數(shù)據(jù). 它們象 put_user 和 __put_user, 但是在相反方向傳遞數(shù)據(jù). 獲取的值存儲(chǔ)于本地變量 local; 返回值指出這個(gè)操作是否成功. 再次, __get_user 應(yīng)當(dāng)只用在已經(jīng)使用 access_ok 校驗(yàn)過(guò)的地址.

關(guān)于linux的ioctl的介紹到此就結(jié)束了,不知道你從中找到你需要的信息了嗎 ?如果你還想了解更多這方面的信息,記得收藏關(guān)注本站。

創(chuàng)新互聯(lián)服務(wù)器托管擁有成都T3+級(jí)標(biāo)準(zhǔn)機(jī)房資源,具備完善的安防設(shè)施、三線及BGP網(wǎng)絡(luò)接入帶寬達(dá)10T,機(jī)柜接入千兆交換機(jī),能夠有效保證服務(wù)器托管業(yè)務(wù)安全、可靠、穩(wěn)定、高效運(yùn)行;創(chuàng)新互聯(lián)專注于成都服務(wù)器托管租用十余年,得到成都等地區(qū)行業(yè)客戶的一致認(rèn)可。


網(wǎng)頁(yè)標(biāo)題:Linux的ioctl命令及其相關(guān)用法詳解(linux的ioctl)
鏈接URL:http://www.dlmjj.cn/article/djgdhie.html