作者:天勤-科技 | 来源:互联网 | 2023-07-12 10:27
在Linux内核模块的开发过程中,经常涉及到运行在用户空间上的应用程序与内核模块进行交互,ioctl系统调用是常用的一种方式。本文并不涉及vlan的具体原理,仅通过vconfig与vlan内核模块进行
在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;