1Linux中与内核通信的Netlink机制Netlink在2.6版本的内核中变化也是很大的,在最新的,其定义已经改成下面这种形式,传递的参数已经达到6个。其中第一个参数和mutex参数都是最新添加的。Mutex也可以为空。这里主要是关于内核空间中的netlink函数的使用。externstructsock*netlink_kernel_create(structnet*net,intunit,unsignedintgroups,void(*input)(structsk_buff*skb),structmutex*cb_mutex,structmodule*module);structnet是一个网络名字空间namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套net_device等等。默认情况下都是使用init_net这个全局变量,下面是内核中调用netlink_kernel_create()函数的一个示例。在内核中,audit_sock=netlink_kernel_create(&init_net,NETLINK_AUDIT,0,audit_receive,NULL,THIS_MODULE);模块调用函数netlink_unicast来发送单播消息:intnetlink_unicast(structsock*ssk,structsk_buff*skb,u32pid,intnonblock)参数ssk为函数netlink_kernel_create()返回的socket,参数skb存放消息,它的data字段指向要发送的netlink消息结构,而skb的控制块保存了消息的地址信息,前面的宏NETLINK_CB(skb)就用于方便设置该控制块,参数pid为接收消息进程的pid,参数nonblock表示该函数是否为非阻塞,如果为1,该函数将在没有接收缓存可利用时立即返回,而如果为0,该函数在没有接收缓存可利用定时睡眠。netlink的内核实现在.c文件net/core/af_netlink.c中,内核模块要想使用netlink,也必须包含头文件linux/netlink.h。内核使用netlink需要专门的API,这完全不同于用户态应用对netlink的使用。如果用户需要增加新的netlink协议类型,必须通过修改linux/netlink.h来实现,当然,目前的netlink实现已经包含了一个通用的协议类型NETLINK_GENERIC以方便用户使用,用户可以直接使用它而不必增加新的协议类型。前面讲到,为了增加新的netlink协议类型,用户仅需增加如下定义到linux/netlink.h就可以:只要增加这个定义之后,用户就可以在内核的任何地方引用该协议。在内核中,为了创建一个netlinksocket用户需要调用如下函数:externstructsock*netlink_kernel_create(structnet*net,intunit,unsignedintgroups,void(*input)(structsk_buff*skb),structmutex*cb_mutex,structmodule*module);structnet是一个网络名字空间namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套net_device等等。默认情况下都是使用init_net这个全局变量参数unit表示netlink协议类型,如NETLINK_MYTEST,参数input则为内核模块定义的netlink消息处理函数,当有消息到达这个netlinksocket时,该input函数指针就会被引用。函数指针input的参数skb实际上就是函数netlink_kernel_create返回的structsock指针,sock实际是socket的一个内核表示数据结构,用户态应用创建的socket在内核中也会有一个structsock结构来表示。函数input()会在发送进程执行sendmsg()时被调用,这样处理消息比较及时,但是,如2果消息特别长时,这样处理将增加系统调用sendmsg()的执行时间,也就是说当用户的程序调用sendmsg()函数时,如果input()函数处理时间过长,也就是说input()函数不执行不完,用户程序调用的sendmsg()函数就不会返回。只有当内核空间中的input()函数返回时,用户调用的sendmsg()函数才会返回。对于这种情况,可以定义一个内核线程专门负责消息接收,而函数input的工作只是唤醒该内核线程,这样sendmsg将很快返回。(这里网上的的说明)不过在查看,一般都是按下面的方法进行处理。这里注册的netlink协议为NETLINK_XFRM。nlsk=netlink_kernel_create(net,NETLINK_XFRM,XFRMNLGRP_MAX,xfrm_netlink_rcv,NULL,THIS_MODULE);staticvoidxfrm_netlink_rcv(structsk_buff*skb){mutex_lock(&xfrm_cfg_mutex);netlink_rcv_skb(skb,&xfrm_user_rcv_msg);mutex_unlock(&xfrm_cfg_mutex);}在netlink_rcv_skb()函数中进行接收处理。intnetlink_broadcast(structsock*ssk,structsk_buff*skb,u32pid,u32group,gfp_tallocation)前面的三个参数与netlink_unicast相同,参数group为接收消息的多播组,该参数的每一个位代表一个多播组,因此如果发送给多个多播组,就把该参数设置为多个多播组组ID的位或。参数allocation为内核内存分配类型,一般地为GFP_ATOMIC或GFP_KERNEL,GFP_ATOMIC用于原子的上下文(即不可以睡眠),而GFP_KERNEL用于非原子上下文。NETLINK_CB(skb).pid=0;NETLINK_CB(skb).dst_pid=0;NETLINK_CB(skb).dst_group=1;字段pid表示消息发送者进程ID,也即源地址,对于内核,它为0,dst_pid表示消息接收者进程ID,也即目标地址,如果目标为组或内核,它设置为0,否则dst_group表示目标组地址,如果它目标为某一进程或内核,dst_group应当设置为0。下面是参考网上使用netlink写的和内核通信的两个程序,一个是用户空间,一个是内核空间。内核空间:#includelinux/init.h#includelinux/module.h#includelinux/timer.h#includelinux/time.h#includelinux/types.h#includenet/sock.h#includenet/netlink.h#defineNETLINK_TEST25#defineMAX_MSGSIZE1024intstringlength(char*s);voidsendnlmsg(char*message);intpid;interr;3structsock*nl_sk=NULL;intflag=0;voidsendnlmsg(char*message){structsk_buff*skb_1;structnlmsghdr*nlh;intlen=NLMSG_SPACE(MAX_MSGSIZE);intslen=0;if(!message||!nl_sk){return;}skb_1=alloc_skb(len,GFP_KERNEL);if(!skb_1){printk(KERN_ERRmy_net_link:alloc_skb_1error\n);}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;message[slen]='\0';memcpy(NLMSG_DATA(nlh),message,slen+1);printk(my_net_link:sendmessage'%s'.\n,(char*)NLMSG_DATA(nlh));netlink_unicast(nl_sk,skb_1,pid,MSG_DONTWAIT);}intstringlength(char*s){intslen=0;for(;*s;s++){slen++;}returnslen;}voidnl_data_ready(structsk_buff*__skb){structsk_buff*skb;structnlmsghdr*nlh;charstr[100];structcompletioncmpl;inti=10;skb=skb_get(__skb);if(skb-len=NLMSG_SPACE(0)){4nlh=nlmsg_hdr(skb);memcpy(str,NLMSG_DATA(nlh),sizeof(str));printk(Messagereceived:%s\n,str);pid=nlh-nlmsg_pid;while(i--){init_completion(&cmpl);wait_for_completion_timeout(&cmpl,3*HZ);sendnlmsg(Iamfromkernel!);}flag=1;kfree_skb(skb);}}//Initializenetlinkintnetlink_init(void){nl_sk=netlink_kernel_create(&init_net,NETLINK_TEST,1,nl_data_ready,NULL,THIS_MODULE);if(!nl_sk){printk(KERN_ERRmy_net_link:createnetlinksocketerror.\n);return1;}printk(my_net_link_3:createnetlinksocketok.\n);return0;}staticvoidnetlink_exit(void){if(nl_sk!=NULL){sock_release(nl_sk-sk_socket);}printk(my_net_link:selfmoduleexited\n);}module_init(netlink_init);module_exit(netlink_exit);MODULE_AUTHOR(frankzfz);MODULE_LICENSE(GPL);下面是用户空间的程序:#includesys/stat.h#includeunistd.h#includestdio.h#includestdlib.h#includesys/socket.h#includesys/types.h5#includestring.h#includeasm/types.h#includelinux/netlink.h#includelinux/socket.h#includeerrno.h#defineNETLINK_TEST25#defineMAX_PAYLOAD1024//maximumpayloadsizeintmain(intargc,char*argv[]){intstate;structsockaddr_nlsrc_addr,dest_addr;structnlmsghdr*nlh=NULL;structioveciov;structmsghdrmsg;intsock_fd,retval;intstate_smg