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.pswp.cn/news/257988.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/257988.shtml
英文地址,請注明出處:http://en.pswp.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;一個應用&…