用户空间与内核驱动的交互过程 — ioctl
在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;