午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

linux內(nèi)核ioctl網(wǎng)絡(luò)控制框架實現(xiàn)分析

 womking 2009-05-17
linux內(nèi)核IOCTL網(wǎng)絡(luò)控制框架實現(xiàn)分析
概述
從ioctl這個名稱上看,它是設(shè)備驅(qū)動程序中對設(shè)備的 I/O通道進(jìn)行管理的函數(shù)。所謂對I/O通道進(jìn)行管理,就是對設(shè)備的一些特性進(jìn)行控制,例如串口的傳輸波特率、馬達(dá)的轉(zhuǎn)速等等, 但實際上ioctl所處理的對象并不限制是真正的I/O設(shè)備,還可以是其它任何一個內(nèi)核設(shè)備.ioctl以系統(tǒng)調(diào)用的形式提供了一條用戶與內(nèi)核交互的便捷 途徑。當(dāng)前一些寬帶計費(fèi)網(wǎng)關(guān)、防火墻系統(tǒng)均利用Ioctl與內(nèi)核良好的通信互動特點(diǎn)支持用戶對基于內(nèi)核模塊的軟件系統(tǒng)的控制.本文針對i386平臺下的 ioctl內(nèi)核網(wǎng)絡(luò)源代碼控制框架進(jìn)行剖析解釋,在文章最后列舉一個實例,通過編程實踐展示如何通過ioctl控制函數(shù)實現(xiàn)自定義的功能的控制,使讀者可 以對ioctl實現(xiàn)原理有一個全面的認(rèn)識,本文只對ioctl實現(xiàn)流程框架做一定的敘述,并不會深入到具體的控制函數(shù)。為了更好的閱讀本文,要求讀者對 Linux 下的網(wǎng)絡(luò)編程有一定的了解。
本文約定
1、以下內(nèi)容如果沒有特殊說明,均參照linux內(nèi)核2.4.0版本
2、“->”箭頭符表示函數(shù)調(diào)用關(guān)系,如sys_socket->sock_map_fd表示sys_socket函數(shù)調(diào)用的sock_map_fd函數(shù)。
3、第五節(jié)的實踐是在redhat9上實現(xiàn),基于2.4.20內(nèi)核,但本文所述在2.4內(nèi)核下都適用。
二、用戶空間ioctl控制函數(shù)調(diào)用形式
通過man 2 ioctl命令查看ioctl函數(shù)的調(diào)用形式類似如下:
#include <sys/ioctl.h>
int ioctl(int d, int request, ...);
其中d就是用戶程序打開設(shè)備時使用open函數(shù)返回的文件描述符,request就是用戶程序?qū)υO(shè)備的控制命令,至于后面的省略號,則是一些補(bǔ)充參數(shù),一般最多一個,有或沒有是和request的意義相關(guān)的,詳情請參考man 2 ioctl_list以了解更多。ioctl函數(shù)是文件結(jié)構(gòu)中的一個屬性分量,就是說如果驅(qū)動程序提供了對ioctl的支持,用戶就可以在用戶程序中使用ioctl函數(shù)控制設(shè)備的I/O通道或其它一些自己想要控制且設(shè)備支持的功能。
內(nèi)核實現(xiàn)ioctl()函數(shù)的是sys_ioctl(),在內(nèi)核中主要調(diào)用框架圖如下,它清晰地給我們展示ioctl的控制傳遞框架,我們接下來的內(nèi)容將根據(jù)此圖向大家做詳細(xì)的解釋:

