運行在用戶上下文環境中的代碼是可以阻塞的,這樣,便可以使用消息隊列和 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 }
三頭文件:
1 /*imp1.h*/
2 #ifndef __IMP1_H__
3 #define __IMP1_H__
4
5 #define IMP1_OPS_BASIC 128
6 #define IMP1_SET IMP1_OPS_BASIC
7 #define IMP1_GET IMP1_OPS_BASIC
8 #define IMP1_MAX IMP1_OPS_BASIC+1
9
10 #endif
~
四 編譯後運行結果:
內核打印:[541380.295993] umsg: a message from userspace
[541390.819256] umsg: a message from userspace
[541392.515414] umsg: a message from userspace
[541393.374753] umsg: a message from userspace
[541393.967123] umsg: a message from userspace
應用程序打印:
/imp1$ sudo ./imp1_u
kmsg: a message from kernel