天天看點

使用者空間與核心的互動---IOCTL

    在procfs一節中我們提到過ioctl,它的作用編寫過驅動和從事過網絡程式設計的人,一定不會陌生. 就是由于它架構的思路的精妙之處,屏蔽了大量抽象的東西.這裡我們就分析下它的使用和架構,當然這裡不會分析ioctl系統調用的實作.這裡參考資料有《linux裝置驅動程式》,《深入了解linux網絡技術内幕》 ,當然也少不了網上好的文章和文章.

       或許我們最熟悉就是檔案的操作,檔案有read、write當然還有其他一些的操作,這裡的工作就給了ioctl. 這裡我們首先說一下驅動程式裡關于ioctl函數的使用. 

       這裡舉一個網絡裝置的例子: 比如我們建立一個gre接口,在核心裡(核心版本3.1.1)net/ipv4/ip_gre.c中 我們看它的核心啟動必須初始化的部分:

        module_init(ipgre_init); 

       我們展開 ipgre_init函數:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

static

int

__init ipgre_init(

void

)

{

int

err;

printk(KERN_INFO

"GRE over IPv4 tunneling driver\\n"

);

err = register_pernet_device(&ipgre_net_ops);

if

(err < 0)

return

err;

err = gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO);

if

(err < 0) {

printk(KERN_INFO

"ipgre init: can't add protocol\\n"

);

goto

add_proto_failed;

}

err = rtnl_link_register(&ipgre_link_ops);

if

(err < 0)

goto

rtnl_link_failed;

err = rtnl_link_register(&ipgre_tap_ops);

if

(err < 0)

goto

tap_ops_failed;

out:

return

err;

tap_ops_failed:

rtnl_link_unregister(&ipgre_link_ops);

rtnl_link_failed:

gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);

add_proto_failed:

unregister_pernet_device(&ipgre_net_ops);

goto

out;

}

這裡我們隻關心 register_pernet_device(&ipgre_net_ops); 故名思意它會注冊一個網絡裝置

我們看看ipgre_net_ops的初始化

1 2 3 4 5 6

static

struct

pernet_operations ipgre_net_ops = {

.init = ipgre_init_net,

.

exit

= ipgre_exit_net,

.id   = &ipgre_net_id,

.size =

sizeof

(

struct

ipgre_net),

};

這裡我們看核心必須初始化的部分 .init = ipgre_init_net :

1 2 3 4 5 6 7 8 9 10 11 12

static

int

__net_init ipgre_init_net(

struct

net *net)