四、IOCTL框架源代碼分析
根據(jù)前面的圖示,我們從入口函數(shù)sys_ioctl開始分析:
4.1、入口函數(shù):sys_ioctl
以下源碼在fs/ioctl.c中,其中刪除了部分與網(wǎng)絡(luò)控制關(guān)系不大的代碼:
asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)
{    
       …//根據(jù)fd獲取文件結(jié)構(gòu)(struct file)
       lock_kernel();
       switch (cmd) {
              case FIOCLEX://對文件設(shè)置專用標(biāo)志,通知內(nèi)核自動關(guān)閉打開的文件
              …
              case FIONCLEX://與FIOCLEX標(biāo)志相反,清除專用標(biāo)志
              …
              case FIONBIO://將文件操作設(shè)置成阻塞/非阻塞
              …
              case FIOASYNC:// 將文件操作設(shè)置成同步/異步IO
              …    //以上省略的代碼是關(guān)于具體的磁盤文件系統(tǒng)的控制處理,
                     //關(guān)于socket的阻塞或非阻塞等設(shè)置很簡單,有興趣的讀者直接閱讀源碼吧
default: //文件其它部分的處理被放在了default部分
                     error = -ENOTTY;
                     if (S_ISREG(filp->f_dentry->d_inode->i_mode)) //普通文件
                            error = file_ioctl(filp, cmd, arg); //
                     else if (filp->f_op && filp->f_op->ioctl) //socket控制在此處理
                            error = filp->f_op->ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
       }
       unlock_kernel();
       fput(filp);
out:
       return error;
}
注意上面藍(lán)色字體部分,即為調(diào)用網(wǎng)絡(luò)部分的代碼入口。大家注意在default情況下,有個S_ISREG宏對文件類型作判斷,其定義在include/linux/stat.h中:
#define S_ISLNK(m)     (((m) & S_IFMT) == S_IFLNK) //符號連接文件
#define S_ISREG(m)     (((m) & S_IFMT) == S_IFREG) //普通文件
#define S_ISDIR(m)      (((m) & S_IFMT) == S_IFDIR)   //目錄文件
#define S_ISCHR(m)     (((m) & S_IFMT) == S_IFCHR) //字符設(shè)備文件
#define S_ISBLK(m)     (((m) & S_IFMT) == S_IFBLK)   //塊設(shè)備文件
#define S_ISFIFO(m)    (((m) & S_IFMT) == S_IFIFO)   //管道文件
#define S_ISSOCK(m)   (((m) & S_IFMT) == S_IFSOCK)       //socket套接字文件
因為linux內(nèi)核把socket套接字當(dāng)作文件來處理, 內(nèi)核在創(chuàng)建socket套接字時,為套接字分配文件id以及生成與id對應(yīng)的文件節(jié)點(diǎn),節(jié)點(diǎn)的i_mode域是代表文件類型的位域標(biāo)志字段,所以內(nèi)核定義 了上述宏來簡化判斷操作。由于套接字文件不屬于普通文件之列,所以程序直接執(zhí)行藍(lán)色字體部分。
4.2、入口函數(shù)跳轉(zhuǎn)
我們來看一下filp->f_op->ioctl函數(shù)指針指向了什么函數(shù),可以參考net/socket.c文件中的sys_socket->sock_map_fd函數(shù)中的一行代碼(藍(lán)色部分代碼):
static int sock_map_fd(struct socket *sock)
{
       …
       sock->file = file;
       file->f_op = sock->inode->i_fop = &socket_file_ops;
       file->f_mode = 3;
       file->f_flags = O_RDWR;
       file->f_pos = 0;
       …
}
內(nèi)核在用戶創(chuàng)建socket套接字時就將此套接字的文件操 作函數(shù)指針初始化了。從上面的代碼我們可以看到,filp->f_op以及文件對應(yīng)的socket節(jié)點(diǎn)的i_fop指針都被賦值為指向 socket_file_ops結(jié)構(gòu),所以我們來看看內(nèi)核是如何實現(xiàn)這個控制過程的轉(zhuǎn)移的。還是在內(nèi)核的net/socket.c文件中,定義了 socket_file_ops結(jié)構(gòu)如下:
static struct file_operations socket_file_ops = {
llseek:             sock_lseek,
read:                     sock_read,
write:             sock_write,
poll:               sock_poll,
ioctl:              sock_ioctl,
mmap:            sock_mmap,
open:              sock_no_open,       /* special open code to disallow open via /proc */
release:           sock_close,
fasync:           sock_fasync,
readv:             sock_readv,
writev:           sock_writev
};
從上面的代碼來看,這個結(jié)構(gòu)定義了socket描述字的文 件操作函數(shù),如對描述字調(diào)用read函數(shù)讀數(shù)據(jù)時最終將訪問sock_read函數(shù),對描述字調(diào)用write函數(shù)讀數(shù)據(jù)時最終將訪問sock_write 函數(shù),等等。而對ioctl的訪問最終將轉(zhuǎn)化為調(diào)用sock_ioctl函數(shù),看到此處我們明白了,filp->f_op->ioctl (filp->f_dentry->d_inode, filp, cmd, arg)調(diào)用實質(zhì)上轉(zhuǎn)化為對sock_ioctl函數(shù)的調(diào)用。
4.3、sock_ioctl函數(shù)
sock_ioctl函數(shù)依然在net/socket.c文件中,列出如下:
int sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
       struct socket *sock;
       int err;
       unlock_kernel();
       sock = socki_lookup(inode);
       err = sock->ops->ioctl(sock, cmd, arg);
       lock_kernel();
       return err;
}
此處函數(shù)引入inode參數(shù)實質(zhì)是通過節(jié)點(diǎn)找到套接字對應(yīng) 的socket結(jié)構(gòu),通過socket的struct proto_ops類型的字段ops執(zhí)行具體的控制操作(即sock->ops->ioctl(sock, cmd, arg)),函數(shù)socki_lookup也在文件net/socket.c中,列出如下:
extern __inline__ struct socket *socki_lookup(struct inode *inode)
{
       return &inode->u.socket_i;
}
寫到這大家可能要問為什么不直接在filp->f_op->ioctl函數(shù)指針指向的函數(shù)里面執(zhí)行ioctl控制操作而要做兩次跳轉(zhuǎn)呢?其實這與linux良好的設(shè)計規(guī)范和業(yè)務(wù)支持的實際情況都有關(guān)系,第一次跳轉(zhuǎn)是轉(zhuǎn)入套接字單獨(dú)處理,因為內(nèi)核中網(wǎng)絡(luò)部分是非常重要的,可以與文件系統(tǒng)相提并論,將網(wǎng)絡(luò)部分獨(dú)立出來處理在設(shè)計思路上更清晰;另外,linux內(nèi)核支持不同層次、類型的套接字,如ipv4、ipv6套接字以及sock_raw原始套接字,對于這些套接字的處理有一定的相似性,又有其不同的地方。所以引入第二次跳轉(zhuǎn)的目的也即在此,以支持對不同的協(xié)議類型的套接字進(jìn)行不同控制,詳情見下面小節(jié)的介紹
 
