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∈…

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…

NSString 练习

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

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…

反编译查看源码dex2jar

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

代码混淆之后定位线上bug

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

本地通知

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

把windows装到linux下,如何将WSL(Windows Subsystem for Linux 2)安装到Windows 10?

原标题&#xff1a;如何将WSL(Windows Subsystem for Linux 2)安装到Windows 10&#xff1f;Windows 10凭借大受欢迎的WSL(Windows Subsystem for Linux)进入Linux领域。由于最近推出了WSL的最新版WSL2&#xff0c;用户现在可以利用实际的Linux内核从Windows执行Linux任务。现在…

vba执行linux命令,从VBA中的shell命令捕获输出值?

慕盖茨4494581根据Andrew Lessard的回答&#xff0c;这是一个运行命令并将输出作为字符串返回的函数 -Public Function ShellRun(sCmd As String) As StringRun a shell command, returning the output as a stringDim oShell As ObjectSet oShell CreateObject("WScript…

C#= 栈模仿堆的操作

//原理&#xff0c;利用两个栈&#xff0c;互相作用&#xff0c;来模仿堆的效果&#xff0c;先进先出。。 1 using System;2 using System.Collections.Generic;3 using System.Linq;4 using System.Threading.Tasks;5 6 namespace TwoStacksQueue7 {8 public class Progra…

linux中内部命令有哪些,linux内部命令有哪些

linux中常见的内部命令有&#xff1a;1.exit命令&#xff0c;退出当前的shell&#xff1b;2.history命令&#xff0c;显示历史执行过的命令&#xff1b;3.cd命令&#xff0c;切换当前工作目录&#xff1b;4.source命令&#xff0c;重新执行刚修改的初始化文件&#xff1b;5.ech…

POJ 2778

题意&#xff1a;很Uva项链题目类似。 区别&#xff1a; 1、字符串很多&#xff0c;用map hash超时&#xff0c;用Trie查找。 2、DFS判断连通&#xff0c;和并查集判连通&#xff0c;被我写错的地方时&#xff0c;查森林的时候&#xff0c;还是要Find_Set。 1 #include <ios…

linux挂载VMFS硬盘,ESX4.1挂载NFS共享存储(VMkernel)

要使用vmotion,iscsi,nfs功能&#xff0c;必须启用VMkernel端口&#xff0c;ESX 4.1默认不启用&#xff0c;ESXi 5.x默认启用。在 vCenter Server“SZVCENTER01”上调用对象“datastoreSystem-44”的“HostDatastoreSystem.CreateNasDatastore” 失败。挂载NFS存储的ESX控制台命…

2017年8个最流行的Web编程趋势

互联网一直在不断的发展&#xff0c;这意味着开发人员必须及时了解当前的所有变化。人们在新闻、社交、购物到银行等各大方面都与互联网有着千丝万缕的联系。因此&#xff0c;为了满足全球数百万网络用户的需求&#xff0c;Web开发需求正在上升。Web编程趋势是在W开发的过程中不…

gRPC-rs:从 C 到 Rust

介绍 在上篇文章中&#xff0c;我们讲到 TiKV 为了支持 [gRPC]&#xff0c;我们造了个轮子 [gRPC-rs]&#xff0c;这篇文章简要地介绍一下这个库。首先我们来聊聊什么是 gRPC。gRPC 是 Google 推出的基于 [HTTP2] 的开源 RPC 框架&#xff0c;希望通过它使得各种微服务之间拥有…

linux系统编程练手项目,精选 22 个 C++ 项目,编程小白练手首选!

C/C 做为元老级的编程语言&#xff0c;任时光更迭依旧屹立不倒&#xff0c;哪怕现在煊赫一时的AI&#xff0c;其底层也是用其编写。linux那么做为新手该如何快速上手 C 呢&#xff1f;固然是敲代码啊&#xff01;一切不写代码的学编程都是瞎搞。下面为你们精选了 22 个 C 项目&…

linux怎么同时查看两个文件,MultiTail - 在单个Linux终端中同时监视多个文件

无论是服务器管理员还是程序员&#xff0c;我们需要参考多个日志文件来有效地排除故障任务。 为了实现这一点&#xff0c;我们必须打开&#xff0c;拖尾或更少的不同shell中的每个日志文件。 但是&#xff0c;我们可以使用传统的tail命令状尾-f在/ var / log / messages文件或尾…

今日BBC

1、随身英语 Dry January 新年戒酒一个月 link 2、地道英语 Hot potato 棘手的问题“烫手山芋” link 3、今日新闻 Brussels attacks: Belgian police arrest six suspects link The arrests were made in the Schaerbeek district. There is no word yet on the identitie…

实验吧 貌似有点难 伪造ip

解题链接&#xff1a; http://ctf5.shiyanbar.com/phpaudit/ 解答&#xff1a; 点击View the source code —>代码显示IP为1.1.1.1即可得到KEY—>使用modify header伪造IP—>拿到flag 相关&#xff1a; modify header我也是第一次用&#xff0c;下面附上相关说明&…