簡介
linux下用戶程序同內核通信的方式一般有ioctl, proc文件系統,剩下一個就是Netlink套接字了。 這里先介紹下netlink。
Netlink 是一種在內核與用戶應用間進行雙向數據傳輸的非常好的方式,用戶態應用使用標準的 socket API 就可以使用 netlink 提供的強大功能,內核態需要使用專門的內核 API 來使用 netlink。
Netlink 相對于系統調用,ioctl 以及 /proc 文件系統而言具有以下優點:
1,為了使用 netlink,用戶僅需要在 include/linux/netlink.h 中增加一個新類型的 netlink 協議定義即可, 如 #define NETLINK_MYTEST 17 然后,內核和用戶態應用就可以立即通過 socket API 使用該 netlink 協議類型進行數據交換。但系統調用需要增加新的系統調用,ioctl 則需要增加設備或文件, 那需要不少代碼,proc 文件系統則需要在 /proc 下添加新的文件或目錄,那將使本來就混亂的 /proc 更加混亂。
2. netlink是一種異步通信機制,在內核與用戶態應用之間傳遞的消息保存在socket緩存隊列中,發送消息只是把消息保存在接收者的socket的接 收隊列,而不需要等待接收者收到消息,但系統調用與 ioctl 則是同步通信機制,如果傳遞的數據太長,將影響調度粒度。
3.使用 netlink 的內核部分可以采用模塊的方式實現,使用 netlink 的應用部分和內核部分沒有編譯時依賴,但系統調用就有依賴,而且新的系統調用的實現必須靜態地連接到內核中,它無法在模塊中實現,使用新系統調用的應用在編譯時需要依賴內核。
4.netlink 支持多播,內核模塊或應用可以把消息多播給一個netlink組,屬于該neilink 組的任何內核模塊或應用都能接收到該消息,內核事件向用戶態的通知機制就使用了這一特性,任何對內核事件感興趣的應用都能收到該子系統發送的內核事件,在 后面的文章中將介紹這一機制的使用。
5.內核可以使用 netlink 首先發起會話,但系統調用和 ioctl 只能由用戶應用發起調用。
6.netlink 使用標準的 socket API,因此很容易使用,但系統調用和 ioctl則需要專門的培訓才能使用。
下面這兩部分代碼主要的目的是用netlink機制實現用戶程序和內核的通信。 具體就是用戶程序執行./netlink -S [我是參數] 或./netlink -G 時,內核會返回"S know you!" 和“I know you!” 這兩種字符串, 然后輸出。 內核和用戶程序均加有打印。
內核模塊
1. Makefile依賴的編譯規則 ruler.dir
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
PWD := $(shell pwd) all: modules romfs modules: $(MAKE) -C $(KDIR) M=$(PWD) modules @echo $(OBJ) modules_install: $(MAKE) -C $(KDIR) M=$(PWD) modules_install romfs: cp -rf *.ko $(MODULES_BUILD_DIR) clean: rm *.o *.ko *.mod.* Module.* modules.* rm -rf $(MODULES_BUILD_DIR) obj-m := $(MOD_NAME).o |
2.Makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
KERNEL_MODULES := netlink export MODULES_ROOT_DIR := $(shell pwd) export MODULES_BUILD_DIR := $(MODULES_ROOT_DIR)/build export KDIR := /lib/modules/$(shell uname -r)/build #這行是為了取出系統下內核的目錄(ubuntu) all: init modules romfs init: mkdir -p $(MODULES_BUILD_DIR) modules:$(patsubst %, _dir_%, $(KERNEL_MODULES)) $(patsubst %, _dir_%, $(KERNEL_MODULES)): @echo @echo Building $(patsubst _dir_%, %, $@) $(MAKE) -C $(patsubst _dir_%, %, $@) all romfs: $(patsubst %, _romfs_%, $(KERNEL_MODULES)) $(patsubst %, _romfs_%, $(KERNEL_MODULES)): $(MAKE) -C $(patsubst _romfs_%, %, $@) romfs clean: $(patsubst %, _clean_%, $(KERNEL_MODULES)) $(RM) $(BUILD_DIR) $(patsubst %, _clean_%, $(KERNEL_MODULES)): @echo @echo Cleaning $(patsubst _dir_%, %, $@) $(MAKE) -C $(patsubst _clean_%, %, $@) clean .PHONY: |
3. ./netlink/netlink.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
/* * netlink.c * * Created on: 2014 * Author: cr */ #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/skbuff.h> #include <linux/ip.h> #include <linux/types.h> #include <linux/sched.h> #include <linux/netlink.h> #include <net/sock.h> #include "usrlink.h" MODULE_LICENSE( "Dual BSD/GPL" ); MODULE_AUTHOR( "MDAXIA" ); struct sock *netlink_fd; static void netlink_to_user( int dest, void *buf, int len) { struct nlmsghdr *nl; struct sk_buff *skb; int size; size = NLMSG_SPACE(len); skb = alloc_skb(size, GFP_ATOMIC); if (!skb || !buf) { printk(KERN_ALERT "netlink_to_user skb of buf null! " ); return ; } nl = nlmsg_put(skb, 0, 0, 0, NLMSG_SPACE(len) - sizeof ( struct nlmsghdr), 0); NETLINK_CB(skb).pid = 0; NETLINK_CB(skb).dst_group = 0; memcpy (NLMSG_DATA(nl), buf, len); nl->nlmsg_len = (len > 2) ? (len - 2):len; netlink_unicast(netlink_fd, skb, dest, MSG_DONTWAIT); printk(KERN_ALERT "K send packet success " ); } static int process_hello_get( int dest, void *buf, int len) { printk(KERN_ALERT "In process_hello get! " ); memcpy (buf, "I known you !" , 13); netlink_to_user(dest, buf, 13); return NET_OK; } static int process_hello_set( int dest, void *buf, int len) { printk(KERN_ALERT "In process_hello set! %s " , ( char *)buf); memcpy (buf, "S known you !" , 13); netlink_to_user(dest, buf, 13); return NET_OK; } static void netlink_process_packet( struct nlmsghdr *nl) { int ret; switch (nl->nlmsg_type) { case HELLO_GET: ret = process_hello_get(nl->nlmsg_pid, NLMSG_DATA(nl), nl->nlmsg_len); break ; case HELLO_SET: ret = process_hello_set(nl->nlmsg_pid, NLMSG_DATA(nl), nl->nlmsg_len); break ; default : break ; } } static void netlink_recv_packet( struct sk_buff *__skb) { struct sk_buff *skb; struct nlmsghdr *nlhdr; skb = skb_get(__skb); if (skb->len >= sizeof ( struct nlmsghdr)) { nlhdr = ( struct nlmsghdr *)skb->data; if (nlhdr->nlmsg_len >= sizeof ( struct nlmsghdr) && __skb->len >= nlhdr->nlmsg_len) { netlink_process_packet(nlhdr); } } else printk(KERN_ALERT "Kernel receive msg length error! " ); } static int __init netlink_init( void ) { netlink_fd = netlink_kernel_create(&init_net, USER_NETLINK_CMD, 0, netlink_recv_packet, NULL, THIS_MODULE); if (NULL == netlink_fd) { printk(KERN_ALERT "Init netlink! " ); return -1; } printk(KERN_ALERT "Init netlink success! " ); return 0; } static void __exit netlink_exit( void ) { netlink_kernel_release(netlink_fd); printk(KERN_ALERT "Exit netlink! " ); } module_init(netlink_init); module_exit(netlink_exit); |
4. ./netlink/usrlink.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
/* * usrlink.h * * Created on: 2014騫?鏈?7鏃? * Author: cr */ #ifndef USRLINK_H_ #define USRLINK_H_ #define USER_NETLINK_CMD 25 #define MAXMSGLEN 1024 typedef enum error_e { NET_ERROR, NET_OK, NET_PARAM, NET_MEM, NET_SOCK, } netlink_err; typedef enum module_e { HELLO_CMD = 1, } netlink_module; typedef enum type_e { HELLO_SET, HELLO_GET, } netlink_type; #endif /* USRLINK_H_ */ |
5. ./netlink/Makefile
1
2
3
4
5
6
7
|
MOD_NAME := netlink $(MOD_NAME)-objs : netlink.o -include $(MODULES_ROOT_DIR)/rules.dir .PHONY: |
6. 編譯方式。
其中Makefile、rulers.dir 在Knetlink/下, netlink.c 、netlink.h 、Makefile在Knetlink/netlink/目錄下。 編譯時在Knetlink目錄下執行Make即可
用戶程序
用戶程序的Makefile 這里就不放出了。 我是直接在eclipse下建的工程 自動編譯的、...
1. netlink.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
/* * usrlink.c * * Created on: 2014騫?鏈?7鏃? * Author: cr */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "usrlink.h" int netlink_sock_init(netlink_sock *netlink_s, int module, int protocol) { netlink_s->sock = socket(PF_NETLINK, SOCK_RAW, protocol); if (netlink_s->sock < 0) return NET_SOCK; memset (&netlink_s->src, 0 , sizeof (netlink_s->src)); netlink_s->src.nl_family = AF_NETLINK; netlink_s->src.nl_pid = module; netlink_s->src.nl_groups = 0; if (bind(netlink_s->sock, ( struct sockaddr *)&netlink_s->src, sizeof (netlink_s->src)) < 0) return NET_SOCK; netlink_s->dest.nl_family = AF_NETLINK; netlink_s->dest.nl_pid = 0; netlink_s->dest.nl_groups = 0; return NET_OK; } int netlink_send(netlink_sock *netlink_s, int type, char *sbuf, int slen, char *rbuf, int *rlen) { struct msghdr msg; struct nlmsghdr *nlhdr = NULL; struct iovec iov; int ret; nlhdr = ( struct nlmsghdr *) malloc (NLMSG_SPACE(MAXMSGLEN)); if (NULL == nlhdr) return NET_MEM; memcpy (NLMSG_DATA(nlhdr), sbuf, slen); nlhdr->nlmsg_len = NLMSG_SPACE(slen); nlhdr->nlmsg_pid = netlink_s->src.nl_pid; nlhdr->nlmsg_type = type; nlhdr->nlmsg_flags = 0; iov.iov_base = ( void *)nlhdr; iov.iov_len = nlhdr->nlmsg_len; msg.msg_name = ( void *)&(netlink_s->dest); msg.msg_namelen = sizeof (netlink_s->dest); msg.msg_iov = &iov; msg.msg_iovlen = 1; ret = sendmsg(netlink_s->sock, &msg, 0); if (ret < 0) { printf ( "Send fail " ); goto error; } ret = recvmsg(netlink_s->sock, &msg, 0); if (ret < 0) { printf ( "Read fail " ); goto error; } memcpy (rbuf, NLMSG_DATA(nlhdr), nlhdr->nlmsg_len); *rlen = nlhdr->nlmsg_len; return NET_OK; error: free (nlhdr); return NET_SOCK; } int netlink_sock_deinit(netlink_sock *netlink_s) { close(netlink_s->sock); memset (netlink_s, 0, sizeof (netlink_sock)); return NET_OK; } |
2. netlink.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
/* * usrlink.h * * Created on: 2014 * Author: cr */ #include <sys/types.h> #include <sys/socket.h> #include <asm/types.h> #include <linux/socket.h> #include <linux/netlink.h> #ifndef USRLINK_H_ #define USRLINK_H_ #define USER_NETLINK_CMD 25 #define MAXMSGLEN 1024 typedef enum error_e { NET_ERROR, NET_OK, NET_PARAM, NET_MEM, NET_SOCK, } netlink_err; typedef enum module_e { HELLO_CMD = 1, } netlink_module; typedef enum type_e { HELLO_SET, HELLO_GET, } netlink_type; typedef struct usr_sock_h { int sock; struct sockaddr_nl dest; struct sockaddr_nl src; } netlink_sock; int netlink_sock_init(netlink_sock *netlink_s, int module, int protocol); int netlink_sock_deinit(netlink_sock *netlink_s); int netlink_send(netlink_sock *netlink_s, int type, char *sbuf, int slen, char *rbuf, int *rlen); #endif /* USRLINK_H_ */ |
3. main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
/* * main.c * * Created on: 2014騫?鏈?7鏃? * Author: cr */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "usrlink.h" int parse_ret( int ret) { switch (ret) { case NET_OK: return ret; case NET_ERROR: printf ( "error " ); goto exit_p; case NET_MEM: printf ( "Memory error " ); goto exit_p; case NET_PARAM: printf ( "Param error " ); goto exit_p; case NET_SOCK: printf ( "Socket error " ); goto exit_p; default : break ; } exit_p: return NET_ERROR; } void usage( void ) { printf ( "Usage: Netlink -G <param> -S <param> " ); } int main( int argc, char **argv) { netlink_sock h_sock; char rbuf[1024]; char sbuf[1024]; int ret, type, slen = 0, rlen = 0; ret = netlink_sock_init(&h_sock, HELLO_CMD, USER_NETLINK_CMD); if (NET_OK != parse_ret(ret)) goto exit_p; bzero(&rbuf, sizeof (rbuf)); bzero(&sbuf, sizeof (sbuf)); if (argc < 3) { usage(); goto exit_p; } if (! strncmp ( "-G" , argv[1], 2)) type = HELLO_GET; else if (! strncmp ( "-S" , argv[1], 2)) type = HELLO_SET; strcpy (sbuf, argv[2]); slen = strlen (sbuf); ret = netlink_send(&h_sock, type, sbuf, slen, rbuf, &rlen); if (NET_OK != parse_ret(ret)) goto exit_p; if (rlen > 0) { rbuf[rlen] = '' ; printf ( "K rep [len = %d]:%s " , rlen, rbuf); } printf ( "K[len = %d]: %s " , rlen, rbuf); exit_p: netlink_sock_deinit(&h_sock); return 0; } |
總結
以上就是本文關于linux下用戶程序同內核通信詳解(netlink機制)的全部內容,希望對大家有所幫助。感興趣的朋友可以繼續參閱本站其他相關專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
原文鏈接:http://blog.csdn.net/daydring/article/details/24000081