运行在用户上下文环境中的代码是可以阻塞的,这样,便可以使用消息队列和 UNIX 域套接字来实现内核态与用户态的通信。但这些方法的数据传输效率较低,Linux 内核提供 copy_from_user()/copy_to_user() 函数来实现内核态与用户态数据的拷贝,但这两个函数会引发阻塞,所以不能用在硬、软中断中。一般将这两个特殊拷贝函数用在类似于系统调用一类的函数中,此类函数在使用中往往"穿梭"于内核态与用户态。此类方法的工作原理路如图【1】。
其中相关的系统调用是需要用户自行编写并载入内核。
内核模块注册了一组设置套接字选项的函数使得用户空间进程可以调用此组函数对内核态数据进行读写。源码包含三个文件,imp1.h 是通用头文件,定义了用户态和内核态都要用到的宏。imp1_k.c 是内核模块的源代码。imp1_u.c 是用户态进程的源代码。整个示例演示了由一个用户态进程向用户上下文环境发送一个字符串,内容为"a message from userspace\n"。然后再由用户上下文环境向用户态进程发送一个字符串,内容为"a message from kernel\n"。
1 内核代码:
/*imp1_k.c*/
2 #ifndef __KERNEL__
3 #define __KERNEL__
4 #endif
5
6 #ifndef MODULE
7 #define MODULE
8 #endif
9
10 #include <linux/module.h>
11 #include <linux/kernel.h>
12 #include <linux/types.h>
13 #include <linux/string.h>
14 #include <linux/netfilter_ipv4.h>
15 #include <linux/init.h>
16 #include <asm/uaccess.h>
17 #include "imp1.h"
18
19 #define KMSG "a message from kernel\n"
20 #define KMSG_LEN sizeof("a message from kernel\n")
21 MODULE_LICENSE("Dual BSD/GPL");
22 static int data_to_kernel(struct sock *sk, int cmd, void *user,
23 unsigned int len)
24 {
25 switch(cmd)
26 {
27 case IMP1_SET:
28 {
29 char umsg[64];
30 memset(umsg, 0, sizeof(char)*64);
31 copy_from_user(umsg, user, sizeof(char)*64);
32 printk("umsg: %s", umsg);
33 }
34 break;
35 }
36 return 0;
37 }
38
39 static int data_from_kernel(struct sock *sk, int cmd, void *user, int *len)
40 {
41 switch(cmd)
42 {
43 case IMP1_GET:
44 {
45 copy_to_user(user, KMSG, KMSG_LEN);
46 }
47 break;
48 }
49 return 0;
50 }
51
52 static struct nf_sockopt_ops imp1_sockops =
53 {
54 .pf = PF_INET,
55 .set_optmin = IMP1_SET,
56 .set_optmax = IMP1_MAX,
57 .set = data_to_kernel,
58 .get_optmin = IMP1_GET,
59 .get_optmax = IMP1_MAX,
60 .get = data_from_kernel,
61 };
62
63 static int __init init(void)
64 {
65 return nf_register_sockopt(&imp1_sockops);
66 }
67
68 static void __exit fini(void)
69 {
70 nf_unregister_sockopt(&imp1_sockops);
71 }
72
73 module_init(init);
74 module_exit(fini);
75
二 应用层代码:
/*imp1_u.c*/
2 #include <unistd.h>
3 #include <stdio.h>
4 #include <sys/socket.h>
5 #include <linux/in.h>
6 #include "imp1.h"
7
8 #define UMSG "a message from userspace\n"
9 #define UMSG_LEN sizeof("a message from userspace\n")
10
11 char kmsg[64];
12
13 int main(void)
14 {
15 int sockfd;
16 int len;
17
18 sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
19 if(sockfd < 0)
20 {
21 printf("can not create a socket\n");
22 return -1;
23 }
24
25 /*call function data_to_kernel()*/
26 setsockopt(sockfd, IPPROTO_IP, IMP1_SET, UMSG, UMSG_LEN);
27
28 len = sizeof(char)*64;
29
30 /*call function data_from_kernel()*/
31 getsockopt(sockfd, IPPROTO_IP, IMP1_GET, kmsg, &len);
32 printf("kmsg: %s", kmsg);
33
34 close(sockfd);
35 return 0;
36 }
三头文件: