linux下用户程序同内核通信详解(netlink机制)
linux下用户程序同内核通信的方式一般有ioctl,proc文件系统,剩下一个就是Netlink套接字了。这里先介绍下netlink。
Netlink是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的socketAPI就可以使用netlink提供的强大功能,内核态需要使用专门的内核API来使用netlink。
Netlink相对于系统调用,ioctl以及/proc文件系统而言具有以下优点:
1,为了使用netlink,用户仅需要在include/linux/netlink.h中增加一个新类型的netlink协议定义即可,如#defineNETLINK_MYTEST17然后,内核和用户态应用就可以立即通过socketAPI使用该netlink协议类型进行数据交换。但系统调用需要增加新的系统调用,ioctl则需要增加设备或文件,那需要不少代码,proc文件系统则需要在/proc下添加新的文件或目录,那将使本来就混乱的/proc更加混乱。
2.netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中,发送消息只是把消息保存在接收者的socket的接收队列,而不需要等待接收者收到消息,但系统调用与ioctl则是同步通信机制,如果传递的数据太长,将影响调度粒度。
3.使用netlink的内核部分可以采用模块的方式实现,使用netlink的应用部分和内核部分没有编译时依赖,但系统调用就有依赖,而且新的系统调用的实现必须静态地连接到内核中,它无法在模块中实现,使用新系统调用的应用在编译时需要依赖内核。
4.netlink支持多播,内核模块或应用可以把消息多播给一个netlink组,属于该neilink组的任何内核模块或应用都能接收到该消息,内核事件向用户态的通知机制就使用了这一特性,任何对内核事件感兴趣的应用都能收到该子系统发送的内核事件,在后面的文章中将介绍这一机制的使用。
5.内核可以使用netlink首先发起会话,但系统调用和ioctl只能由用户应用发起调用。
6.netlink使用标准的socketAPI,因此很容易使用,但系统调用和ioctl则需要专门的培训才能使用。
下面这两部分代码主要的目的是用netlink机制实现用户程序和内核的通信。具体就是用户程序执行./netlink-S[我是参数]或./netlink-G时,内核会返回"Sknowyou!"和“Iknowyou!”这两种字符串,然后输出。内核和用户程序均加有打印。
1.Makefile依赖的编译规则ruler.dir
PWD:=$(shellpwd) all:modulesromfs 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
KERNEL_MODULES:=netlink exportMODULES_ROOT_DIR:=$(shellpwd) exportMODULES_BUILD_DIR:=$(MODULES_ROOT_DIR)/build exportKDIR:=/lib/modules/$(shelluname-r)/build#这行是为了取出系统下内核的目录(ubuntu) all:initmodulesromfs init: mkdir-p$(MODULES_BUILD_DIR) modules:$(patsubst%,_dir_%,$(KERNEL_MODULES)) $(patsubst%,_dir_%,$(KERNEL_MODULES)): @echo @echoBuilding$(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 @echoCleaning$(patsubst_dir_%,%,$@) $(MAKE)-C$(patsubst_clean_%,%,$@)clean .PHONY:
3../netlink/netlink.c
/* *netlink.c * *Createdon:2014*Author:cr */ #include#include #include #include #include #include #include #include #include #include"usrlink.h" MODULE_LICENSE("DualBSD/GPL"); MODULE_AUTHOR("MDAXIA"); structsock*netlink_fd; staticvoidnetlink_to_user(intdest,void*buf,intlen) { structnlmsghdr*nl; structsk_buff*skb; intsize; size=NLMSG_SPACE(len); skb=alloc_skb(size,GFP_ATOMIC); if(!skb||!buf) { printk(KERN_ALERT"netlink_to_userskbofbufnull!\n"); return; } nl=nlmsg_put(skb,0,0,0,NLMSG_SPACE(len)-sizeof(structnlmsghdr),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"Ksendpacketsuccess\n"); } staticintprocess_hello_get(intdest,void*buf,intlen) { printk(KERN_ALERT"Inprocess_helloget!\n"); memcpy(buf,"Iknownyou!",13); netlink_to_user(dest,buf,13); returnNET_OK; } staticintprocess_hello_set(intdest,void*buf,intlen) { printk(KERN_ALERT"Inprocess_helloset!%s\n",(char*)buf); memcpy(buf,"Sknownyou!",13); netlink_to_user(dest,buf,13); returnNET_OK; } staticvoidnetlink_process_packet(structnlmsghdr*nl) { intret; switch(nl->nlmsg_type) { caseHELLO_GET: ret=process_hello_get(nl->nlmsg_pid,NLMSG_DATA(nl),nl->nlmsg_len); break; caseHELLO_SET: ret=process_hello_set(nl->nlmsg_pid,NLMSG_DATA(nl),nl->nlmsg_len); break; default:break; } } staticvoidnetlink_recv_packet(structsk_buff*__skb) { structsk_buff*skb; structnlmsghdr*nlhdr; skb=skb_get(__skb); if(skb->len>=sizeof(structnlmsghdr)) { nlhdr=(structnlmsghdr*)skb->data; if(nlhdr->nlmsg_len>=sizeof(structnlmsghdr)&& __skb->len>=nlhdr->nlmsg_len) { netlink_process_packet(nlhdr); } } else printk(KERN_ALERT"Kernelreceivemsglengtherror!\n"); } staticint__initnetlink_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"Initnetlink!\n"); return-1; } printk(KERN_ALERT"Initnetlinksuccess!\n"); return0; } staticvoid__exitnetlink_exit(void) { netlink_kernel_release(netlink_fd); printk(KERN_ALERT"Exitnetlink!\n"); } module_init(netlink_init); module_exit(netlink_exit);
4../netlink/usrlink.h
/* *usrlink.h * *Createdon:2014骞?鏈?7鏃?*Author:cr */ #ifndefUSRLINK_H_ #defineUSRLINK_H_ #defineUSER_NETLINK_CMD25 #defineMAXMSGLEN1024 typedefenumerror_e{ NET_ERROR, NET_OK, NET_PARAM, NET_MEM, NET_SOCK, }netlink_err; typedefenummodule_e{ HELLO_CMD=1, }netlink_module; typedefenumtype_e{ HELLO_SET, HELLO_GET, }netlink_type; #endif/*USRLINK_H_*/
5../netlink/Makefile
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
/* *usrlink.c * *Createdon:2014骞?鏈?7鏃?*Author:cr */ #include#include #include #include #include"usrlink.h" intnetlink_sock_init(netlink_sock*netlink_s,intmodule,intprotocol) { netlink_s->sock=socket(PF_NETLINK,SOCK_RAW,protocol); if(netlink_s->sock<0) returnNET_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,(structsockaddr*)&netlink_s->src,sizeof(netlink_s->src))<0) returnNET_SOCK; netlink_s->dest.nl_family=AF_NETLINK; netlink_s->dest.nl_pid=0; netlink_s->dest.nl_groups=0; returnNET_OK; } intnetlink_send(netlink_sock*netlink_s,inttype,char*sbuf,intslen,char*rbuf,int*rlen) { structmsghdrmsg; structnlmsghdr*nlhdr=NULL; structioveciov; intret; nlhdr=(structnlmsghdr*)malloc(NLMSG_SPACE(MAXMSGLEN)); if(NULL==nlhdr) returnNET_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("Sendfail\n"); gotoerror; } ret=recvmsg(netlink_s->sock,&msg,0); if(ret<0) { printf("Readfail\n"); gotoerror; } memcpy(rbuf,NLMSG_DATA(nlhdr),nlhdr->nlmsg_len); *rlen=nlhdr->nlmsg_len; returnNET_OK; error: free(nlhdr); returnNET_SOCK; } intnetlink_sock_deinit(netlink_sock*netlink_s) { close(netlink_s->sock); memset(netlink_s,0,sizeof(netlink_sock)); returnNET_OK; }
2.netlink.h
/* *usrlink.h * *Createdon:2014*Author:cr */ #include#include #include #include #include #ifndefUSRLINK_H_ #defineUSRLINK_H_ #defineUSER_NETLINK_CMD25 #defineMAXMSGLEN1024 typedefenumerror_e{ NET_ERROR, NET_OK, NET_PARAM, NET_MEM, NET_SOCK, }netlink_err; typedefenummodule_e{ HELLO_CMD=1, }netlink_module; typedefenumtype_e{ HELLO_SET, HELLO_GET, }netlink_type; typedefstructusr_sock_h{ intsock; structsockaddr_nldest; structsockaddr_nlsrc; }netlink_sock; intnetlink_sock_init(netlink_sock*netlink_s,intmodule,intprotocol); intnetlink_sock_deinit(netlink_sock*netlink_s); intnetlink_send(netlink_sock*netlink_s,inttype,char*sbuf,intslen,char*rbuf,int*rlen); #endif/*USRLINK_H_*/
3.main.c
/* *main.c * *Createdon:2014骞?鏈?7鏃?*Author:cr */ #include#include #include #include"usrlink.h" intparse_ret(intret) { switch(ret) { caseNET_OK: returnret; caseNET_ERROR: printf("error\n"); gotoexit_p; caseNET_MEM: printf("Memoryerror\n"); gotoexit_p; caseNET_PARAM: printf("Paramerror\n"); gotoexit_p; caseNET_SOCK: printf("Socketerror\n"); gotoexit_p; default:break; } exit_p: returnNET_ERROR; } voidusage(void) { printf("Usage:Netlink-G\n\t-S\n"); } intmain(intargc,char**argv) { netlink_sockh_sock; charrbuf[1024]; charsbuf[1024]; intret,type,slen=0,rlen=0; ret=netlink_sock_init(&h_sock,HELLO_CMD,USER_NETLINK_CMD); if(NET_OK!=parse_ret(ret)) gotoexit_p; bzero(&rbuf,sizeof(rbuf)); bzero(&sbuf,sizeof(sbuf)); if(argc<3) { usage(); gotoexit_p; } if(!strncmp("-G",argv[1],2)) type=HELLO_GET; elseif(!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)) gotoexit_p; if(rlen>0) { rbuf[rlen]='\0'; printf("Krep[len=%d]:%s\n",rlen,rbuf); } printf("K[len=%d]:%s\n",rlen,rbuf); exit_p: netlink_sock_deinit(&h_sock); return0; }
以上就是本文关于linux下用户程序同内核通信详解(netlink机制)的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!