在Linux内核模块的开发过程中,经常涉及到运行在用户空间上的应用程序与内核模块进行交互,ioctl系统调用是常用的一种方式。本文并不涉及vlan的具体原理,仅通过vconfig与vlan内核模块进行交互为例,讲解通过ioctl系统调用来实现用户空间与内核驱动交互的过程。
1、用户空间命令行配置工具
vconfig是vlan在用户空间上的命令行配置工具,在vconfig的源码中,可以看到在用户空间上与内核通信部分,其实仅做了三件事。文章地址https://www.yii666.com/article/764113.html网址:yii666.com
接收用户输入,填充vlan_ioctl_args结构体,vlan_ioctl_args结构体在linux/linux-2.6/include/linux/if_vlan.h中定义。
struct vlan_ioctl_args {
int cmd; /* Should be one of the vlan_ioctl_cmds enum above. */
char device1[]; union {
char device2[];
int VID;
unsigned int skb_priority;
unsigned int name_type;
unsigned int bind_type;
unsigned int flag; /* Matches vlan_dev_info flags */
} u; short vlan_qos;
};
创建socket描述符
/* We use sockets now, instead of the file descriptor */
if ((fd = socket(AF_INET, SOCK_STREAM, )) < ) {
fprintf(stderr, "FATAL: Couldn't open a socket..go figure!\n");
exit();
}
ioctl请求
/* add */
if (strcasecmp(cmd, "add") == ) {
if_request.cmd = ADD_VLAN_CMD;
if (ioctl(fd, SIOCSIFVLAN, &if_request) < ) {
fprintf(stderr,"ERROR: trying to add VLAN #%u to IF -:%s:- error: %s\n",
vid, if_name, strerror(errno));
}
}//if
2、内核空间— vlan驱动
vlan驱动工作在内核空间,因此需要相应的内核API去读取用户空间的数据。在/linux/linux-2.6/net/8021q/vlan.c的vlan模块初始化函数vlan_proto_init中使用vlan_ioctl_set注册vlan_ioctl_handler函数,使之用于响应用户空间的ioctl请求。文章来源地址:https://www.yii666.com/article/764113.html
vlan_ioctl_set(vlan_ioctl_handler);
在vlan_ioctl_handler函数中,首先使用copy_from_user函数从用户空间拷贝数据到内核空间。
struct vlan_ioctl_args args; if (copy_from_user(&args, arg, sizeof(struct vlan_ioctl_args)))
return -EFAULT;
在/linux/linux-2.6/net/socket.c中可以查看到vlan_ioctl_set的定义,它的参数是一个函数指针。当把vlan_ioctl_handler函数作为参数传递时,vlan_ioctl_hook指向其首地址,通过这种方式把对特定ioctl的响应处理方法注册进内核。用户可以添加不同的命令,只需在模块的vlan_ioctl_handler中对相应的命令进行解析、响应即可。
static DEFINE_MUTEX(vlan_ioctl_mutex);
static int (*vlan_ioctl_hook) (void __user *arg); void vlan_ioctl_set(int (*hook) (void __user *))
{
mutex_lock(&vlan_ioctl_mutex);
vlan_ioctl_hook = hook;
mutex_unlock(&vlan_ioctl_mutex);
} EXPORT_SYMBOL(vlan_ioctl_set);
而后的sock_ioctl函数中调用了vlan_ioctl_hook,void __user *argp指向用户空间传递的参数。
case SIOCGIFVLAN:
case SIOCSIFVLAN:
err = -ENOPKG;
if (!vlan_ioctl_hook)
request_module("8021q"); mutex_lock(&vlan_ioctl_mutex);
if (vlan_ioctl_hook)
err = vlan_ioctl_hook(argp);
mutex_unlock(&vlan_ioctl_mutex);
break;