4.4、二次跳轉(zhuǎn)
       閑話少說,步入正題。接下來我們看看sock->ops->ioctl函數(shù)指針調(diào)用了什么函數(shù),首先看看    sock變量的結(jié)構(gòu)類型struct socket,大家要多注意這個結(jié)構(gòu),在后面我們也列出了相關(guān)結(jié)構(gòu)相互引用圖中涉及到的這個結(jié)構(gòu)的幾個字段,以加深大家的印象.結(jié)構(gòu)的源代碼在include/linux/Net.h文件中:
struct socket
{
       socket_state           state;
       unsigned long         flags;
       struct proto_ops     *ops;
       struct inode           *inode;
       struct fasync_struct      *fasync_list;   /* Asynchronous wake up list       */
       struct file        *file;              /* File back pointer for gc     */
       struct sock            *sk;
       wait_queue_head_t wait;
};
套接字就是通過結(jié)構(gòu)中ops指針來執(zhí)行具體的ioctl控制函數(shù)的。struct proto_ops定義在同樣的頭文件中:
struct proto_ops {
int       family;
int       (*release)       (struct socket *sock);
int       (*bind)           (struct socket *sock, struct sockaddr *umyaddr, int sockaddr_len);
int       (*connect)      (struct socket *sock, struct sockaddr *uservaddr, int sockaddr_len, int flags);
int       (*socketpair) (struct socket *sock1, struct socket *sock2);
int       (*accept) (struct socket *sock, struct socket *newsock,    int flags);
int       (*getname) (struct socket *sock, struct sockaddr *uaddr, int *usockaddr_len, int peer);
unsigned int (*poll)    (struct file *file, struct socket *sock, struct poll_table_struct *wait);
int       (*ioctl)    (struct socket *sock, unsigned int cmd, unsigned long arg);
int       (*listen)   (struct socket *sock, int len);
int       (*shutdown)   (struct socket *sock, int flags);
int       (*setsockopt) (struct socket *sock, int level, int optname, char *optval, int optlen);
int       (*getsockopt) (struct socket *sock, int level, int optname, char *optval, int *optlen);
int (*sendmsg) (struct socket *sock, struct msghdr *m, int total_len, struct scm_cookie *scm);
int (*recvmsg) (struct socket *sock, struct msghdr *m, int total_len, int flags, struct scm_cookie *scm);
int       (*mmap) (struct file *file, struct socket *sock, struct vm_area_struct * vma);
};
補(bǔ)充一下基礎(chǔ)知識,一個套接字接口在邏輯上有三個要素:網(wǎng)域,類型和規(guī)程(協(xié)議).
網(wǎng)域:表明套接字接口用于哪一中網(wǎng)絡(luò)或這說哪一族網(wǎng)絡(luò)規(guī)程.就是我們通常說的地址族(family),常見的有AF_UNIX/AF_INET/AF_X25/AF_IPX等待.
類型:表明通訊中所遵循的模式,主要有兩種模式:”有連接”和”無連接”,對應(yīng)到以太網(wǎng)就是SOCK_STREAM和SOCK_DGRAM兩種.
規(guī)程:具體的網(wǎng)絡(luò)協(xié)議.通常,網(wǎng)域和類型基本就能夠確定使用的規(guī)程了.
這里的proto_ops結(jié)構(gòu)就是通過不同的實例來支持具 體的網(wǎng)域的不同類型、規(guī)程所使用的通信函數(shù),每個網(wǎng)域都有多種類型、多種規(guī)程,所以也有多個proto_ops實例,給這個實例賦值具體規(guī)程的處理函數(shù), 如ipv4的有連接和無連接實例所指定的控制函數(shù)都是inet_ioctl(如果處理不同也可以指向不同的控制函數(shù)),這樣可以使具體的控制操作轉(zhuǎn)向具體 的處理,細(xì)節(jié)實現(xiàn)我們下一小節(jié)介紹.
構(gòu)造內(nèi)核時,內(nèi)核會初始化網(wǎng)絡(luò)地址族,即初始化net_families[NRPORO]全局量, 這是一個靜態(tài)指針數(shù)組。每個網(wǎng)域地址族的初始化函數(shù)都由其中一個元素來表征,例如,“INET”和它的初始程序地址分別是PF_INET(等同于 AF_INET)和inet_create。當(dāng)套接口啟動時被初始化時,要調(diào)用每一網(wǎng)域初始化程序,為具體的類型指定處理函數(shù),內(nèi)核初始化網(wǎng)域地址族后net_families[NRPORO]變量的相關(guān)字段取值狀態(tài)示意圖如下:
對IPV4地址族來說,這個初始化函數(shù)就是inet_create,其代碼在net/ipv4/af_inet.c中:
static int inet_create(struct socket *sock, int protocol)
{
       …
       switch (sock->type) {
       case SOCK_STREAM:
              if (protocol && protocol != IPPROTO_TCP) //類型與規(guī)程檢測
                     goto free_and_noproto;
              protocol = IPPROTO_TCP;
              prot = &tcp_prot;
              sock->ops = &inet_stream_ops; //此處指定函數(shù)跳轉(zhuǎn)表
              break;
       case SOCK_SEQPACKET:
              goto free_and_badtype;
       case SOCK_DGRAM:
              if (protocol && protocol != IPPROTO_UDP)
                     goto free_and_noproto;
              protocol = IPPROTO_UDP;
              sk->no_check = UDP_CSUM_DEFAULT;
              prot=&udp_prot;
              sock->ops = &inet_dgram_ops; //此處指定函數(shù)跳轉(zhuǎn)表
              break;
       case SOCK_RAW:
              if (!capable(CAP_NET_RAW)) //檢驗是否有創(chuàng)建原始套接字的權(quán)限
                     …
              sock->ops = &inet_dgram_ops;//
              if (protocol == IPPROTO_RAW)
                     sk->protinfo.af_inet.hdrincl = 1;
              break;
       default:
              goto free_and_badtype;
       }
}
從上面的代碼可以看出:已注冊的網(wǎng)域的類型所對應(yīng)的操作被存在socket結(jié)構(gòu)的ops 指針中,它就是指向具體的proto_ops數(shù)據(jù)結(jié)構(gòu)實例,如inet_stream_ops、inet_dgram_opsproto_ops結(jié)構(gòu)由地址族類型和一系列指向與特定地址族對應(yīng)的socket操作函數(shù)的指針組成。ops 字段通過地址族標(biāo)識符來索引,接下來我們看看proto_ops結(jié)構(gòu)。
4.5、struct proto_ops結(jié)構(gòu)實例
       前面說過,具體的ioctl執(zhí)行過程時通過兩次跳轉(zhuǎn)而來,其中第二次就是針對各個不同層次、類型的套接字。我們來看看內(nèi)核中所定義的各個具體的proto_ops結(jié)構(gòu)實例以分析不同的控制執(zhí)行流程.      內(nèi)核中為每個規(guī)程定義了一個proto_ops結(jié)構(gòu)實例,常見的如下:
