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

 

posted @ 2013-10-31 22:30  chenshuyi  阅读(6004)  评论(0编辑  收藏  举报