Linux内核态之间进程通信,内核态和用户态通信(二)--实现

本文主要使用netlink套接字实现中断环境与用户态进程通信。

系统环境:基于linux 2.6.32.27 和 linux 3.16.36

Linux内核态和用户态进程通信方法的提出和实现

用户上下文环境

运行在用户上下文环境中的代码是可以阻塞的,这样,便可以使用消息队列和Unix域套接字来实现内核态和用户态的通信。但这些的数据传输效率较低,linux内核提供copy_from_user() 和 copy_to_user() 函数来实现内核态与用户态数据的拷贝,但这两个函数会引发阻塞,所以不能用在硬、软中断中。一般将这两个特殊拷贝函数用在类似系统调用一类的函数中,如图,

4654d6df5bcd0fa2fff9ca3fdcc81278.png

其中相关的系统调用是需要用户自行编写并载入内核。

硬、软中断环境

硬中断和软中断环境与用户态进程无丝毫关系,而且运行过程不能阻塞。

软中断、硬中断有一套同步机制 — 自旋锁(spinlock),可以通过自旋锁来实现中断环境和中断环境,中断环境与内核线程的同步,而内核线程是运行在有进程上下文环境中的,这样便可以在内核线程中使用套接字或消息队列来取得用户空间的数据,然后再将数据通过临界区传递给中断过程,如图

6c2b9a197043aa782650138d019368e3.png

因为中断过程不可能无休止地等待用户态进程发送数据,所以要通过一个内核线程来接收用户空间的数据,再通过临界区传给中断过程。中断过程向用户空间的数据发送必须是无阻塞的。这样的通信模型并不令人满意,因为内核线程是和其他用户态进程竞争cpu接收数据的,效率很低,这样中断过程便不能实时地接收来自用户空间的数据。

netlink套接字的通信依据是一个对应于进程的标识,一般定义为该进程的ID。当通信的一端处于中断过程时,该标识为0。当使用 netlink 套接字进行通信,通信的双方都是用户态进程,则使用方法类似于消息队列。但通信双方有一端是中断过程,使用方法则不同。netlink 套接字的最大特点是对中断过程的支持,它在内核空间接收用户空间数据时不再需要用户自行启动一个内核线程,而是通过另一个软中断调用用户事先指定的接收函数。工作原理如图

9b32e2c71bb7369226557d1496860ccb.png

很明显,这里使用了软中断而不是内核线程来接收数据,这样就可以保证数据接收的实时性。

当 netlink 套接字用于内核空间与用户空间的通信时,在用户空间的创建方法和一般套接字使用类似,但内核空间的创建方法则不同。如图

cf3c9f3c9e1f9de6fe02605b7b496aaf.png

/**

*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 31

struct packet_info

{

__u32 src;

__u32 dest;

};

#endif

/**

*imp2_u.c

*/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include "imp2.h"

struct 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_log

static 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_buff

struct 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,

};

#endif

static 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);

#else

nlfd = 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");

#Makefile

MODULE_NAME := imp2_k

obj-m := $(MODULE_NAME).o

KERNELDIR ?= /lib/modules/$(shell uname -r)/build

PWD := $(shell pwd)

all:

$(MAKE) -C $(KERNELDIR) M=$(PWD)

clean:

rm -fr *.ko

rm -fr *.o

rm -fr *.cmd sender $(MODULE_NAME).mod.c

.PHONY:clean aLL

netlink具体结构分析,可参考其他博文

http://blog.csdn.net/luckyapple1028/article/details/50839395

http://blog.csdn.net/luckyapple1028/article/details/50936563

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/257988.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

上下文无关文法

在计算机科学中&#xff0c;若一个形式文法 G (N, Σ, P, S) 的产生式规则都取如下的形式&#xff1a;V -> w&#xff0c;则称之为上下文无关文法&#xff08;英语&#xff1a;context-free grammar&#xff0c;缩写为CFG&#xff09;&#xff0c;其中 V∈N &#xff0c;w∈…

centos 安装mysql时错误unknown variable #39;defaults-file=/opt/redmine-2.6.0-2/mysql/my.cnf#39;...

找到my.cnf所在目录。运行 chmod 664 my.cnf&#xff0c;再启动mysql成功

p5js可以在linux上运行吗,在linux上使用python运行phantomjs

我跟随this link&#xff0c;现在当我输入phan然后输入tab(\t)时&#xff0c;它会自动完成幻影JS。在但是&#xff0c;如果我运行phantomJS -v或phantomJS --version&#xff0c;我得到&#xff1a;bash: /usr/local/bin/phantomjs: /lib/ld-linux.so.2: bad ELF interpreter: …

使用Instant Client配置PL/SQL Developer

之前使用PL/SQL Developer都是直接在本机安装完整版的Oracle Database&#xff0c;一是省事&#xff0c;二是可以在本机做一些demo测试&#xff1b;最近换了台电脑&#xff0c;感觉Instant Client更简单一些&#xff0c;分分钟配好。 先下载Instant Client&#xff0c;注意&…

linux脚本转换exe,Ps1 To Exe(powershell脚本转换EXE工具) V3.0.6 官方版

Ps1 To Exe是款将PowerShell脚本转换为EXE可执行文件的软件。同时软件非常小巧&#xff0c;功能实用&#xff0c;软件还支持各国的语言&#xff0c;有需要的小伙伴们不要错过了。(点击图片查看高清大图)【软件特色】1、Ps1 To Exe 支持多种语言2、Ps1 To Exe使用简单&#xff0…

标C编程笔记day04 预处理、宏定义、条件编译、makefile、结构体使用