1、在net/ipv4/Af_inet.c文件中:
struct proto_ops inet_stream_ops = {
       …   
       poll:        tcp_poll,
       ioctl:              inet_ioctl,
       listen:             inet_listen,
};
struct proto_ops inet_dgram_ops = {
       …   
       poll:        datagram_poll,
       ioctl:              inet_ioctl,
       listen:             sock_no_listen,
       …
};
可見這兩個實例有相當(dāng)多的處理函數(shù)都是一樣的,并且最終調(diào)用相同的控制函數(shù)inet_ioctl.
2、在net/ipv6/Af_inet6.c文件中提供了inet6_stream_ops和inet6_dgram_ops,其地址族及ioctl處理函數(shù)分別為PF_INET6和inet6_ioctl:
struct proto_ops inet6_stream_ops = {
       family:            PF_INET6,
       ioctl:              inet6_ioctl,                    /* must change */
       …
};
struct proto_ops inet6_dgram_ops = {
       family:            PF_INET6,
       ioctl:              inet6_ioctl,                    /* must change */
       …
};
3、在net/packet/Af_ packet 6.c文件中提供了packet_ops_spkt和packet_ops,其地址族及ioctl處理函數(shù)分別為PF_PACKET和packet_ioctl:
struct proto_ops packet_ops = {
       family:            PF_PACKET,
       ioctl:              packet_ioctl,
       …
};
還有x25和ipx、netlinkunix等等地址族所對應(yīng)的文件提供了各自的協(xié)議規(guī)程操作函數(shù)指針以支持不同的ioctl處理函數(shù),大家有興趣可以參考內(nèi)核相關(guān)源碼.
可見,通過二次跳轉(zhuǎn)表,內(nèi)核可以支持不同協(xié)議規(guī)程做不同的操作,包括控制處理。本文把重點(diǎn)放在ipv4的ioctl控制函數(shù),引導(dǎo)大家深入到其處理源碼.

 

