Linux 内核态与用户态通信 netlink
參考資料:
https://blog.csdn.net/zqixiao_09/article/details/77131283
https://www.cnblogs.com/lopnor/p/6158800.html
?
?
?
?
?
Netlink 是一種特殊的 socket,它是 Linux 所特有的,類(lèi)似于 BSD 中的AF_ROUTE 但又遠(yuǎn)比它的功能強(qiáng)大,目前在最新的 Linux 內(nèi)核(2.6.14)中使用netlink 進(jìn)行應(yīng)用與內(nèi)核通信的應(yīng)用很多,包括:路由 daemon(NETLINK_ROUTE),1-wire 子系統(tǒng)(NETLINK_W1),用戶(hù)態(tài) socket 協(xié)議(NETLINK_USERSOCK),防火墻(NETLINK_FIREWALL),socket 監(jiān)視(NETLINK_INET_DIAG),netfilter 日志(NETLINK_NFLOG),ipsec 安全策略(NETLINK_XFRM),SELinux 事件通知(NETLINK_SELINUX),iSCSI 子系統(tǒng)(NETLINK_ISCSI),進(jìn)程審計(jì)(NETLINK_AUDIT),轉(zhuǎn)發(fā)信息表查詢(xún)(NETLINK_FIB_LOOKUP),netlink connector(NETLINK_CONNECTOR),netfilter 子系統(tǒng)(NETLINK_NETFILTER),IPv6 防火墻(NETLINK_IP6_FW),DECnet 路由信息(NETLINK_DNRTMSG),內(nèi)核事件向用戶(hù)態(tài)通知(NETLINK_KOBJECT_UEVENT),通用 netlink(NETLINK_GENERIC)。
Netlink 是一種在內(nèi)核與用戶(hù)應(yīng)用間進(jìn)行雙向數(shù)據(jù)傳輸?shù)姆浅:玫姆绞?#xff0c;用戶(hù)態(tài)應(yīng)用使用標(biāo)準(zhǔn)的 socket API 就可以使用 netlink 提供的強(qiáng)大功能,內(nèi)核態(tài)需要使用專(zhuān)門(mén)的內(nèi)核 API 來(lái)使用 netlink。
Netlink 相對(duì)于系統(tǒng)調(diào)用,ioctl 以及 /proc 文件系統(tǒng)而言具有以下優(yōu)點(diǎn):
1,為了使用 netlink,用戶(hù)僅需要在 include/linux/netlink.h 中增加一個(gè)新類(lèi)型的 netlink 協(xié)議定義即可, 如 #define NETLINK_MYTEST 17 然后,內(nèi)核和用戶(hù)態(tài)應(yīng)用就可以立即通過(guò) socket API 使用該 netlink 協(xié)議類(lèi)型進(jìn)行數(shù)據(jù)交換。但系統(tǒng)調(diào)用需要增加新的系統(tǒng)調(diào)用,ioctl 則需要增加設(shè)備或文件, 那需要不少代碼,proc 文件系統(tǒng)則需要在 /proc 下添加新的文件或目錄,那將使本來(lái)就混亂的 /proc 更加混亂。
問(wèn)題:
增加了netlink協(xié)議后,需要重新編譯內(nèi)核嗎?還是僅僅修改了頭文件netlink.h就可以了呢?在后期的實(shí)驗(yàn)中,需要對(duì)這一問(wèn)題進(jìn)行驗(yàn)證。
2. netlink是一種異步通信機(jī)制,在內(nèi)核與用戶(hù)態(tài)應(yīng)用之間傳遞的消息保存在socket緩存隊(duì)列中,發(fā)送消息只是把消息保存在接收者的socket的接收隊(duì)列,而不需要等待接收者收到消息,但系統(tǒng)調(diào)用與 ioctl 則是同步通信機(jī)制,如果傳遞的數(shù)據(jù)太長(zhǎng),將影響調(diào)度粒度。
異步通信與同步通信的區(qū)別:看接受者的響應(yīng)方式。異步通訊是不需要接受者立即響應(yīng)的
3.使用 netlink 的內(nèi)核部分可以采用模塊的方式實(shí)現(xiàn),使用 netlink 的應(yīng)用部分和內(nèi)核部分沒(méi)有編譯時(shí)依賴(lài),但系統(tǒng)調(diào)用就有依賴(lài),而且新的系統(tǒng)調(diào)用的實(shí)現(xiàn)必須靜態(tài)地連接到內(nèi)核中,它無(wú)法在模塊中實(shí)現(xiàn),使用新系統(tǒng)調(diào)用的應(yīng)用在編譯時(shí)需要依賴(lài)內(nèi)核。
可以像編寫(xiě)驅(qū)動(dòng)模塊一樣的實(shí)現(xiàn)方式來(lái)實(shí)現(xiàn)netlink部分。
4.netlink 支持多播,內(nèi)核模塊或應(yīng)用可以把消息多播給一個(gè)netlink組,屬于該neilink 組的任何內(nèi)核模塊或應(yīng)用都能接收到該消息,內(nèi)核事件向用戶(hù)態(tài)的通知機(jī)制就使用了這一特性,任何對(duì)內(nèi)核事件感興趣的應(yīng)用都能收到該子系統(tǒng)發(fā)送的內(nèi)核事件,在后面的文章中將介紹這一機(jī)制的使用。
5.內(nèi)核可以使用 netlink 首先發(fā)起會(huì)話,但系統(tǒng)調(diào)用和 ioctl 只能由用戶(hù)應(yīng)用發(fā)起調(diào)用。
內(nèi)核主動(dòng)向用戶(hù)應(yīng)用發(fā)起數(shù)據(jù)
6.netlink 使用標(biāo)準(zhǔn)的 socket API,因此很容易使用,但系統(tǒng)調(diào)用和 ioctl則需要專(zhuān)門(mén)的培訓(xùn)才能使用。
?
NETLINK_GENERIC是一個(gè)通用的協(xié)議類(lèi)型,它是專(zhuān)門(mén)為用戶(hù)使用的,因此,用戶(hù)可以直接使用它,而不必再添加新的協(xié)議類(lèi)型。
?
#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/types.h> #include <linux/sched.h> #include <net/sock.h> #include <net/netlink.h> #include <linux/netlink.h>#define NETLINK_USER 22 #define USER_MSG (NETLINK_USER + 1) #define USER_PORT 50 /*此處是寫(xiě)死了,可以用進(jìn)程ID,表示來(lái)自用戶(hù)態(tài)的唯一進(jìn)程*/MODULE_LICENSE("GPL"); MODULE_AUTHOR("arvik"); MODULE_DESCRIPTION("netlink_demo");static void test_netlink_rcv(struct sk_buff *skb);struct netlink_kernel_cfg cfg = {.input = test_netlink_rcv,/*...*/ }; static struct sock *test_netlink_sock = NULL;int send_msg(int8_t *pbuf, uint16_t len) {struct sk_buff *nl_skb;struct nlmsghdr *nlh;int ret;nl_skb = nlmsg_new(len, GFP_ATOMIC);if (!nl_skb) {printk("netlink_alloc_skb error\n");return -1;}/*將header填充到skb中*/nlh = nlmsg_put(nl_skb, 0, 0, USER_MSG, len, 0);if (nlh == NULL) {printk("put error\n");nlmsg_free(nl_skb);return -1;}/*拷貝data*/memcpy(nlmsg_data(nlh), pbuf, len);/*發(fā)送*/ret = netlink_unicast(test_netlink_sock, nl_skb, USER_PORT, MSG_DONTWAIT);return ret; }static void test_netlink_rcv(struct sk_buff *skb) {struct nlmsghdr *nlh = NULL;void *data = NULL;printk("skb->len %u\n", skb->len);if (skb->len >= nlmsg_total_size(0)){nlh = nlmsg_hdr(skb);data = NLMSG_DATA(nlh);if (data) {printk("kernel receive date : %s\n", (int8_t *)data);send_msg(data, nlmsg_len(nlh));}} }static int __init test_netlink_init(void) {printk("test netlink init\n");test_netlink_sock = netlink_kernel_create(&init_net, USER_MSG, &cfg); if (test_netlink_sock == NULL) {printk("test netlink init error\n");return -1;}printk("test netlink init ok\n");return 0; } static void __exit test_netlink_exit(void) {netlink_kernel_release(test_netlink_sock);test_netlink_sock = NULL;printk("test netlink exit\n"); }module_init(test_netlink_init); module_exit(test_netlink_exit);?
?
#include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <string.h> #include <linux/netlink.h> #include <stdint.h> #include <unistd.h> #include <errno.h>#define NETLINK_USER 22 #define USER_MSG (NETLINK_USER + 1)#define MSG_LEN 100#define MAX_PLOAD 100struct _my_msg {struct nlmsghdr hdr;int8_t data[MSG_LEN]; };int main() {char *data = "hello kernel";socklen_t addr_len;struct sockaddr_nl local, dest_addr;int skfd;struct nlmsghdr *nlh = NULL;struct _my_msg info;int ret;//創(chuàng)建socketskfd = socket(AF_NETLINK, SOCK_RAW, USER_MSG);if (skfd == (-1)) {fprintf(stderr, "create socket error...%s\n", strerror(errno));return (-1);}/*綁定*/memset(&local, 0, sizeof(local));local.nl_family = AF_NETLINK;local.nl_pid = 50; /*此處的pid通常用進(jìn)程ID,表示來(lái)自用戶(hù)態(tài)的唯一進(jìn)程*/local.nl_groups = 0;if (bind(skfd, (struct sockaddr *)&local, sizeof(local)) != 0) {fprintf(stderr, "bind error\n");close(skfd);return (-1);}//初始化目的地址memset(&dest_addr, 0, sizeof(dest_addr));dest_addr.nl_family = AF_NETLINK; dest_addr.nl_pid = 0;dest_addr.nl_groups = 0;/*填寫(xiě)data*/nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PLOAD));memset(nlh, 0, sizeof(struct nlmsghdr));nlh->nlmsg_len = NLMSG_SPACE(MAX_PLOAD);nlh->nlmsg_flags = 0;nlh->nlmsg_type = 0;nlh->nlmsg_seq = 0;nlh->nlmsg_pid = local.nl_pid;memcpy(NLMSG_DATA(nlh), data, strlen(data));ret = sendto(skfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_nl));if (ret < 0) { // fprintf(stderr, "sendto error\n");perror("sendto error: ");close(skfd);return (-1);}printf("wait kernel msg!\n");memset(&info, 0, sizeof(info));/*接受信息*/ret = recvfrom(skfd, &info, sizeof(struct _my_msg), 0, (struct sockaddr *)&dest_addr, &addr_len);if (ret <= 0) { // fprintf(stderr, "recv from kernel error\n");perror("recv from kernel error: ");close(skfd);return (-1);}printf("msg receive from kernel : %s\n", info.data);close(skfd);free((void *)nlh);close(skfd);return 0; }?
轉(zhuǎn)載于:https://www.cnblogs.com/rivsidn/p/10493954.html
總結(jié)
以上是生活随笔為你收集整理的Linux 内核态与用户态通信 netlink的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
                            
                        - 上一篇: 和菜鸟一起学linux之bluez学习记
 - 下一篇: 字符串匹配的KMP算法(转)