{

struct

ipgre_net *ign = net_generic(net, ipgre_net_id);

int

err;

ign->fb_tunnel_dev = alloc_netdev(

sizeof

(

struct

ip_tunnel),

"gre0"

,

ipgre_tunnel_setup);

if

(!ign->fb_tunnel_dev) {

err = -ENOMEM;

goto

err_alloc_dev;

}

......

 這裡建立gre0接口,并用 ipgre_tunnel_setup來完成部分初始化工作.

1 2 3 4 5 6 7 8 9 10 11 12 13 14

static

void

ipgre_tunnel_setup(

struct

net_device *dev)

{

dev->netdev_ops      = &ipgre_netdev_ops;

dev->destructor  = ipgre_dev_free;

dev->type        = ARPHRD_IPGRE;

dev->needed_headroom     = LL_MAX_HEADER +

sizeof

(

struct

iphdr) + 4;

dev->mtu     = ETH_DATA_LEN -

sizeof

(

struct

iphdr) - 4;

dev->flags       = IFF_NOARP;

dev->iflink      = 0;

dev->addr_len        = 4;

dev->features        |= NETIF_F_NETNS_LOCAL;

dev->priv_flags      &= ~IFF_XMIT_DST_RELEASE;

}

這裡ipgre_netdev_ops 即完成了裝置相關的操作函數的初始化(如果使用者空間調用就會調用相應的函數)

1 2 3 4 5 6 7 8 9 10 11 12

static

const

struct

net_device_ops ipgre_netdev_ops = {

.ndo_init       = ipgre_tunnel_init,

.ndo_uninit     = ipgre_tunnel_uninit,

#ifdef CONFIG_NET_IPGRE_BROADCAST

.ndo_open       = ipgre_open,

.ndo_stop       = ipgre_close,

#endif

.ndo_start_xmit     = ipgre_tunnel_xmit,

.ndo_do_ioctl       = ipgre_tunnel_ioctl,

.ndo_change_mtu     = ipgre_tunnel_change_mtu,

.ndo_get_stats      = ipgre_get_stats,

};

 我們看到了關于裝置ioctl的初始化.對沒錯. 這裡核心已經很好的把底層具體的ioctl操作和gre0裝置關聯了起來.

 下面就看看使用者空間如何調用:

 這裡簡單的說明下:

 int fd;

 struct ifreq ifr;

 fd= open("/dev/gre0");

 if(fd < 0 ) return -1;

 ...

 if( ioctl(fd,SIOCGETTUNNEL,&ifr))

 ..... 

 當然使用者空間也要有SIOCGETTUNNEL的定義 ,核心空間的是在include/linux/if_tunnel.h中定義.這裡所說是驅動裡私有的ioctl的使用.

 #define SIOCGETTUNNEL (SIOCDEVPRIVATE + 0) 

#define SIOCADDTUNNEL (SIOCDEVPRIVATE + 1)

 #define SIOCDELTUNNEL (SIOCDEVPRIVATE + 2)

......

#define SIOCCHG6RD (SIOCDEVPRIVATE + 11)

 在linux系統預設給dev預留了16個私有的ioctl,在include/linux/sockios.h中

1 2 3 4 5 6 7 8 9 10 11 12

#define SIOCDEVPRIVATE  0x89F0 

 但是在實際情況中這個16個并不夠用,或者會産生混亂或者沖突,是以在驅動中還有另外一種定義和實作方法,也更為常用: 即通用驅動ioctl的實作方法:

     IO(type,nr),_IOR(type,nr,datatype),_IOW(type,nr,datatype)。它們在<linux/ioctl.h>包含的<asm/ioctl.h>中有定義,是以在我們的驅動中應包含這個頭檔案。其中_IO()用做無參數的指令編号,_IOR()用做從驅動中讀取資料的指令編号,_IOW()用做寫入資料指令。 LDD中用:define SCULL_IOC_MAGIC 'k' ,define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC,1,int)表示SCULL_IOCSQUANTUM指令編号為向驅動中寫資料,指令編号為1,傳送參數類型為int.

     下面就看一下簡單的使用方法:

 #define MY_MAGIC 'k' 

 #define MY_GDEVNAME _IOR(MY_MAGIC, 1,int)

 當然這個定義需要核心和使用者空間都有定義. 這裡不再詳述,具體請閱讀ldd3 第六章進階字元驅動程式操作一章.

 這裡分析當然會有不到之處,僅作為好的學習的開始.

 當然上面主要說明了驅動中ioctl的使用,也就不得不說下網絡程式設計中ioctl的使用,對于網絡程式設計中自定義這裡不再說明.

 這裡僅就已經有的很多指令調用做一個簡單的總結. 

 具體的定義在include/linux/sockios.h中

 ioctl 函數

 本函數影響由fd 參數引用的一個打開的檔案。

 #include<unistd.h> int ioctl( int fd, int request, ... ); 

傳回0 :成功

 -1 :出錯

 第三個參數總是一個指針,但指針的類型依賴于request 參數。 

我們可以把和網絡相關的請求劃分為6 類:

 套接口操作

 檔案操作

 接口操作 

ARP 

高速緩存操作 

路由表操作

 流系統

 具體的使用和分類請參考《unix網絡程式設計》.下面貼出網絡ioctl調用流程圖: 

使用者空間與核心的互動---IOCTL

這樣ioctl就統一了起來,驅動的ioctl和網絡的ioctl.其實實作思路都是一樣的,見開始gre0部分的實作.

來源:http://blog.chinaunix.net/uid-20786208-id-3479965.html

繼續閱讀