Linux核心的ioctl函數學習

來源:互聯網
上載者:User

from :http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=1967512


ioctl 函數 參數 詳解
2009-04-24 11:55
ioctl函數
本函數影響由fd參數引用的一個開啟的檔案。
#include
int ioctl( int fd, int request, .../* void *arg */ );
返回0:成功 -1:出錯
第三個參數總是一個指標,但指標的類型依賴於request參數。
我們可以把和網路相關的請求劃分為6類:
套介面操作
檔案操作
介面操作
ARP快取操作
路由表操作
流系統
下表列出了網路相關ioctl請求的request參數以及arg地址必須指向的資料類型:
類別 Request 說明 資料類型
套介面
SIOCATMARK
SIOCSPGRP
SIOCGPGRP
是否位於帶外標記
設定套介面的進程ID或進程組ID
擷取套介面的進程ID或進程組ID
int
int
int
檔案
FIONBIN
FIOASYNC
FIONREAD
FIOSETOWN
FIOGETOWN
設定/清除非阻塞I/O標誌
設定/清除訊號驅動非同步I/O標誌
擷取接收緩衝區中的位元組數
設定檔案的進程ID或進程組ID
擷取檔案的進程ID或進程組ID
int
int
int
int
int
介面
SIOCGIFCONF
SIOCSIFADDR
SIOCGIFADDR
SIOCSIFFLAGS
SIOCGIFFLAGS
SIOCSIFDSTADDR
SIOCGIFDSTADDR
SIOCGIFBRDADDR
SIOCSIFBRDADDR
SIOCGIFNETMASK
SIOCSIFNETMASK
SIOCGIFMETRIC
SIOCSIFMETRIC
SIOCGIFMTU
SIOCxxx
擷取所有介面的清單
設定介面地址
擷取介面地址
設定介面標誌
擷取介面標誌
設定點到點地址
擷取點到點地址
擷取廣播位址
設定廣播位址
擷取子網路遮罩
設定子網路遮罩
擷取介面的測度
設定介面的測度
擷取介面MTU
(還有很多取決於系統的實現)
struct ifconf
struct ifreq
struct ifreq
struct ifreq
struct ifreq
struct ifreq
struct ifreq
struct ifreq
struct ifreq
struct ifreq
struct ifreq
struct ifreq
struct ifreq
struct ifreq
ARP
SIOCSARP
SIOCGARP
SIOCDARP
建立/修改ARP表項
擷取ARP表項
刪除ARP表項
struct arpreq
struct arpreq
struct arpreq
路由
SIOCADDRT
SIOCDELRT
增加路徑
刪除路徑
struct rtentry
struct rtentry
流
I_xxx
套介面操作:
明確用於套介面操作的ioctl請求有三個,它們都要求ioctl的第三個參數是指向某個整數的一個指標。
SIOCATMARK: 如果本套介面的的度指標當前位於帶外標記,那就通過由第三個參數指向的整數返回一個非0值;否則返回一個0值。POSIX以函數sockatmark替換本請求。
SIOCGPGRP: 通過第三個參數指向的整數返回本套介面的進程ID或進程組ID,該ID指定針對本套介面的SIGIO或SIGURG訊號的接收進程。本請求和fcntl的F_GETOWN命令等效,POSIX標準化的是fcntl函數。
SIOCSPGRP: 把本套介面的進程ID或者進程組ID設定成第三個參數指向的整數,該ID指定針對本套介面的SIGIO或SIGURG訊號的接收進程,本請求和fcntl的F_SETOWN命令等效,POSIX標準化的是fcntl操作。
檔案操作:
以下5個請求都要求ioctl的第三個參數指向一個整數。
FIONBIO: 根據ioctl的第三個參數指向一個0或非0值分別清除或設定本套介面的非阻塞標誌。本請求和O_NONBLOCK檔案狀態標誌等效,而該標誌通過fcntl的F_SETFL命令清除或設定。
FIOASYNC: 根據iocl的第三個參數指向一個0值或非0值分別清除或設定針對本套介面的訊號驅動非同步I/O標誌,它決定是否收取針對本套介面的非同步I/O訊號 (SIGIO)。本請求和O_ASYNC檔案狀態標誌等效,而該標誌可以通過fcntl的F_SETFL命令清除或設定。
FIONREAD: 通過由ioctl的第三個參數指向的整數返回當前在本套介面接收緩衝區中的位元組數。本特性同樣適用於檔案,管道和終端。
FIOSETOWN: 對於套介面和SIOCSPGRP等效。
FIOGETOWN: 對於套介面和SIOCGPGRP等效。
介面配置:
得到系統中所有介面由SIOCGIFCONF請求完成,該請求使用ifconf結構,ifconf又使用ifreq
結構,如下所示:
Struct ifconf{
int ifc_len; // 緩衝區的大小
union{
caddr_t ifcu_buf; // input from user->kernel
struct ifreq *ifcu_req; // return of structures returned
}ifc_ifcu;
};
#define ifc_buf ifc_ifcu.ifcu_buf //buffer address
#define ifc_req ifc_ifcu.ifcu_req //array of structures returned
#define IFNAMSIZ 16
struct ifreq{
char ifr_name[IFNAMSIZ]; // interface name, e.g., “le0”
union{
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
short ifru_flags;
int ifru_metric;
caddr_t ifru_data;
}ifr_ifru;
};
#define ifr_addr ifr_ifru.ifru_addr // address
#define ifr_dstaddr ifr_ifru.ifru_dstaddr // otner end of p-to-p link
#define ifr_broadaddr ifr_ifru.ifru_broadaddr // broadcast address
#define ifr_flags ifr_ifru.ifru_flags // flags
#define ifr_metric ifr_ifru.ifru_metric // metric
#define ifr_data ifr_ifru.ifru_data // for use by interface
再調用ioctl前我們必須先分撇一個緩衝區和一個ifconf結構,然後才初始化後者。如
展示了一個ifconf結構的初始化結構,其中緩衝區的大小為1024,ioctl的第三個參數指向
這樣一個ifconf結構。
ifc_len
Ifc_buf
1024
--------------------->緩衝
假設核心返回2個ifreq結構,ioctl返回時通過同一個ifconf結構緩衝區填入了那2個ifreq結構,ifconf結構的ifc_len成員也被更新,以反映存放在緩衝區中的資訊量
一般來講ioctl在使用者程式中的調用是:
ioctl(int fd,int command, (char*)argstruct)
ioctl調用與網路編程有關(本文只討論這一點),檔案描述符fd實際上是由socket()系統調用返回的。參數command的取值由/usr/include/linux/sockios.h所規定。這些command的由於功能的不同,可分為以下幾個小類:
• 改變路由表 (例如 SIOCADDRT, SIOCDELRT),
• 讀/更新 ARP/RARP 快取(如:SIOCDARP, SIOCSRARP),
• 一般的與網路介面有關的(例如 SIOCGIFNAME, SIOCSIFADDR 等等)
在Gooodies目錄下有很多範例程式展示了如何使用ioctl。當你看這些程式時,注意參數argstruct是與參數command相關的。例如, 與路由表相關的ioctl使用rtentry這種結構,rtentry定義在/usr/include/linux/route.h(參見例子 adddefault.c)。與ARP有關的ioctl調用使用arpreq結構,arpreq定義在/usr/include/linux /if_arp.h(參見例子arpread.c)
與網路介面有關的ioctl調用使用的command參數通常看起來像SIOCxIFyyyy的形式,這裡x要麼是S(設定set,寫write),要麼 是G(得到get,讀read)。在getifinfo.c程式中就使用了這種形式的command參數來讀IP地址,硬體地址,廣播位址和得到與網路接 口有關的一些標誌(flag)。在這些ioctl調用中,第三個參數是ifreq結構,它在/usr/include/linux/if.h中定義。在某 些情況下, ioctrl調用可能會使用到在sockios.h之外的新的定義,例如,WaveLAN無線網路卡會儲存有關無線網路訊號強度的資訊,這對使用者的程式可
能有用。但使用者怎麼得到這種資訊呢?我們的第一個本能是在sockios.h中定義新的ioctl命令,例如SIOCGIFWVLNSS(它的英文縮寫表 示WaveLAN的訊號強度)。但不幸的是,這種命令不是對所有其他的網路介面(例如:loopback環回介面)有意義,而且不應當允許對於 WAVLAN卡以外的網路介面使用ioctl命令。那麼,我們需要的是這樣一種機制:它能夠定義一種與網路介面相關的ioctl命令。幸運的是,在 Linux作業系統中已經為實現這個目的內建了一種掛鈎(hook)機制。當你再次看sockios.h檔案時,你將發現每一種裝置已經預先定義了
SIOCDEVPRIVATE的ioctl命令。而它的實現將留給開發相應驅動程式的人去完成。
通常,一個使用者程式使用ioctl(sockid,SIOCDEVPRIVATE,(char*)&ifr)來調用與某種裝置(指像 WaveLAN那樣的特殊裝置)相關的ioctl命令,這裡ifr是struct ifreq ifr形式的變數。使用者程式應當在ifr.ifr_name中填充與這個裝置相關的名字,例如,假設WaveLAN使用的介面號為eth1。一般的,一個 使用者程式還需要與核心互相交換ioctl的command參數和結果,這可以通過ifr.ifr_data這個變數來實現,例如,想得到WaveLAN中 表示訊號強度的資訊時,可以通過返回這個變數來實現。Linux的原始碼已經包括了兩種裝置de4x5和ewrk3,它們定義並且實現了特定的ioctl
調用。這兩個裝置的原始碼在de4x5.h,de4x5.c,ewrk3.h,ewrk3.c中(在/usr/src/linux/drivers /net/目錄中)。這兩種裝置都定義了它們特有的結構(struct ewrk3_ioctl 和 struct de4x5_ioctl)來方便使用者程式和裝置驅動之間交換資訊。每次調用ioctl前,使用者程式應當在相應的結構變數中設定合適的初值,並且將 ifr.ifr_data指向該值。
在我們進一步討論ewrk3和de4x5的代碼前,讓我們仔細看看ioctl調用是如何一步步地實現的。所有的和介面相關的ioctl請求 (SIOCxIFyyyy 和 SIOCDEVPRIVATE)將會調用dev_ioctl()(在/usr/src/linux/net/core/dev.c中)。但這隻是一個封裝 器(wrapper),實際的動作將由dev_ifsioc()(也在dev.c中)來實現。差不多dev_ioctl()這個函數所做的所有工作只是檢 查這個調用是否已經有了正當的許可權(例如,改變路由表需要有root的許可權)。而dev_ifsioc()這個函數首先要做的一些事情包括得到與
ifr.ifr_name相匹配的裝置的結構(在/usr/include/linux/netdevice.h中定義)。但這是在實現特定的介面命令 (例如:SIOCGIFADDR)之後。這些特定的介面命令被放置到一個巨大的switch語句之中。其中SIOCDEVPRIVATE命令和其他的在 0x89F0到0x89FF之間的代碼將出現在switch語句中的一個分支——default語句中。核心會檢查表示裝置的結構變數中,是否已經定義了 一個與裝置相關的ioctl控制代碼(handler)。這裡的控制代碼是一個函數指標,它在表示裝置的結構變數中do_ioctl部分。如果已經設定了這個句
柄,那麼核心將會執行它。
所以,如果要實現一個與裝置相關的ioctl命令,所要做的只是編寫一個與這個裝置相關的ioctl控制代碼,並且將表示這個裝置的結構變數中 do_ioctl部分指向這個控制代碼。對於ewrk3這個裝置,它的控制代碼是ewrk3_ioctl()(在ewrk3.c裡面)並且相應的表示該裝置的結構 變數由ewrk3_init()來初始化。在ewrk3_ioctl()的代碼中清晰的指出ifr.ifr_data是用作裝置驅動程式和使用者程式之間交 換資訊的。注意,這部分的記憶體可以雙向的交流資訊。例如,在ewrk3的驅動程式代碼中,if.ifr_data的頭兩個位元組是用來表示特殊的動作(例
如,EWRK3_SET_PROM,EWRK3_CLR_PROM),而這個動作是符合使用者(驅動程式實現了多個與裝置相關的、由 SIOCDEVPRIVATE調用的命令)的要求的。另外,ifr.ifr_data中第5個位元組指向的緩衝區(buffer)被用來交換其他的資訊 (如:當使用EWRK3_SET_HWADDR和EWRK3_GET_HWADDR時為硬體地址)
在你深入ewrk3_ioctl()時,請注意一般情況下一個使用者進程不能直接存取核心所在的記憶體。為此,驅動開發人員可以使用兩個特殊的函數 memcpy_tofs()和memcpy_fromfs()。核心功能memcpy_tofs(arg1, arg2, arg3) 從地址arg2(使用者空間)向地址arg1(核心空間)拷貝arg3個位元組。類似的,memcpy_fromfs(arg1,arg2,arg3)從地址 arg2(使用者空間)向地址arg1(核心空間)拷貝arg3個位元組。在這些調用之前,verify_area()將會檢查這個進程是否擁有合適的訪問權
限。另外,注意使用printk()函數可以輸出debug資訊。這個函數與printf()函數類似,但不能處理浮點類型的數。核心代碼不能夠使用 printf()函數。printk()函數產生的結果將記錄在/usr/adm/messages裡。如果想知道更多的關於這些函數的或者與它們相關的 資訊,可以參考《Linux Kernel Hacker’s Guide》(在Linux文檔網站的首頁) 這本書中Supporting Functions部分。

