在Linux內核模塊的開發過程中,經常涉及到運行在用戶空間上的應用程序與內核模塊進行交互,ioctl系統調用是常用的一種方式。本文並不涉及vlan的具體原理,僅通過vconfig與vlan內核模塊進行交互為例,講解通過ioctl系統調用來實現用戶空間與內核驅動交互的過程。
1、用戶空間命令行配置工具
vconfig是vlan在用戶空間上的命令行配置工具,在vconfig的源碼中,可以看到在用戶空間上與內核通信部分,其實僅做了三件事。
接收用戶輸入,填充vlan_ioctl_args結構體,vlan_ioctl_args結構體在linux/linux-2.6/include/linux/if_vlan.h中定義。
1 struct vlan_ioctl_args { 2 int cmd; /* Should be one of the vlan_ioctl_cmds enum above. */ 3 char device1[24]; 4 5 union { 6 char device2[24]; 7 int VID; 8 unsigned int skb_priority; 9 unsigned int name_type; 10 unsigned int bind_type; 11 unsigned int flag; /* Matches vlan_dev_info flags */ 12 } u; 13 14 short vlan_qos; 15 };
創建socket描述符
1 /* We use sockets now, instead of the file descriptor */ 2 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 3 fprintf(stderr, "FATAL: Couldn't open a socket..go figure!\n"); 4 exit(2); 5 }
ioctl請求
1 /* add */ 2 if (strcasecmp(cmd, "add") == 0) { 3 if_request.cmd = ADD_VLAN_CMD; 4 if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { 5 fprintf(stderr,"ERROR: trying to add VLAN #%u to IF -:%s:- error: %s\n", 6 vid, if_name, strerror(errno)); 7 } 8 }//if
2、內核空間— vlan驅動
vlan驅動工作在內核空間,因此需要相應的內核API去讀取用戶空間的數據。在/linux/linux-2.6/net/8021q/vlan.c的vlan模塊初始化函數vlan_proto_init中使用vlan_ioctl_set注冊vlan_ioctl_handler函數,使之用於響應用戶空間的ioctl請求。
1 vlan_ioctl_set(vlan_ioctl_handler);
在vlan_ioctl_handler函數中,首先使用copy_from_user函數從用戶空間拷貝數據到內核空間。
1 struct vlan_ioctl_args args; 2 3 if (copy_from_user(&args, arg, sizeof(struct vlan_ioctl_args))) 4 return -EFAULT;
在/linux/linux-2.6/net/socket.c中可以查看到vlan_ioctl_set的定義,它的參數是一個函數指針。當把vlan_ioctl_handler函數作為參數傳遞時,vlan_ioctl_hook指向其首地址,通過這種方式把對特定ioctl的響應處理方法注冊進內核。用戶可以添加不同的命令,只需在模塊的vlan_ioctl_handler中對相應的命令進行解析、響應即可。
1 static DEFINE_MUTEX(vlan_ioctl_mutex); 2 static int (*vlan_ioctl_hook) (void __user *arg); 3 4 void vlan_ioctl_set(int (*hook) (void __user *)) 5 { 6 mutex_lock(&vlan_ioctl_mutex); 7 vlan_ioctl_hook = hook; 8 mutex_unlock(&vlan_ioctl_mutex); 9 } 10 11 EXPORT_SYMBOL(vlan_ioctl_set);
而后的sock_ioctl函數中調用了vlan_ioctl_hook,void __user *argp指向用戶空間傳遞的參數。
1 case SIOCGIFVLAN: 2 case SIOCSIFVLAN: 3 err = -ENOPKG; 4 if (!vlan_ioctl_hook) 5 request_module("8021q"); 6 7 mutex_lock(&vlan_ioctl_mutex); 8 if (vlan_ioctl_hook) 9 err = vlan_ioctl_hook(argp); 10 mutex_unlock(&vlan_ioctl_mutex); 11 break;