4.6、inet_ioctl函數(shù)
       由于inet_ioctl函數(shù)內(nèi)容分支很多,但功能、處理不難理解,所以我把一些不常見的內(nèi)容都省去,挑簡單重要的說,完全在于拋磚引玉:
static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
       …
       switch(cmd)
       {
              case FIOSETOWN://設(shè)置屬主
              case SIOCSPGRP://設(shè)置進(jìn)程組
                     err = get_user(pid, (int *) arg);
                     if (err)
                            return err;
                     if (current->pid != pid && current->pgrp != -pid &&
                         !capable(CAP_NET_ADMIN))
                            return -EPERM;
                     sk->proc = pid;
                     return(0);
              case FIOGETOWN://獲取屬主
              case SIOCGPGRP://獲取進(jìn)程組
                     return put_user(sk->proc, (int *)arg);
              case SIOCGSTAMP://
                     if(sk->stamp.tv_sec==0)
                            return -ENOENT;
                     err = copy_to_user((void *)arg,&sk->stamp,sizeof(struct timeval));
                     if (err)
                            err = -EFAULT;
                     return err;
              case SIOCADDRT://增加路由
              case SIOCDELRT://刪除路由
              case SIOCRTMSG:
                     return(ip_rt_ioctl(cmd,(void *) arg));//IP路由配置
              case SIOCDARP://刪除arp項
              case SIOCGARP://獲取arp項
              case SIOCSARP://創(chuàng)建/修改arp項
                     return(arp_ioctl(cmd,(void *) arg));//arp配置
              case SIOCGIFADDR://獲取接口地址
              case SIOCSIFADDR://設(shè)置接口地址
              case SIOCGIFBRDADDR://獲取廣播地址
              case SIOCSIFBRDADDR://設(shè)置廣播地址
              case SIOCGIFNETMASK://獲取網(wǎng)絡(luò)掩碼
              case SIOCSIFNETMASK://設(shè)置網(wǎng)絡(luò)掩碼
              case SIOCGIFDSTADDR://獲取p2p地址
              case SIOCSIFDSTADDR://設(shè)置p2p地址
              case SIOCSIFPFLAGS: //
              case SIOCGIFPFLAGS:
              case SIOCSIFFLAGS://設(shè)置接口標(biāo)志
                     return(devinet_ioctl(cmd,(void *) arg));//網(wǎng)絡(luò)接口相關(guān)配置,linux內(nèi)核自帶的ifconfig
//的很多處理都是通過這里實現(xiàn)的
              case SIOCGIFBR:
              case SIOCSIFBR://網(wǎng)橋設(shè)置,稍后的實例就是介紹如何截獲網(wǎng)橋控制鉤子
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) //如果內(nèi)核支持網(wǎng)橋功能
#ifdef CONFIG_KMOD//若支持內(nèi)核模塊動態(tài)加載
                     if (br_ioctl_hook == NULL)//網(wǎng)橋鉤子為空則動態(tài)請求模塊
                            request_module("bridge");//加載網(wǎng)橋模塊
