国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
ioctl()函數(shù)
本函數(shù)影響由fd參數(shù)引用的一個打開的文件。
#include<unistd.h>
#include<sys/ioctl.h>
int ioctl( int fd, int request, .../* void *arg */ );
返回0:成功    -1:出錯
第三個參數(shù)總是一個指針,但指針的類型依賴于request參數(shù)。
我們可以把和網(wǎng)絡相關的請求劃分為6類:
套接口操作
文件操作
接口操作
ARP高速緩存操作
路由表操作
流系統(tǒng)
下表列出了網(wǎng)絡相關ioctl請求的request參數(shù)以及arg地址必須指向的數(shù)據(jù)類型:
類別
Request
說明
數(shù)據(jù)類型
SIOCATMARK
SIOCSPGRP
SIOCGPGRP
是否位于帶外標記
設置套接口的進程ID或進程組ID
獲取套接口的進程ID或進程組ID
int
int
int
 
FIONBIN
FIOASYNC
FIONREAD
FIOSETOWN
FIOGETOWN
設置/清除非阻塞I/O標志
設置/清除信號驅動異步I/O標志
獲取接收緩存區(qū)中的字節(jié)數(shù)
設置文件的進程ID或進程組ID
獲取文件的進程ID或進程組ID
int
int
int
int
int
SIOCGIFCONF
SIOCSIFADDR
SIOCGIFADDR
SIOCSIFFLAGS
SIOCGIFFLAGS
SIOCSIFDSTADDR
SIOCGIFDSTADDR
SIOCGIFBRDADDR
SIOCSIFBRDADDR
SIOCGIFNETMASK
SIOCSIFNETMASK
SIOCGIFMETRIC
SIOCSIFMETRIC
SIOCGIFMTU
SIOCxxx
獲取所有接口的清單
設置接口地址
獲取接口地址
設置接口標志
獲取接口標志
設置點到點地址
獲取點到點地址
獲取廣播地址
設置廣播地址
獲取子網(wǎng)掩碼
設置子網(wǎng)掩碼
獲取接口的測度
設置接口的測度
獲取接口MTU
(還有很多取決于系統(tǒng)的實現(xiàn))
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
創(chuàng)建/修改ARP表項
獲取ARP表項
刪除ARP表項
struct arpreq
struct arpreq
struct arpreq
SIOCADDRT
SIOCDELRT
增加路徑
刪除路徑
struct rtentry
struct rtentry
I_xxx
 
 
套接口操作:
明確用于套接口操作的ioctl請求有三個,它們都要求ioctl的第三個參數(shù)是指向某個整數(shù)的一個指針。
SIOCATMARK:    如果本套接口的的度指針當前位于帶外標記,那就通過由第三個參數(shù)指向的整數(shù)返回一個非0值;否則返回一個0值。POSIX以函數(shù)sockatmark替換本請求。
SIOCGPGRP:       通過第三個參數(shù)指向的整數(shù)返回本套接口的進程ID或進程組ID,該ID指定針對本套接口的SIGIO或SIGURG信號的接收進程。本請求和fcntl的F_GETOWN命令等效,POSIX標準化的是fcntl函數(shù)。
SIOCSPGRP:     把本套接口的進程ID或者進程組ID設置成第三個參數(shù)指向的整數(shù),該ID指定針對本套接口的SIGIO或SIGURG信號的接收進程,本請求和fcntl的F_SETOWN命令等效,POSIX標準化的是fcntl操作。
文件操作:
以下5個請求都要求ioctl的第三個參數(shù)指向一個整數(shù)。 
FIONBIO:        根據(jù)ioctl的第三個參數(shù)指向一個0或非0值分別清除或設置本套接口的非阻塞標志。本請求和O_NONBLOCK文件狀態(tài)標志等效,而該標志通過fcntl的F_SETFL命令清除或設置。 
FIOASYNC:      根據(jù)iocl的第三個參數(shù)指向一個0值或非0值分別清除或設置針對本套接口的信號驅動異步I/O標志,它決定是否收取針對本套接口的異步I/O信號(SIGIO)。本請求和O_ASYNC文件狀態(tài)標志等效,而該標志可以通過fcntl的F_SETFL命令清除或設置。
FIONREAD:     通過由ioctl的第三個參數(shù)指向的整數(shù)返回當前在本套接口接收緩沖區(qū)中的字節(jié)數(shù)。本特性同樣適用于文件,管道和終端。
FIOSETOWN:    對于套接口和SIOCSPGRP等效。
FIOGETOWN:    對于套接口和SIOCGPGRP等效。 
接口配置:
得到系統(tǒng)中所有接口由SIOCGIFCONF請求完成,該請求使用ifconf結構,ifconf又使用ifreq結構,如下所示:
Struct ifconf{
   int ifc_len;                 // 緩沖區(qū)的大小
   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
再調(diào)用ioctl前我們必須先分撇一個緩沖區(qū)和一個ifconf結構,然后才初始化后者。如下圖展示了一個ifconf結構的初始化結構,其中緩沖區(qū)的大小為1024,ioctl的第三個參數(shù)指向這樣一個ifconf結構。
ifc_len          1024
Ifc_buf           ---------->緩存
假設內(nèi)核返回2個ifreq結構,ioctl返回時通過同一個ifconf結構緩沖區(qū)填入了那2個ifreq結構,ifconf結構的ifc_len成員也被更新,以反映存放在緩沖區(qū)中的信息量
一般來講ioctl在用戶程序中的調(diào)用是:ioctl(int fd,int command, (char*)argstruct)
ioctl調(diào)用與網(wǎng)絡編程有關(本文只討論這一點),文件描述符fd實際上是由socket()系統(tǒng)調(diào)用返回的。參數(shù)command的取值由/usr/include/linux/sockios.h所規(guī)定。這些command的由于功能的不同,可分為以下幾個小類:
• 改變路由表 (例如 SIOCADDRT, SIOCDELRT), 
• 讀/更新 ARP/RARP 緩存(如:SIOCDARP, SIOCSRARP), 
• 一般的與網(wǎng)絡接口有關的(例如 SIOCGIFNAME, SIOCSIFADDR 等等) 
在Gooodies 目錄下有很多樣例程序展示了如何使用ioctl。當你看這些程序時,注意參數(shù)argstruct是與參數(shù)command相關的。例如,與路由表相關的 ioctl使用rtentry這種結構,rtentry定義在/usr/include/linux/route.h(參見例子 adddefault.c)。與ARP有關的ioctl調(diào)用使用arpreq結構,arpreq定義在 /usr/include/linux/if_arp.h(參見例子arpread.c)
與網(wǎng)絡接口有關的ioctl調(diào)用使用的command參數(shù)通??雌饋硐馭IOCxIFyyyy的形式,這里x要 么是S(設定set,寫write),要么是G(得到get,讀read)。在getifinfo.c程序中就使用了這種形式的command參數(shù)來讀 IP地址,硬件地址,廣播地址和得到與網(wǎng)絡接口有關的一些標志(flag)。在這些ioctl調(diào)用中,第三個參數(shù)是ifreq結構,它在 /usr/include/linux/if.h中定義。在某些情況下, ioctrl調(diào)用可能會使用到在sockios.h之外的新的定義,例如,WaveLAN無線網(wǎng)絡卡會保存有關無線網(wǎng)絡信號強度的信息,這對用戶的程序可 能有用。但用戶怎么得到這種信息呢?我們的第一個本能是在sockios.h中定義新的ioctl命令,例如SIOCGIFWVLNSS(它的英文縮寫表 示W(wǎng)aveLAN的信號強度)。但不幸的是,這種命令不是對所有其他的網(wǎng)絡接口(例如:loopback環(huán)回接口)有意義,而且不應當允許對于 WAVLAN卡以外的網(wǎng)絡接口使用ioctl命令。那么,我們需要的是這樣一種機制:它能夠定義一種與網(wǎng)絡接口相關的ioctl命令。幸運的是,在 Linux操作系統(tǒng)中已經(jīng)為實現(xiàn)這個目的內(nèi)建了一種掛鉤(hook)機制。當你再次看sockios.h文件時,你將發(fā)現(xiàn)每一種設備已經(jīng)預先定義了 SIOCDEVPRIVATE的ioctl命令。而它的實現(xiàn)將留給開發(fā)相應驅動程序的人去完成。
通常,一個用戶程序使用ioctl (sockid,SIOCDEVPRIVATE,(char*)&ifr)來調(diào)用與某種設備(指像WaveLAN那樣的特殊設備)相關的 ioctl命令,這里ifr是struct ifreq ifr形式的變量。用戶程序應當在ifr.ifr_name中填充與這個設備相關的名字,例如,假設WaveLAN使用的接口號為eth1。一般的,一個 用戶程序還需要與內(nèi)核互相交換ioctl的command參數(shù)和結果,這可以通過ifr.ifr_data這個變量來實現(xiàn),例如,想得到WaveLAN中 表示信號強度的信息時,可以通過返回這個變量來實現(xiàn)。Linux的源代碼已經(jīng)包括了兩種設備de4x5和ewrk3,它們定義并且實現(xiàn)了特定的ioctl 調(diào)用。這兩個設備的源代碼在de4x5.h,de4x5.c,ewrk3.h,ewrk3.c中(在 /usr/src/linux/drivers/net/目錄中)。這兩種設備都定義了它們特有的結構(struct ewrk3_ioctl 和 struct de4x5_ioctl)來方便用戶程序和設備驅動之間交換信息。每次調(diào)用ioctl前,用戶程序應當在相應的結構變量中設定合適的初值,并且將 ifr.ifr_data指向該值。
在我們進一步討論ewrk3和de4x5的代碼前,讓我們仔細看看ioctl調(diào)用是如何一步步地實現(xiàn)的。所有的和接口相關的ioctl請求 (SIOCxIFyyyy 和 SIOCDEVPRIVATE)將會調(diào)用dev_ioctl()(在/usr/src/linux/net/core/dev.c中)。但這只是一個包裝 器(wrapper),實際的動作將由dev_ifsioc()(也在dev.c中)來實現(xiàn)。差不多dev_ioctl()這個函數(shù)所做的所有工作只是檢 查這個調(diào)用是否已經(jīng)有了正當?shù)臋嘞蓿ɡ?,改變路由表需要有root的權限)。而dev_ifsioc()這個函數(shù)首先要做的一些事情包括得到與 ifr.ifr_name相匹配的設備的結構(在/usr/include/linux/netdevice.h中定義)。但這是在實現(xiàn)特定的接口命令 (例如:SIOCGIFADDR)之后。這些特定的接口命令被放置到一個巨大的switch語句之中。其中SIOCDEVPRIVATE命令和其他的在 0x89F0到0x89FF之間的代碼將出現(xiàn)在switch語句中的一個分支——default語句中。內(nèi)核會檢查表示設備的結構變量中,是否已經(jīng)定義了 一個與設備相關的ioctl句柄(handler)。這里的句柄是一個函數(shù)指針,它在表示設備的結構變量中do_ioctl部分。如果已經(jīng)設置了這個句 柄,那么內(nèi)核將會執(zhí)行它。
所以,如果要實現(xiàn)一個與設備相關的ioctl命令,所要做的只是編寫一個與這個設備相關的ioctl句柄,并且將表示這 個設備的結構變量中do_ioctl部分指向這個句柄。對于ewrk3這個設備,它的句柄是ewrk3_ioctl()(在ewrk3.c里面)并且相應 的表示該設備的結構變量由ewrk3_init()來初始化。在ewrk3_ioctl()的代碼中清晰的指出ifr.ifr_data是用作設備驅動程 序和用戶程序之間交換信息的。注意,這部分的內(nèi)存可以雙向的交流信息。例如,在ewrk3的驅動程序代碼中,if.ifr_data的頭兩個字節(jié)是用來表 示特殊的動作(例如,EWRK3_SET_PROM,EWRK3_CLR_PROM),而這個動作是符合使用者(驅動程序實現(xiàn)了多個與設備相關的、由 SIOCDEVPRIVATE調(diào)用的命令)的要求的。另外,ifr.ifr_data中第5個字節(jié)指向的緩沖區(qū)(buffer)被用來交換其他的信息 (如:當使用EWRK3_SET_HWADDR和EWRK3_GET_HWADDR時為硬件地址) 
在你深入ewrk3_ioctl()時,請注意一般情況下一個用戶進程不能直接訪問內(nèi)核所在的內(nèi)存。為此,驅動開發(fā)者可以使用兩個特殊的函數(shù) memcpy_tofs()和memcpy_fromfs()。內(nèi)核函數(shù)memcpy_tofs(arg1, arg2, arg3) 從地址arg2(用戶空間)向地址arg1(內(nèi)核空間)拷貝arg3個字節(jié)。類似的,memcpy_fromfs(arg1,arg2,arg3)從地址 arg2(用戶空間)向地址arg1(內(nèi)核空間)拷貝arg3個字節(jié)。在這些調(diào)用之前,verify_area()將會檢查這個進程是否擁有合適的訪問權 限。另外,注意使用printk()函數(shù)可以輸出debug信息。這個函數(shù)與printf()函數(shù)類似,但不能處理浮點類型的數(shù)。內(nèi)核代碼不能夠使用 printf()函數(shù)。printk()函數(shù)產(chǎn)生的結果將記錄在/usr/adm/messages里。如果想知道更多的關于這些函數(shù)的或者與它們相關的 信息,可以參考《Linux Kernel Hacker’s Guide》(在Linux文檔網(wǎng)站的首頁) 這本書中Supporting Functions部分。
 