關於linux核心的ioctl函數
trangod發佈於 2007-4-07 |
4744次閱讀 字型大小:
大

中

小
(網友評論 3 條)
我要評論

  一般的說,,使用者空間的IOCTL系統調用如下所示: ioctl(int fd, int command, (char *) argstruct)因為這個調用擁有與網路相關的代碼,所以檔案描述符號fd就是socket()系統調用所返回的,而command參數可以是/usr/include/linux/sockios.h標頭檔中的任何一個,這些個命令根據它可以解決的問題所涉及的方面被分為多種的類型.
  比如:
  改變路由表(SIOCADDRT, SIOCDELRT)
  
  讀取或更新ARP/RARP緩衝(SIOCDARP, SIOCSRARP)
一般的和網路有關的函數(SIOCGIFNAME, SIOCSIFADDR等等)
  Goodies目錄中包含了很多展示ioctl用法的樣本程式,看這些程式的時候,注意根據ioctl的命令類型來學習具體的調用參數結構,比如:和路由表相關的IOCTL用RTENTRY結構, rtentry結構是被定義在/usr/include/linux/route.h檔案中的,再一個和ARP相關的ioctl調用用到的arpreq結構被定義在/usr/include/linux/if_arp.h檔案之中.網路介面相關的ioctl命令最具有代表性的特徵為都是以S或G開頭,其實就是設定或得到資料, getifinfo.c程式用這些命令去讀取IP地址資訊,硬體地址資訊,廣播位址資訊,和與網路介面相關的標誌.對於這些ioctl,第三個參數是一個IFREQ結構體,這個結構體被定義在/usr/include/linux/if.h標頭檔中,在一些情況下,新的ioctl命令可能被需要(除了在那個標頭檔中被定義的之外),比如