#endif
                     if (br_ioctl_hook != NULL)
                            return br_ioctl_hook(arg);//通過鉤子函數(shù)處理命令參數(shù)
#endif
              case SIOCGIFDIVERT://
              case SIOCSIFDIVERT:
#ifdef CONFIG_NET_DIVERT
                     return(divert_ioctl(cmd, (struct divert_cf *) arg));
#else
                     return -ENOPKG;
#endif     /* CONFIG_NET_DIVERT */
                     return -ENOPKG;
                    
              case SIOCADDDLCI://
              case SIOCDELDLCI:// 數(shù)據(jù)鏈路連接標(biāo)識控制
#ifdef CONFIG_DLCI
                     lock_kernel();
                     err = dlci_ioctl(cmd, (void *) arg);//控制函數(shù)
                     unlock_kernel();
                     return err;
#endif
#ifdef CONFIG_DLCI_MODULE
#ifdef CONFIG_KMOD
                     if (dlci_ioctl_hook == NULL)//如果鉤子函數(shù)為空,則加載模塊
                            request_module("dlci");
#endif
                     if (dlci_ioctl_hook) {//鉤子函數(shù)指針不空
                            lock_kernel();
                            err = (*dlci_ioctl_hook)(cmd, (void *) arg);//調(diào)用鉤子函數(shù)
                            unlock_kernel();
                            return err;
                     }