使用ioctl與內(nèi)核交換數(shù)據(jù)
1. 前言 
使用ioctl系統(tǒng)調(diào)用是用戶空間向內(nèi)核交換數(shù)據(jù)的常用方法之一,從ioctl這個名稱上看,本意是針對I/O設備進行的控制操作,但實際并不限制是真正的I/O設備,可以是任何一個內(nèi)核設備即可。
2. 基本過程
在內(nèi)核空間中ioctl是很多內(nèi)核操作結構的一個成員函數(shù),如文件操作結構struct file_operations(include/linux/fs.h)、協(xié)議操作結構struct proto_ops(include/linux/net.h)等、tty操作結構struct tty_driver(include/linux/tty_driver.h)等,而這些操作結構分別對應各種內(nèi)核設備,只要在用戶空間打開這些設備, 如I/O設備可用open(2)打開,網(wǎng)絡協(xié)議可用socket(2)打開等,獲取一個文件描述符后,就可以在這個描述符上調(diào)用ioctl(2)來向內(nèi)核 交換數(shù)據(jù)。
3. ioctl(2)
ioctl(2)函數(shù)的基本使用格式為:int ioctl(int fd, int cmd, void *data)
第一個參數(shù)是文件描述符;cmd是操作命令,一般分為GET、SET以及其他類型命令,GET是用戶空間進程從內(nèi)核讀數(shù)據(jù),SET是用戶空間進程向內(nèi)核寫數(shù)據(jù),cmd雖然是一個整數(shù),但是有一定的參數(shù)格式的,下面再詳細說明;第三個參數(shù)是數(shù)據(jù)起始位置指針,
cmd命令參數(shù)是個32位整數(shù),分為四部分:dir(2b)  size(14b)  type(8b) nr(8b)
詳細定義cmd要包括這4個部分時可使用宏_IOC(dir,type,nr,size)來定義,而最簡單情況下使用_IO(type, nr)來定義就可以了,這些宏都在include/asm/ioctl.h中定義
本文cmd定義為:
#define NEWCHAR_IOC_MAGIC   'M'
#define NEWCHAR_SET    _IO(NEWCHAR_IOC_MAGIC, 0)
#define NEWCHAR_GET    _IO(NEWCHAR_IOC_MAGIC, 1)
#define NEWCHAR_IOC_MAXNR   1
要定義自己的ioctl操作,可以有兩個方式,一種是在現(xiàn)有的內(nèi)核代碼中直接添加相關代碼進行支持,比如想通過socket描述符進行 ioctl操作,可在net/ipv4/af_inet.c中的inet_ioctl()函數(shù)中添加自己定義的命令和相關的處理函數(shù),重新編譯內(nèi)核即可, 不過這種方法一般不推薦;第二種方法是定義自己的內(nèi)核設備,通過設備的ioctl()來操作,可以編成模塊,這樣不影響原有的內(nèi)核,這是最通常的做法。
4. 內(nèi)核設備
為進行ioctl操作最通常是使用字符設備來進行,當然定義其他類型的設備也可以。在用戶空間,可使用mknod命令建立一個字符類型設備文件,假設該設備的主設備號為123,次設備號為0:
mknode /dev/newchar c 123 0
如果是編程的話,可以用mknode(2)函數(shù)來建立設備文件。
建立設備文件后再將該設備的內(nèi)核模塊文件插入內(nèi)核,就可以使用open(2)打開/dev/newchar文件,然后調(diào)用ioctl(2)來傳遞數(shù)據(jù),最后用close(2)關閉設備。而如果內(nèi)核中還沒有插入該設備的模塊,open(2)時就會失敗。
由于內(nèi)核內(nèi)存空間和用戶內(nèi)存空間不同,要將內(nèi)核數(shù)據(jù)拷貝到用戶空間,要使用專用拷貝函數(shù)copy_to_user();要將用戶空間數(shù)據(jù)拷貝到內(nèi)核,要使用copy_from_user()。
要最簡單實現(xiàn)以上功能,內(nèi)核模塊只需要實現(xiàn)設備的open, ioctl和release三個函數(shù)即可,下面介紹程序片斷:
static int newchar_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg);
static int newchar_open(struct inode *inode, struct file *filep);
static int newchar_release(struct inode *inode, struct file *filep);
// 定義文件操作結構,結構中其他元素為空
struct file_operations newchar_fops = 
{
 owner:  THIS_MODULE,
 ioctl:  newchar_ioctl,
 open:  newchar_open,
 release: newchar_release,
};
// 定義要傳輸?shù)臄?shù)據(jù)塊結構
struct newchar{
 int a;
 int b;
};
#define MAJOR_DEV_NUM 123
#define DEVICE_NAME "newchar"
打開設備,非常簡單,就是增加模塊計數(shù)器,防止在打開設備的情況下刪除模塊,當然想搞得復雜的話可進行各種限制檢查,如只允許指定的用戶打開等:
static int newchar_open(struct inode *inode, struct file *filep)
{
 MOD_INC_USE_COUNT;
 return 0;
} 
關閉設備,也很簡單,減模塊計數(shù)器:
static int newchar_release(struct inode *inode, struct file *filep)
{
 MOD_DEC_USE_COUNT;
 return 0;
} 
進行ioctl調(diào)用的基本處理函數(shù)
static int newchar_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, unsigned long arg)
{
 int  ret;
// 首先檢查cmd是否合法
 if (_IOC_TYPE(cmd) != NEWCHAR_IOC_MAGIC) return -EINVAL;
 if (_IOC_NR(cmd) > NEWCHAR_IOC_MAXNR) return -EINVAL;
// 錯誤情況下的缺省返回值
 ret = EINVAL;
 switch(cmd)
 {
 case KNEWCHAR_SET:
// 設置操作,將數(shù)據(jù)從用戶空間拷貝到內(nèi)核空間
  {
   struct  newchar  nc;
   if(copy_from_user(&nc, (const char*)arg, sizeof(nc)) != 0)
    return -EFAULT;
   ret = do_set_newchar(&nc);
  }
  break;
 case KNEWCHAR_GET:
// GET操作通常會在數(shù)據(jù)緩沖區(qū)中先傳遞部分初始值作為數(shù)據(jù)查找條件,獲取全部
// 數(shù)據(jù)后重新寫回緩沖區(qū)
// 當然也可以根據(jù)具體情況什么也不傳入直接向內(nèi)核獲取數(shù)據(jù)
  {
   struct  newchar  nc;
   if(copy_from_user(&nc, (const char*)arg, sizeof(nc)) != 0)
    return -EFAULT;
   ret = do_get_newchar(&nc);
   if(ret == 0){
    if(copy_to_user((unsigned char *)arg, &nc, sizeof(nc))!=0)
     return -EFAULT;
   }
  }
  break;
 }
 return ret;
}
模塊初始化函數(shù),登記字符設備
static int __init _init(void)
{
 int  result;
// 登記該字符設備,這是2.4以前的基本方法,到2.6后有了些變化,
// 是使用MKDEV和cdev_init()來進行,本文還是按老方法
 result = register_chrdev(MAJOR_DEV_NUM, DEVICE_NAME, &newchar_fops);
 if (result < 0) {
  printk(KERN_WARNING __FUNCTION__ ": failed register character device for /dev/newchar\n");
  return result;
 }
 return 0;
}
模塊退出函數(shù),登出字符設備
static void __exit _cleanup(void)
{
 int  result;
 result = unregister_chrdev(MAJOR_DEV_NUM, DEVICE_NAME);
 if (result < 0)
  printk(__FUNCTION__ ": failed unregister character device for /dev/newchar\n");
 return;
} 
module_init(_init);
module_exit(_cleanup);
5. 結論
用ioctl()在用戶空間和內(nèi)核空間傳遞數(shù)據(jù)是最常用方法之一,比較簡單方便,而且可以在同一個ioctl中對不同的命令傳送不同的數(shù)據(jù)結構,本文只是為描述方便而在不同命令中使用了相同的數(shù)據(jù)結構。
轉載:http://yfydz.cublog.cn 
 
 
本站僅提供存儲服務,所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
ifconfig源碼分析之與內(nèi)核交互數(shù)據(jù)
ioctl函數(shù)詳解
linux下ioctl操作網(wǎng)絡接口
用ioctl獲得本地ip地址
Linux下C語言獲取所有網(wǎng)卡信息的代碼
幾個網(wǎng)絡編程常用的數(shù)據(jù)結構sockaddr_in
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服