预处理&#xff1a;也就是包括须要的头文件&#xff0c;用#include<标准头文件>或#include "自己定义的头文件"宏定义&#xff0c;如&#xff1a;#define PI 3.1415926查看用宏定义的值替换宏名称,如&#xff1a;gcc -E test.c带參数的宏&#xff1a;MAX(x,y) …

java数据结构系列——排列(2):有序阵列

package Array;/*** 对数组排序。当添加到阵列保持有序数组元素&#xff1b;* author wl**/ public class MyOrderArray {private long array[];private int elements;//用于记录数组中实际数据的个数public MyOrderArray(){arraynew long[50];//数组默认长度为50&#xff1b;}…

NSString 练习

//将“⽂文艺⻘青年”改成“213⻘青年”。 NSString *str "文艺青年"; NSString *str1 [str stringByReplacingOccurrencesOfString:"文艺" withString:"213"]; NSLog("%",str1); //将 整数123 转换为字符串“123”。 NSString *s …

安全市场五巨头将面临新兴厂商的挑战

赛门铁克、思科、IBM、Check Point、英特尔&#xff0c;警钟已敲响~ 2016年同比增长率11.5%的数据出台之后&#xff0c;市场研究公司科技商业研究(TBR)为来年的安全行业绘制了一幅崭新的蓝图——安全市场上现有的企业将受到新兴厂商的挑战。 展望未来&#xff0c;现有安全市场五…

linux编译运行build.sh,linux下libwebsockets编译及实例

最近想自己搭建一个webscoket协议的服务器&#xff0c;打算用libwebsockts这个库。下载代码编译。编写一个shell脚本#!/bin/sh# wget http://git.warmcat.com/cgi-bin/cgit/libwebsockets/snapshot/libwebsockets-1.4-chrome43-firefox-36.tar.gz# tar xvzf libwebsockets-1.4-…

Tomcat如何配置环境变量

1&#xff0c; JDK&#xff1a;版本为jdk-7-windows-i586.exe 下载地址: http://www.oracle.com/technetwork/java/javase/downloads/index.html 2&#xff0c;tomcat&#xff1a;版本为apache-tomcat-7.0.33-windows-x86.zip 下载地址&#xff1a;http://tomcat.apache.org/ 2…

eclipse常用快捷键——非常实用

1、eclipse 查看变量或方法被调用的快捷键如下&#xff1a; &#xff08;1&#xff09;双击选中变量或者方法&#xff08;2&#xff09;键盘上CtrlshiftG组合键 2、eclipse中查看接口实现类快捷键 先找到接口类打开,然后双击接口名选中,再按住ctrlT就可以了。 3、eclipse中全局…

反编译查看源码dex2jar

为什么80%的码农都做不了架构师&#xff1f;>>> 上次说到了用apktool反编译&#xff0c;这次我们来用dex2jar 把apk解压得到文件夹 文件夹打开看到这些文件 其中这个classes.dex就是这次需要用到的字节码文件 把这个字节码文件托到dex2jar目录里 命令行编辑 得到下…

linux命令验证sqlldr,Linux:sqlldr命令

第一步&#xff1a;写一个 ctl格式的控制文件CTL 控制文件的内容 &#xff1a;load data --1. 控制文件标识infilexxx.txt --2. 要导入的数据文件名insert into table test--3. 将文件插入到数据库的 test 表中fields terminated by X09 --4. 用于分割一行中各个属性值的符号(例…

STL 中的链表排序

一直以来学习排序算法&#xff0c; 都没有在链表排序上下太多功夫&#xff0c;因为用得不多。最近看STL源码&#xff0c;才发现&#xff0c;原来即使是链表&#xff0c;也能有时间复杂度为O(nlogn)的算法&#xff0c; 大大出乎我的意料之外&#xff0c;一般就能想到个插入排序。…

cmd更换编码类型

chcp 65001 UTF-8 65001 GBK 936 本文出自 “曾颐楠的播客” 博客&#xff0c;请务必保留此出处http://zengyinan.blog.51cto.com/9524976/1721475 转载于:https://www.cnblogs.com/zengyinanos/p/5042732.html

代码混淆之后定位线上bug

代码混淆的目的 代码混淆的目的是防止竞争对手通过反编译来阅读项目代码。 Android中通过ProGuard来做代码混淆&#xff08;当然也还有其他的产品可以做代码混淆&#xff09;。 bug日志反混淆 资料&#xff1a;错误log、mapping.txt 异常log&#xff1a; mapping.txt&#xff…

linux怎么切换不同版本的r,在linux中用同一个版本的R 同时安装 Seurat2 和 Seurat3

在linux中用同一个版本的R 同时安装 Seurat 2 和 Seurat 3Seurat 作为单细胞分析中的重量级R包&#xff0c;有多好用用&#xff0c;用过的人都知道。Seurat 分析流程基本涵盖了单细胞分析中的所有常见分析方法&#xff0c;包括filtering&#xff0c;tSNE&#xff0c;UMAP降维及…

Unity手游之路四3d旋转-四元数,欧拉角和变幻矩阵

http://blog.csdn.net/janeky/article/details/17272625 今天我们来谈谈关于Unity中的旋转。主要有三种方式。变换矩阵&#xff0c;四元数和欧拉角。 定义 变换矩阵可以执行任意的3d变换&#xff08;平移&#xff0c;旋转&#xff0c;缩放&#xff0c;切边&#xff09;并且透视…

本地通知

本地通知&#xff0c;local notification&#xff0c;用于基于时间行为的通知&#xff0c;比如有关日历或者todo列表的小应用。另外&#xff0c;应用如果在后台执行&#xff0c;iOS允许它在受限的时间内运行&#xff0c;它也会发现本地通知有用。比如&#xff0c;一个应用&…