#endif
                     return -ENOPKG;
              default:
                     …
                     return err;
       }
       /*NOTREACHED*/
       return(0);
}
       從上面的函數(shù)代碼來看,同套接字有關(guān)的控制請求主要有如下幾類:
       1、文件操作
       2、套接字操作
       3、路由選項操作
       4、接口操作
       5、ARP高速緩存操作
       6、網(wǎng)橋控制
       7、數(shù)據(jù)鏈路連接標(biāo)識控制
       結(jié)合代碼中的注釋,讀者不難理解具體的控制分支。具體的 控制處理就轉(zhuǎn)到具體的函數(shù)里面去處理了,例如關(guān)于內(nèi)核自帶的命令工具ifconfig對ip地址的配置處理,基本都在devinet_ioctl函數(shù)中; 關(guān)于arp命令的處理都在arp_ioctl中處理;關(guān)于路由配置都在ip_rt_ioctl中處理。其中參數(shù)arg是用戶空間傳來的自定義的數(shù)據(jù),可以 是結(jié)構(gòu),可以是聯(lián)合或其它一些更復(fù)雜的類型,由具體的業(yè)務(wù)模塊來解釋處理。在隨后的實踐中,我們就是通過arg的不同解釋來做不同的處理。
4.7、網(wǎng)絡(luò)主要結(jié)構(gòu)相關(guān)字段相互引用圖
通過上面的分析,大家應(yīng)該大致明白了linux內(nèi)核網(wǎng)絡(luò)ioctl控制框架的實現(xiàn)了。下面是在內(nèi)核網(wǎng)絡(luò)組件初始化后,ipv4相關(guān)的結(jié)構(gòu)字段之間相互引用圖,供大家閱讀是參考:
結(jié)合前面主要函數(shù)調(diào)用關(guān)系圖與源碼分析,讀者可以很清晰的 順著上圖所示的箭頭,從ioctl入口函數(shù)開始,方便地找到具體的處理模塊.其中,文件操作對象socket_file_ops調(diào)用sock_ioctl ()時,通過inode節(jié)點(diǎn)的socket_i字段最終找到inet_ioctl()函數(shù).
 
       此處介紹通過自己編寫控制程序,在用戶空間調(diào)用ioctl函數(shù)控制內(nèi)核顯示一行信息的例子供大家參考:
