网站添加文章,南京金九建设集团网站,wordpress游戏评测站睡觉,建设通官网首页本文主要使用netlink套接字实现中断环境与用户态进程通信。系统环境#xff1a;基于linux 2.6.32.27 和 linux 3.16.36Linux内核态和用户态进程通信方法的提出和实现用户上下文环境运行在用户上下文环境中的代码是可以阻塞的#xff0c;这样#xff0c;便可以使用消息队列和…本文主要使用netlink套接字实现中断环境与用户态进程通信。系统环境基于linux 2.6.32.27 和 linux 3.16.36Linux内核态和用户态进程通信方法的提出和实现用户上下文环境运行在用户上下文环境中的代码是可以阻塞的这样便可以使用消息队列和Unix域套接字来实现内核态和用户态的通信。但这些的数据传输效率较低linux内核提供copy_from_user() 和 copy_to_user() 函数来实现内核态与用户态数据的拷贝但这两个函数会引发阻塞所以不能用在硬、软中断中。一般将这两个特殊拷贝函数用在类似系统调用一类的函数中如图 其中相关的系统调用是需要用户自行编写并载入内核。硬、软中断环境硬中断和软中断环境与用户态进程无丝毫关系而且运行过程不能阻塞。软中断、硬中断有一套同步机制 — 自旋锁(spinlock)可以通过自旋锁来实现中断环境和中断环境中断环境与内核线程的同步而内核线程是运行在有进程上下文环境中的这样便可以在内核线程中使用套接字或消息队列来取得用户空间的数据然后再将数据通过临界区传递给中断过程如图 因为中断过程不可能无休止地等待用户态进程发送数据所以要通过一个内核线程来接收用户空间的数据再通过临界区传给中断过程。中断过程向用户空间的数据发送必须是无阻塞的。这样的通信模型并不令人满意因为内核线程是和其他用户态进程竞争cpu接收数据的效率很低这样中断过程便不能实时地接收来自用户空间的数据。netlink套接字的通信依据是一个对应于进程的标识一般定义为该进程的ID。当通信的一端处于中断过程时该标识为0。当使用 netlink 套接字进行通信通信的双方都是用户态进程则使用方法类似于消息队列。但通信双方有一端是中断过程使用方法则不同。netlink 套接字的最大特点是对中断过程的支持它在内核空间接收用户空间数据时不再需要用户自行启动一个内核线程而是通过另一个软中断调用用户事先指定的接收函数。工作原理如图 很明显这里使用了软中断而不是内核线程来接收数据这样就可以保证数据接收的实时性。当 netlink 套接字用于内核空间与用户空间的通信时在用户空间的创建方法和一般套接字使用类似但内核空间的创建方法则不同。如图 /***imp2.h*/#ifndef __IMP2_H__#define __IMP2_H__#define IMP2_OPS_BASIC 128#define IMP2_SET IMP2_OPS_BASIC#define IMP2_GET IMP2_OPS_BASIC#define IMP2_MAX (IMP2_OPS_BASIC 1)#define IMP2_U_PID 0#define IMP2_K_MSG 1#define IMP2_CLOSE 2#define NL_IMP2 31struct packet_info{__u32 src;__u32 dest;};#endif/***imp2_u.c*/#include#include#include#include#include#include#include#include#include#include#include#include imp2.hstruct msg_to_kernel{struct nlmsghdr hdr;};struct u_packet_info{struct nlmsghdr hdr;struct packet_info icmp_info;};static int skfd;static void sig_int(int signo){struct sockaddr_nl kpeer;struct msg_to_kernel message;memset(kpeer,0,sizeof(kpeer));kpeer.nl_family AF_NETLINK;kpeer.nl_pid 0;kpeer.nl_groups 0;memset(message,0,sizeof(message));message.hdr.nlmsg_len NLMSG_LENGTH(0);message.hdr.nlmsg_flags 0;message.hdr.nlmsg_type IMP2_CLOSE;message.hdr.nlmsg_pid getpid();sendto(skfd,message,message.hdr.nlmsg_len,0,(struct sockaddr *)(kpeer),sizeof(kpeer));close(skfd);exit(0);}int main(void){/* 本地的 */struct sockaddr_nl local;/* 连线kernel的 */struct sockaddr_nl kpeer;int kpeerlen;struct msg_to_kernel message;struct u_packet_info info;int sendlen 0;int rcvlen 0;struct in_addr addr;skfd socket(AF_NETLINK,SOCK_RAW,NL_IMP2);if(skfd 0) {printf(cannot create a netlink socket\n);exit(0);}memset(local,0,sizeof(local));local.nl_family AF_NETLINK;local.nl_pid getpid();local.nl_groups 0;if(bind(skfd,(struct sockaddr *)local,sizeof(local)) ! 0) {printf(bind() error\n);return -1;}signal(SIGINT,sig_int);memset(kpeer,0,sizeof(kpeer));kpeer.nl_family AF_NETLINK;kpeer.nl_pid 0;kpeer.nl_groups 0;memset(message,0,sizeof(message));message.hdr.nlmsg_len NLMSG_LENGTH(0);message.hdr.nlmsg_flags 0;message.hdr.nlmsg_type IMP2_U_PID;message.hdr.nlmsg_pid local.nl_pid;sendto(skfd,message,message.hdr.nlmsg_len,0,(struct sockaddr *)kpeer,sizeof(kpeer));while(1) {kpeerlen sizeof(struct sockaddr_nl);rcvlen recvfrom(skfd,info,sizeof(struct u_packet_info),0,(struct sockaddr *)kpeer,kpeerlen);addr.s_addr info.icmp_info.src;printf(src:%s,,inet_ntoa(addr));addr.s_addr info.icmp_info.dest;printf(dest:%s\n,inet_ntoa(addr));}return 0;}/*** imp2_k.c //兼容linux 2.6.32 和 linux 3.16.36*/#ifndef __KERNEL__#define __KERNEL__#endif#ifndef MODULE#define MODULE#endif#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include#include imp2.h#define NF_IP_PRE_ROUTING 0/* 实现从netfilter的NF_IP_PRE_ROUTING点截获的ICMP数据包再将数据包的相关信息传递到一个用户态进程 */static struct sock *nlfd;//是竞争的资源struct {__u32 pid;rwlock_t lock;}user_proc;//相当于np_logstatic int send_to_user(struct packet_info *info){int ret 0;int size 0;unsigned char *old_tail NULL;struct sk_buff *skb NULL;struct nlmsghdr *nlh NULL;struct packet_info *packet NULL;printk(%s,begin ....\n,__FUNCTION__);size NLMSG_SPACE(sizeof(*info));//分配一块skb(数据缓存区和skb描述符),大小,GFP自动分配skb alloc_skb(size,GFP_ATOMIC);old_tail skb-tail;nlh nlmsg_put(skb,0,0,IMP2_K_MSG,(size-sizeof(*nlh)),0);packet NLMSG_DATA(nlh);memset(packet , 0 ,sizeof(struct packet_info));packet-src info-src;packet-dest info-dest;nlh-nlmsg_len skb-tail - old_tail;NETLINK_CB(skb).dst_group 0;read_lock_bh(user_proc.lock);ret netlink_unicast(nlfd,skb,user_proc.pid,MSG_DONTWAIT);read_unlock_bh(user_proc.lock);printk(%s,end....\n,__FUNCTION__);return ret;nlmsg_failure:if(skb) {kfree_skb(skb);}return -1;}static unsigned int get_icmp(unsigned int hooknum,struct sk_buff *skb,const struct net_device *in,const struct net_device *out,int(*okfn)(struct sk_buff *)){//struct iphdr *iph (*pskb)-nh.iph;struct iphdr *iph ip_hdr(skb); //2.6.24开始使用因为struct sk_buffstruct packet_info info;if(iph-protocol IPPROTO_ICMP) {read_lock_bh(user_proc.lock);if(user_proc.pid ! 0) {info.src iph-saddr;info.dest iph-daddr;//printk(%s,src %u.%u,%u.%u,dst %u,%u,%u,%u\n,__FUNCTION__,NIPQUAD(info.src), NIPQUAD(info.dest));read_unlock_bh(user_proc.lock);send_to_user(info);} else {//printk(%s, no user process runing....\n,__FUNCTION__);read_unlock_bh(user_proc.lock);}}return NF_ACCEPT;}static struct nf_hook_ops imp2_ops {.hook get_icmp,.pf PF_INET,.hooknum NF_IP_PRE_ROUTING,.priority NF_IP_PRI_FILTER - 1,.owner THIS_MODULE,};static void kernel_receive(struct sk_buff *skb){struct nlmsghdr *nlh NULL;int len 0;nlh nlmsg_hdr(skb);len skb-len;while(NLMSG_OK(nlh,len)) {write_lock_bh(user_proc.lock);if(nlh-nlmsg_type IMP2_U_PID) {user_proc.pid nlh-nlmsg_pid;}else if(nlh-nlmsg_type IMP2_CLOSE nlh-nlmsg_pid user_proc.pid) {user_proc.pid 0;}write_unlock_bh(user_proc.lock);netlink_ack(skb,nlh,0);nlh NLMSG_NEXT(nlh,len);}}#if LINUX_VERSION_CODE KERNEL_VERSION(3,10,0)struct netlink_kernel_cfg cfg {.input kernel_receive,};#endifstatic int __init init(void){rwlock_init(user_proc.lock);//这里的版本问题需要解决/*在内核创建一个netlink socket ,协议 NL_IMP2是自定义的,并指示由 kernel_receive接收数据*/{#if LINUX_VERSION_CODE KERNEL_VERSION(3,10,0)nlfd netlink_kernel_create(init_net,NL_IMP2,cfg);#elif LINUX_VERSION_CODE KERNEL_VERSION(2,6,26)nlfd netlink_kernel_create(init_net ,NL_IMP2 , 0, kernel_receive, NULL, THIS_MODULE);#elsenlfd NULL;#endif}if(!nlfd) {printk(cannot create a netlink socket\n);return -1;}return nf_register_hook(imp2_ops);}static void __exit fini(void){if(nlfd) {netlink_kernel_release(nlfd);}nf_unregister_hook(imp2_ops);}module_init(init);module_exit(fini);MODULE_LICENSE(GPL);#MakefileMODULE_NAME : imp2_kobj-m : $(MODULE_NAME).oKERNELDIR ? /lib/modules/$(shell uname -r)/buildPWD : $(shell pwd)all:$(MAKE) -C $(KERNELDIR) M$(PWD)clean:rm -fr *.korm -fr *.orm -fr *.cmd sender $(MODULE_NAME).mod.c.PHONY:clean aLLnetlink具体结构分析可参考其他博文http://blog.csdn.net/luckyapple1028/article/details/50839395http://blog.csdn.net/luckyapple1028/article/details/50936563