WAVELAN無線網卡保持著無線訊號強度的資訊,這些信西可能要 對使用者程式有用.使用者程式是怎麼訪問到這些資訊的呢?我們的第一反應就是定義一個新的命令在sockios.h標頭檔中,比如SIOCGIFWVLNSS,不幸的是,這個命令在其他的網路介面上是根本沒有意義的,另外試圖在其他介面上用這個名另而並非是在無線網口上用會出現違規訪問,我們需要的是定義新特性介面命令的機理。幸運的是,LINUX作業系統為此目的內建了鉤子,如果你再看一下那個標頭檔sockios.h你會注意到每一個裝置都有一個預定義的SIOCDEVPRIVATE命令,實現它的任務就全權交給了寫這個裝置驅動的程式員了.根據常規約定,一個使用者程式調用一個特定的ioctl命令如下:
ioctl(sockid, SIOCDEVPRIVATE, (char *) &ifr)這裡ifr是一個ifreq結構體變數,它用一個和這個裝置聯絡的介面名稱填充ifr的ifr NAME域,比如,前述的無線網卡介面名稱為eth1。
  不失一般性,一個使用者程式將同樣要與核心交換命令參數和操作結果,而這些已經通過一個域ifr.ifr_data的填充而做到了,比如,這個網卡的訊號強度資訊被返回到這個域當中。LINUX原始碼已經包含了兩個特殊裝置de4x5和ewrk3,他們定義和實現了特殊的ioctl命令.,這些裝置的原始碼在以下的檔案中:de4x5.h, de4x5.c, ewrk3.h, ewrk3.c, 他們兩個裝置都為在使用者空間和驅動間交換資料定義了他們自己的私人結構,在ioctl之前,使用者程式填充了需要的資料並且將ifr.ifr_data指向這個結構體.
  我們在兩個驅動中走的更遠些從而進入代碼前,讓我們跟蹤一下處理ioctl系統調用的若干步驟,,所有介面類型的ioctl請求都導致dev_ioctl()被調用,這個ioctl僅僅是個封裝,大部分的真實的操作留給了dev_ifsioc().,這個dev_ioctl()要做的唯一一個事情就是檢查調用過程是否擁有合適的許可去核發這個命令,然後dev_ifsioc()首先要做的事情之一就是得到和名字域ifr.ifr_name中所對應的裝置結構,這在一個很大的switch語塊的代碼後實現。
  SIOCDEVPRIVATE命令和SIOCDEVPRIVATE+15的命令參數全部交給了預設操作,這些都是switch的分支語句.這裡發生的是,核心檢查是否一個裝置特殊的ioctl的回調已經在裝置結構中被設定,這個回調是保持在裝置結構中的一個函數指標。如果回調已經被設定了.核心就會調用它.
  所以,為了實現一個特殊的ioctl,需要做的就是寫一個特殊ioctl的回調,然後讓device結構中的do_ioctl域指向它,對於EWK3裝置,這個函數叫做ewrk3_ioctl(),對應的裝置結構在ewrk3_init()中被初始化,ewrk3_ioctl()的代碼清晰的展示了ifr.ifr_data的作用 ,是為了在使用者程式和驅動之間交換資訊。注意,記憶體的這個地區有雙方向交換資料的作用,例如,在ewrk3驅動代碼中,ifr.ifr_data最初的2個位元組被用做向驅動傳遞預想要的動作。同樣第五個位元組指向的緩衝區用於交換其他的資訊。
  當你瀏覽ewrk3_ioctl()代碼的時候,記住在一個應用中使用者空間的指令是無法訪問核心空間的,由於這個原因 ,2個特殊的步驟提供給了驅動編寫人員.他們是memcpy_tofs()和memcpy_fromfs()。核心裡的做法是用memcpy_tofs() 拷貝核心資料到使用者空間,類似的memcpy_fromfs()也是這樣的,只是他拷貝使用者資料到核心空間.。這些程式步驟是由於調用verify_area()而被執行的,目的是確認資料訪問不會違法。同樣記住printk()的用法是列印調試資訊,這個函數和printf()很相象,但是它不能處理浮點數據,printf()函數在核心中是不能被使用的。由printk()產生的輸出被轉儲到了一個目錄./usr/adm/messages。
