欢迎来到三一办公! | 帮助中心 三一办公31ppt.com(应用文档模板下载平台)
三一办公
全部分类
  • 办公文档>
  • PPT模板>
  • 建筑/施工/环境>
  • 毕业设计>
  • 工程图纸>
  • 教育教学>
  • 素材源码>
  • 生活休闲>
  • 临时分类>
  • ImageVerifierCode 换一换
    首页 三一办公 > 资源分类 > DOC文档下载  

    Linux Netlink 基本使用方法.doc

    • 资源ID:4842631       资源大小:76KB        全文页数:21页
    • 资源格式: DOC        下载积分:10金币
    快捷下载 游客一键下载
    会员登录下载
    三方登录下载: 微信开放平台登录 QQ登录  
    下载资源需要10金币
    邮箱/手机:
    温馨提示:
    用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)
    支付方式: 支付宝    微信支付   
    验证码:   换一换

    加入VIP免费专享
     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    Linux Netlink 基本使用方法.doc

    Linux Netlink 基本使用方法1.什么是 Netlink什么是Netlink?Netlink是linux提供的用于内核和用户态进程之间的通信方式。但是注意虽然Netlink主要用于用户空间和内核空间的通信,但是也能用于用户空间的两个进程通信。只是进程间通信有其他很多方式,一般不用Netlink。除非需要用到Netlink的广播特性时。那么Netlink有什么优势呢?一般来说用户空间和内核空间的通信方式有三种:/proc、ioctl、Netlink。而前两种都是单向的,但是Netlink可以实现双工通信。Netlink协议基于BSDsocket和AF_NETLINK地址簇(addressfamily),使用32位的端口号寻址(以前称作PID),每个Netlink协议(或称作总线,man手册中则称之为netlinkfamily),通常与一个或一组内核服务/组件相关联,如NETLINK_ROUTE用于获取和设置路由与链路信息、NETLINK_KOBJECT_UEVENT用于内核向用户空间的udev进程发送通知等。netlink具有以下特点:支持全双工、异步通信(当然同步也支持)用户空间可使用标准的BSDsocket接口(但netlink并没有屏蔽掉协议包的构造与解析过程,推荐使用libnl等第三方库)在内核空间使用专用的内核API接口支持多播(因此支持“总线”式通信,可实现消息订阅)在内核端可用于进程上下文与中断上下文如何学习Netlink?我觉得最好的方式就是将Netlink和UDPsocket对比学习。因为他们真的很对地方相似。AF_NETLINK和AF_INET对应,是一个协议族,而NETLINK_ROUTE、NETLINK_GENERIC这些是协议,对应于UDP。那么我们主要关注Netlink和UDPsocket之间的不同点,其中最重要的一点就是:使用UDPsocket发送数据包时,用户无需构造UDP数据包的包头,内核协议栈会根据原、目的地址(sockaddr_in)填充头部信息。但是Netlink需要我们自己构造一个包头(这个包头有什么用,我们后面再说)。一般我们使用Netlink都要指定一个协议,我们可以使用内核为我们预留的NETLINK_GENERIC(定义在linux/netlink.h中),也可以使用我们自定义的协议,其实就是定义一个内核还没有占用的数字。下面我们用NETLINK_TEST做为我们定义的协议写一个例子(注意:自定义协议不一定非要添加到linux/netlink.h中,只要用户态和内核态代码都能找到该定义就行)。我们知道使用UDP发送报文有两种方式:sendto和sendmsg,同样Netlink也支持这两种方式。下面先看使用sendmsg的方式。2.用户态数据结构首先看一下几个重要的数据结构的关系:2.1structmsghdrmsghdr这个结构在socket变成中就会用到,并不算Netlink专有的,这里不在过多说明。只说明一下如何更好理解这个结构的功能。我们知道socket消息的发送和接收函数一般有这几对:recvsend、readvwritev、recvfromsendto。当然还有recvmsgsendmsg,前面三对函数各有各的特点功能,而recvmsgsendmsg就是要囊括前面三对的所有功能,当然还有自己特殊的用途。msghdr的前两个成员就是为了满足recvfromsendto的功能,中间两个成员msg_iov和msg_iovlen则是为了满足readvwritev的功能,而最后的msg_flags则是为了满足recvsend中flag的功能,剩下的msg_control和msg_controllen则是满足recvmsgsendmsg特有的功能。2.2Structsockaddr_lnStructsockaddr_ln为Netlink的地址,和我们通常socket编程中的sockaddr_in作用一样,他们的结构对比如下。structsockaddr_nl的详细定义和描述如下:1234567struct sockaddr_nlsa_family_t nl_family; /*该字段总是为AF_NETLINK */unsigned short nl_pad; /* 目前未用到,填充为0*/_u32 nl_pid; /* process pid */_u32 nl_groups; /* multicast groups mask */;(1)nl_pid:在Netlink规范里,PID全称是Port-ID(32bits),其主要作用是用于唯一的标识一个基于netlink的socket通道。通常情况下nl_pid都设置为当前进程的进程号。前面我们也说过,Netlink不仅可以实现用户-内核空间的通信还可使现实用户空间两个进程之间,或内核空间两个进程之间的通信。该属性为0时一般指内核。(2)nl_groups:如果用户空间的进程希望加入某个多播组,则必须执行bind()系统调用。该字段指明了调用者希望加入的多播组号的掩码(注意不是组号,后面我们会详细讲解这个字段)。如果该字段为0则表示调用者不希望加入任何多播组。对于每个隶属于Netlink协议域的协议,最多可支持32个多播组(因为nl_groups的长度为32比特),每个多播组用一个比特来表示。2.3structnlmsghdrNetlink的报文由消息头和消息体构成,structnlmsghdr即为消息头。消息头定义在文件里,由结构体nlmsghdr表示:12345678struct nlmsghdr_u32 nlmsg_len; /* Length of message including header */_u16 nlmsg_type; /* Message content */_u16 nlmsg_flags; /* AddiTIonal flags */_u32 nlmsg_seq; /* Sequence number */_u32 nlmsg_pid; /* Sending process PID */;消息头中各成员属性的解释及说明:(1)nlmsg_len:整个消息的长度,按字节计算。包括了Netlink消息头本身。(2)nlmsg_type:消息的类型,即是数据还是控制消息。目前(内核版本2.6.21)Netlink仅支持四种类型的控制消息,如下:a)NLMSG_NOOP-空消息,什么也不做;b)NLMSG_ERROR-指明该消息中包含一个错误;c)NLMSG_DONE-如果内核通过Netlink队列返回了多个消息,那么队列的最后一条消息的类型为NLMSG_DONE,其余所有消息的nlmsg_flags属性都被设置NLM_F_MULTI位有效。d)NLMSG_OVERRUN-暂时没用到。(3)nlmsg_flags:附加在消息上的额外说明信息,如上面提到的NLM_F_MULTI。那消息体怎么设置呢?可以使用NLMSG_DATA,具体见后面例子。3.用户态范例一客户端112345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485#include#include#include#include#include#include#include#include#include#include#include#define MAX_PAYLOAD 1024 / maximum payload size#define NETLINK_TEST 25 /自定义的协议int main(int argc, char* argv)int state;struct sockaddr_nl src_addr, dest_addr;struct nlmsghdr *nlh = NULL; /Netlink数据包头struct iovec iov;struct msghdr msg;int sock_fd, retval;int state_smg = 0;/ Create a socketsock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);if(sock_fd = -1)printf("error getTIng socket: %s", strerror(errno);return -1;/ To prepare bindingmemset(src_addr.nl_family = AF_NETLINK;src_addr.nl_pid = 100; /A:设置源端端口号src_addr.nl_groups = 0;/Bindretval = bind(sock_fd, (struct sockaddr*)if(retval nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);nlh->nlmsg_pid = 100; /C:设置源端口nlh->nlmsg_flags = 0;strcpy(NLMSG_DATA(nlh),"Hello you!"); /设置消息体iov.iov_base = (void *)nlh;iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);/Create mssagememset(msg.msg_name = (void *)msg.msg_namelen = sizeof(dest_addr);msg.msg_iov = msg.msg_iovlen = 1;/send messageprintf("state_smgn");state_smg = sendmsg(sock_fd,if(state_smg = -1)printf("get error sendmsg = %sn",strerror(errno);memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD);/receive messageprintf("waiting received!n");while(1)printf("In while recvmsgn");state = recvmsg(sock_fd, if(stateadcastint netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, u32 group, gfp_t allocation)前面的三个参数与netlink_unicast相同,参数group为接收消息的多播组,该参数的每一个位代表一个多播组,因此如果发送给多个多播组,就把该参数设置为多个多播组组ID的位或。参数allocation为内核内存分配类型,一般地为GFP_ATOMIC或GFP_KERNEL,GFP_ATOMIC用于原子的上下文(即不可以睡眠),而GFP_KERNEL用于非原子上下文。4.4释放 netlinksocket1int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, u32 pid, u32 group, gfp_t allocation)5.内核态程序范例一1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798#include#include#include#include#include#include#include#define NETLINK_TEST 25#define MAX_MSGSIZE 1024int stringlength(char *s);int err;struct sock *nl_sk = NULL;int flag = 0;/向用户态进程回发消息void sendnlmsg(char *message, int pid)struct sk_buff *skb_1;struct nlmsghdr *nlh;int len = NLMSG_SPACE(MAX_MSGSIZE);int slen = 0;if(!message | !nl_sk)return ;printk(KERN_ERR "pid:%dn",pid);skb_1 = alloc_skb(len,GFP_KERNEL);if(!skb_1)printk(KERN_ERR "my_net_link:alloc_skb errorn");slen = stringlength(message);nlh = nlmsg_put(skb_1,0,0,0,MAX_MSGSIZE,0);NETLINK_CB(skb_1).pid = 0;NETLINK_CB(skb_1).dst_group = 0;messageslen= 0;memcpy(NLMSG_DATA(nlh),message,slen+1);printk("my_net_link:send message %s.n",(char *)NLMSG_DATA(nlh);netlink_unicast(nl_sk,skb_1,pid,MSG_DONTWAIT);int stringlength(char *s)int slen = 0;for(; *s; s+)slen+;return slen;/接收用户态发来的消息void nl_data_ready(struct sk_buff *_skb)struct sk_buff *skb;struct nlmsghdr *nlh;char str100;struct completion cmpl;printk("begin data_readyn");int i=10;int pid;skb = skb_get (_skb);if(skb->len >= NLMSG_SPACE(0)nlh = nlmsg_hdr(skb);memcpy(str, NLMSG_DATA(nlh), sizeof(str);printk("Message received:%sn",str) ;pid = nlh->nlmsg_pid;while(i-)/我们使用completion做延时,每3秒钟向用户态回发一个消息init_completion(wait_for_completion_timeout(sendnlmsg("I am from kernel!",pid);flag = 1;kfree_skb(skb);/ Initialize netlinkint netlink_init(void)nl_sk = netlink_kernel_create(">nl_data_ready, NULL, THIS_MODULE);if(!nl_sk)printk(KERN_ERR "my_net_link: create netlink socket error.n");return 1;printk("my_net_link_4: create netlink socket ok.n");return 0;static void netlink_exit(void)if(nl_sk != NULL)sock_release(nl_sk->sk_socket);printk("my_net_link: self module exitedn");module_init(netlink_init);module_exit(netlink_exit);MODULE_AUTHOR("yilong");MODULE_LICENSE("GPL");附上内核代码的Makefile文件:12345678ifneq ($(KERNELRELEASE),)obj-m :=netl.oelseKERNELDIR ?=/lib/modules/$(shell uname -r)/buildPWD :=$(shell pwd)default:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesendif6.程序结构分析我们将内核模块insmod后,运行用户态程序,结果如下:这个结果复合我们的预期,但是运行过程中打印出“state_smg”卡了好久才输出了后面的结果。这时候查看客户进程是处于D状态的(不了解D状态的同学可以google一下)。这是为什么呢?因为进程使用Netlink向内核发数据是同步,内核向进程发数据是异步。什么意思呢?也就是用户进程调用sendmsg发送消息后,内核会调用相应的接收函数,但是一定到这个接收函数执行完用户态的sendmsg才能够返回。我们在内核态的接收函数中调用了10次回发函数,每次都等待3秒钟,所以内核接收函数30秒后才返回,所以我们用户态程序的sendmsg也要等30秒后才返回。相反,内核回发的数据不用等待用户程序接收,这是因为内核所发的数据会暂时存放在一个队列中。再来回到之前的一个问题,用户态程序的源地址(pid)可以用0吗?我把上面的用户程序的A和C处pid都改为了0,结果一运行就死机了。为什么呢?我们看一下内核代码的逻辑,收到用户消息后,根据消息中的pid发送回去,而pid为0,内核并不认为这是用户程序,认为是自身,所有又将回发的10个消息发给了自己(内核),这样就陷入了一个死循环,而用户态这时候进程一直处于D。另外一个问题,如果同时启动两个用户进程会是什么情况?答案是再调用bind时出错:“Addressalreadyinuse”,这个同UDP一样,同一个地址同一个port如果没有设置SO_REUSEADDR两次bind就会出错,之后我用同样的方式再Netlink的socket上设置了SO_REUSEADDR,但是并没有什么效果。7.用户态范例二之前我们说过UDP可以使用sendmsgrecvmsg也可以使用sendtorecvfrom,那么Netlink同样也可以使用sendtorecvfrom。具体实现如下:123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475#include#include#include#include#include#include#include#include#include#include#include#define MAX_PAYLOAD 1024 / maximum payload size#define NETLINK_TEST 25int main(int argc, char* argv)struct sockaddr_nl src_addr, dest_addr;struct nlmsghdr *nlh = NULL;int sock_fd, retval;int state,state_smg = 0;/ Create a socketsock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST);if(sock_fd = -1)printf("error getting socket: %s", strerror(errno);return -1;/ To prepare bindingmemset(src_addr.nl_family = AF_NETLINK;src_addr.nl_pid = 100;src_addr.nl_groups = 0;/Bindretval = bind(sock_fd, (struct sockaddr*)if(retval nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);nlh->nlmsg_pid = 100;nlh->nlmsg_flags = 0;strcpy(NLMSG_DATA(nlh),"Hello you!");/send messageprintf("state_smgn");sendto(sock_fd,nlh,NLMSG_LENGTH(MAX_PAYLOAD),0,(struct sockaddr*)(if(state_smg = -1)printf("get error sendmsg = %sn",strerror(errno);memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD);/receive messageprintf("waiting received!n");while(1)printf("In while recvmsgn");state=recvfrom(sock_fd,nlh,NLMSG_LENGTH(MAX_PAYLOAD),0,NULL,NULL);if(state<0)printf("state<1");printf("Received message: %sn",(char *) NLMSG_DATA(nlh);memset(nlh,0,NLMSG_SPACE(MAX_PAYLOAD);close(sock_fd);return 0;熟悉UDP编程的同学看到这个程序一定很熟悉,除了多了一个Netlink消息头的设置。但是我们发现程序中调用了bind函数,这个函数再UDP编程中的客户端不是必须的,因为我们不需要把UDPsocket与某个地址关联,同时再发送UDP数据包时内核会为我们分配一个随即的端口。但是对于Netlink必须要有这一步bind,因为Netlink内核可不会为我们分配一个pid。再强调一遍消息头(nlmsghdr)中的pid是告诉内核接收端要回复的地址,但是这个地址存不存在内核并不关心,这个地址只有用户端调用了bind后才存在。再说一个体外话,我们看到这两个例子都是用户态首先发起的,那Netlink是否支持内核态主动发起的情况呢?当然是可以的,只是内核一般需要事件触发,这里,只要和用户态约定号一个地址(pid),内核直接调用netlink_unicast就可以了。 

    注意事项

    本文(Linux Netlink 基本使用方法.doc)为本站会员(sccc)主动上传,三一办公仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知三一办公(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    备案号:宁ICP备20000045号-2

    经营许可证:宁B2-20210002

    宁公网安备 64010402000987号

    三一办公
    收起
    展开