1.編寫運(yùn)行于用戶空間的控制程序
(1)一般先定義自己的結(jié)構(gòu)參數(shù)類型,如下:
typedef struct stMyIoctlArg {
    unsigned int cmd;//其實就是第一個參數(shù),當(dāng)作自己的命令參數(shù)
    unsigned int arg1; //用于提供給具體的命令參數(shù)
    unsigned int arg2;
    …//如果有更多參數(shù)直接加在后面
} IOCTL_ARG,P IOCTL_ARG;
(2)然后在main中賦值并調(diào)用ioctl函數(shù):
#define FIOCSMYSHOW 0x1234
int main( int argc, char **argv )
{
       int fd;
IOCTL_ARG arg; //需要組織傳遞到內(nèi)核的參數(shù)
arg.cmd= FIOCSMYSHOW //自定義命令
…//其它參數(shù)賦值
int fd = socket( AF_INET, SOCK_STREAM, 0 );//創(chuàng)建控制socket
    if ( fd < 0 )
    {
        perror( "socket failed" );
        return 0;
    }
    if ( ioctl( fd, SIOCSIFBR, &arg) < 0 ) //通過網(wǎng)橋請求參數(shù)來控制內(nèi)核作相關(guān)操作
   {
        perror( "ioctl( SIOCSIFBR ) failed" );
        close( fd );
        return 0;
}
close(fd);
}
例子源代碼
2.內(nèi)核功能支持
2.1、修改內(nèi)核相關(guān)代碼:
(1)在內(nèi)核include/linux/sockios.h的尾部加入前面定義的公共的結(jié)構(gòu)與常量:
typedef struct stMyIoctlArg {
    unsigned int cmd;//其實就是第一個參數(shù),當(dāng)作自己的命令參數(shù)
    unsigned int arg1;
    unsigned int arg2;
    …//如果有更多參數(shù)直接加在后面
} IOCTL_ARG,P IOCTL_ARG;
#define FIOCSMYSHOW 0x1234
(2)在inet_ioctl函數(shù)網(wǎng)橋處理分支處增加如下藍(lán)色字體內(nèi)容:
IOCTL_ARG myarg;//inet_ioctl函數(shù)開始時加入此變量定義
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
if ( copy_from_user( & myarg, (void *) arg, sizeof(IOCTL_ARG ) ) ) //拷貝用戶空間參數(shù)
        return -EFAULT;
switch (myarg.cmd ){
       case FIOCSMYSHOW ://解析自己的命令
              printk(KERN_INFO “get ioctl hook.\n”); //可以增加對arg1/arg2等參數(shù)的解析處理
              return 0; //直接返回
              break;
       …
       default:
              break;
}
#ifdef CONFIG_KMOD
                     if (br_ioctl_hook == NULL)
                            request_module("bridge");
#endif
                     if (br_ioctl_hook != NULL)
                            return br_ioctl_hook(arg);
#endif
內(nèi)核修改文件: 。注意在修改內(nèi)核代碼后,用README中的命令編譯一下修改的文件,沒有錯誤才編譯內(nèi)核,避免走彎路重新編譯。
2.2、編譯內(nèi)核
具體編譯過程請參照網(wǎng)絡(luò)上的文章,我所用到的重要的命令有:
make mrproper
make oldconfig
make xconfig //在network options中選擇802.1 ethernet bridge選項支持網(wǎng)橋功能
make dep
make bzImage
make modules
make modules_install
depmod -a
cp System.map /boot/System.map-2.4.20-8custom
cp arch/i386/boot/bzImage /boot/vmlinuz-2.4.20-8custom
new-kernel-pkg --install --mkinitrd --depmod 2.4.20-8custom
3.運(yùn)行控制程序
內(nèi)核編譯前運(yùn)行顯示:
內(nèi)核編譯后運(yùn)行顯示:
4.查看結(jié)果
可以通過dmesg | grep hook命令查看結(jié)果,顯示:
這正是我們在內(nèi)核中要打印的字符,說明我們的控制命令已經(jīng)通知給內(nèi)核了。
       ioctl系統(tǒng)調(diào)用是最常用的用戶與內(nèi)核空間交互的手段 之一,linux系統(tǒng)自帶的相當(dāng)多的命令工具尤其是網(wǎng)絡(luò)控制工具都是采用ioctl控制框架實現(xiàn)了用戶和內(nèi)核通信的橋梁,在當(dāng)前一些基于內(nèi)核模塊技術(shù)的軟 件系統(tǒng)中也有重要的用途,如某些寬帶計費(fèi)網(wǎng)關(guān)、防火墻軟件、網(wǎng)絡(luò)交換機(jī)等。了解ioctl控制框架,無疑會提高我們對linux內(nèi)核通信機(jī)制的認(rèn)識,也可 以指導(dǎo)我們的實踐工作。
1 linux內(nèi)核源代碼情景分析
2 linux內(nèi)核2.4.0源碼
3 ioctl man手冊
4 ifconfig工具源碼

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多