ioctl(轉載)
2009-03-19 19:02
要按照Linux核心的約定方法為驅動程式選擇ioctl編號,應該首先看看include/asm/ioctl.h和Documentation/ioctl-number.txt這兩個檔案。
_IO(type, nr)
用於構造無參數的命令編號;
_IOR(type, nr, datatype)
用於構造從驅動程式中讀取資料的命令編號;
_IOW(type, nr, datatype)
用於寫入資料的命令;
_IOWR(type, nr, datatype)
用於雙向傳輸。
傳回值
----------------------------------
ioctl的實現通常就是一個基於命令號的switch語句。但是當命令號不能匹配任何合法的操作時,預設的選擇是什麼呢?對於這個問題頗有爭議。有些核心功能會返回-ENVAL("Invalid argument, 非法參數"),這是合理的,因為命令參數的確不是合法的參數。然而,POSIX標準規定,如果使用了不合適的ioctl命令參數,應該返回-ENOTTY。C庫將這個錯誤碼解釋為“Inappropriate ioctl for device, 不合適的裝置ioctl”,這看起來更貼切些。儘管如此,對非法的ioctl命令放回-EINVAL仍然時很普遍的做法。
cd ioctl_test
ioctl_test.c
-------------------------------------------
#include
#include
#include
#include
#include
#include
#include
#include "ioctl_test.h"
typedef struct {
struct class *class;
int major;
char *dev_name;
} MODULE_DATA;
static MODULE_DATA *g_module;
static int test_dev_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int ret, val;
switch (cmd) {
case DEVICE_READ:
{
return put_user(10, (long *) arg);
}
case DEVICE_WRITE:
{
ret = get_user(val, (long *) arg);
if (ret)
return ret;
printk("val = %d\n", val);
break;
}
default:
return -ENOTTY;
}
return 0;
}
static int test_dev_open(struct inode *inode, struct file *file)
{
printk("test_dev open\n");
return 0;
}
static struct file_operations test_dev_fops = {
ioctl: test_dev_ioctl,
open: test_dev_open,
};
static int __init test_dev_init(void)
{
int res;
struct class_device *temp_class;
struct class *dev_class;
int major;
char dev_name[] = "test_dev";
g_module = kmalloc(sizeof (MODULE_DATA), GFP_KERNEL);
if (g_module == NULL) {
PERROR("kmalloc error\n");
return -ENOMEM;
}
memset(g_module, 0, sizeof (MODULE_DATA));
res = register_chrdev(0, dev_name, &test_dev_fops);
if (res major = major;
g_module->class = dev_class;
g_module->dev_name = dev_name;
return 0;
}
static void __exit test_dev_exit(void)
{
struct class *dev_class;
int major;
char *dev_name;
dev_class = g_module->class;
major = g_module->major;
dev_name = g_module->dev_name;
class_device_destroy(dev_class, MKDEV(major, 0));
class_destroy(dev_class);
unregister_chrdev(major, dev_name);
kfree(g_module);
PINFO("test_dev driver removed\n");
return;
}
module_init(test_dev_init);
module_exit(test_dev_exit);
MODULE_LICENSE("GPL");
ioctl_test.h
-------------------------------------------
#ifndef MXC_CMMB_INNO_H
#define MXC_CMMB_INNO_H
#undef PERROR
#define PERROR(fmt, args...) printk(KERN_ERR "cmmb driver error: line %d- %s():" fmt,__LINE__, __FUNCTION__, ## args)
#undef PINFO
#define PINFO(fmt, args...) printk(KERN_INFO fmt, ## args)
#undef PDEBUG /* undef it, just in case */
#ifdef CMMB_INNO_DEBUG
#define PDEBUG(fmt, args...) printk(KERN_DEBUG "cmmb driver line %d - %s():" fmt,__LINE__, __FUNCTION__, ## args)
#else
#define PDEBUG(fmt, args...) /* not debugging: nothing */
#endif
#define DEVICE_READ _IOR('t', 1, int)
#define DEVICE_WRITE _IOW('t', 2, int)
#endif
Makefile
-------------------------------------------
KDIR=/usr/src/linux-headers-2.6.24-19-generic
obj-m += ioctl_test.o
all:
make -C $(KDIR) M=`pwd` modules
clean:
make -C $(KDIR) M=`pwd` clean
cd ..
test_ioctl.c
-------------------------------------------
#include
#include
#include
#include "ioctl_test/ioctl_test.h"
#define INNO_DEV_NAME "/dev/test_dev"
int main()
{
int fd, ret, get_int, put_int = 527;
fd = open(INNO_DEV_NAME, O_RDWR);
ret = ioctl(fd, DEVICE_READ, &get_int);
if (ret 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.