织梦技术个人网站模板下载,asp网站如何搭建,百度网页版入口网址,音乐网站建设价格6linux用户态与内核态通过字符设备交互
简述
Linux设备分为三类#xff0c;字符设备、块设备、网络接口设备。字符设备只能一个字节一个字节读取#xff0c;常见外设基本都是字符设备。块设备一般用于存储设备#xff0c;一块一块的读取。网络设备#xff0c;Linux将对网络…linux用户态与内核态通过字符设备交互
简述
Linux设备分为三类字符设备、块设备、网络接口设备。字符设备只能一个字节一个字节读取常见外设基本都是字符设备。块设备一般用于存储设备一块一块的读取。网络设备Linux将对网络通信抽象成一个设备通过套接字对其进行操作。 对于字符设备的用户态与内核态交互主要涉及到打开、读取、写入、关闭等操作。通过字符设备实现内核与用户程序的交互设计实现一个内核态监控文件目录及文件复制拷贝的内核模块程序其中字符设备交互时序图如下 #mermaid-svg-PVFQMi5OcXErZgMD {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-PVFQMi5OcXErZgMD .error-icon{fill:#552222;}#mermaid-svg-PVFQMi5OcXErZgMD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-PVFQMi5OcXErZgMD .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-PVFQMi5OcXErZgMD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-PVFQMi5OcXErZgMD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-PVFQMi5OcXErZgMD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-PVFQMi5OcXErZgMD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-PVFQMi5OcXErZgMD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-PVFQMi5OcXErZgMD .marker.cross{stroke:#333333;}#mermaid-svg-PVFQMi5OcXErZgMD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-PVFQMi5OcXErZgMD .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PVFQMi5OcXErZgMD text.actortspan{fill:black;stroke:none;}#mermaid-svg-PVFQMi5OcXErZgMD .actor-line{stroke:grey;}#mermaid-svg-PVFQMi5OcXErZgMD .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-PVFQMi5OcXErZgMD .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-PVFQMi5OcXErZgMD #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-PVFQMi5OcXErZgMD .sequenceNumber{fill:white;}#mermaid-svg-PVFQMi5OcXErZgMD #sequencenumber{fill:#333;}#mermaid-svg-PVFQMi5OcXErZgMD #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-PVFQMi5OcXErZgMD .messageText{fill:#333;stroke:#333;}#mermaid-svg-PVFQMi5OcXErZgMD .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PVFQMi5OcXErZgMD .labelText,#mermaid-svg-PVFQMi5OcXErZgMD .labelTexttspan{fill:black;stroke:none;}#mermaid-svg-PVFQMi5OcXErZgMD .loopText,#mermaid-svg-PVFQMi5OcXErZgMD .loopTexttspan{fill:black;stroke:none;}#mermaid-svg-PVFQMi5OcXErZgMD .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-PVFQMi5OcXErZgMD .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-PVFQMi5OcXErZgMD .noteText,#mermaid-svg-PVFQMi5OcXErZgMD .noteTexttspan{fill:black;stroke:none;}#mermaid-svg-PVFQMi5OcXErZgMD .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PVFQMi5OcXErZgMD .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PVFQMi5OcXErZgMD .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-PVFQMi5OcXErZgMD .actorPopupMenu{position:absolute;}#mermaid-svg-PVFQMi5OcXErZgMD .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-PVFQMi5OcXErZgMD .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-PVFQMi5OcXErZgMD .actor-man circle,#mermaid-svg-PVFQMi5OcXErZgMD line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-PVFQMi5OcXErZgMD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} user_space kernel_space 发送监控目录信息(list) 回复监控目录信息已设置 user_space kernel_space 通信协议格式
[2bytes数据长度] |2bytes目录路径数量| |2bytes 长度| |目录数据| ... |2bytes 长度| |目录数据|控制命令定义
#include linux/ioctl.h#define BASEMINOR 0
#define COUNT 5
#define NAME ioctl_test#define IOCTL_TYPE k//定义无参的命令
#define IOCTL_NO_ARG _IO(IOCTL_TYPE, 1)//用户空间向内核空间写
#define IOCTL_WRITE_INT _IOW(IOCTL_TYPE, 2,int)//用户空间从内核空间读
#define IOCTL_READ_INT _IOR(IOCTL_TYPE, 3, int)//用户空间向内核空间写
#define IOCTL_WRITE_STRING _IOW(IOCTL_TYPE, 4,char*)//用户空间从内核空间读
#define IOCTL_READ_STRING _IOR(IOCTL_TYPE, 5, char*)#define IOCTL_MAXNR 5上述命令实现了用户态向内核态写入、读取int型或string类型的数据定义控制命令个数为5
用户态程序
#include stdio.h
#include string.h
#include linux/ioctl.h
#include unistd.h
#include fcntl.h
#include sys/ioctl.h
#include stdlib.h
#include cmd.h
enum arg_type{ARG_INT,ARG_STRING
};
union data{int integer;char string[255];};struct arg_node{int type; //字符串类型union data arg_data;struct arg_node*next;
};
void insert_node(struct arg_node**head, struct arg_node * item ){if(itemNULL){printf(待插入节点指针为空\n);return ;}if(*head NULL){*head item;printf(节点指针赋值%p\n,*head);}else{struct arg_node *current *head;while(current-next ! NULL){current current-next;}current-next item;}}//参数格式user_ipc -int 200 -string 12324154
int main(int argc, char *argv[])
{if(argc2 || argc%20){printf(参数个数不匹配\n);return -1;}int fd 0;int arg 0;fd open(/dev/ioctl_test, O_RDWR);if(fd 0){printf(open memdev0 failed!\n);return -1;}if(ioctl(fd, IOCTL_NO_ARG, arg) 0){printf(----打印命令传输失败----\n);return -1;}unsigned char *protocol_body NULL;int init_length 5;int realloc_length 10;int len_tag_bytes 2;protocol_body calloc(init_length, sizeof(unsigned char )*init_length);int index 4;int num_of_dirs 0;struct arg_node *p_head NULL;int i0;for(i1; iargc; ii2){if(strcmp(argv[i],-int) 0){struct arg_node*p_item malloc(sizeof(struct arg_node));p_item-next NULL;p_item-type ARG_INT;p_item-arg_data.integer atoi(argv[i1]);insert_node(p_head, p_item);printf(插入int类型值: %d \n,p_item-arg_data.integer);if(p_headNULL)printf(链表头指针为空\n);}else if(strcmp(argv[i], -string) 0){struct arg_node *p_item malloc(sizeof(struct arg_node));p_item-next NULL; p_item-type ARG_STRING;memcpy(p_item-arg_data.string, argv[i1],strlen(argv[i1]));insert_node(p_head, p_item);printf(插入string类型值: %s \n,p_item-arg_data.string);//插入值组装协议数据包[2bytes数据长度] [2bytes 字符串数量] [2bytes长度] [目录绝对路径] ... [2bytes长度] [目录绝对路径]int length strlen(argv[i1]);if((indexlen_tag_byteslength) init_length) //空间不够再分配{realloc_length length len_tag_bytes 1; //计算再分配字节多分配1个字节作为结束nullprotocol_body realloc(protocol_body, sizeof(unsigned char)*(init_length realloc_length));if(!protocol_body){printf(再分配空间失败\n);exit(-1);}memset(protocol_bodyindex, 0, sizeof(unsigned char)*(init_length realloc_length)); //初始化再分配空间为零init_length realloc_length;printf(新分配空间成功新分配空间字节大小 %d总空间大小 %d\n,realloc_length, init_length);}protocol_body[index] length / 256 ;protocol_body[index 1] length % 256;index index 2;memcpy(protocol_body index, argv[i1],length);index index length;num_of_dirs;}}index index -2;protocol_body[0] index / 256;protocol_body[1] index % 256;protocol_body[2] num_of_dirs /256;protocol_body[3] num_of_dirs %256;printf(组包数据%d\n,index);for(i0; iindex2; i){printf(%02x ,protocol_body[i]);}printf(\n);//内核交互 -- 字符设备if(ioctl(fd, IOCTL_WRITE_STRING, protocol_body)0){printf(----用户态向内核写入字符串数据失败----\n);return -1;}char recv[256]{0};if(ioctl(fd,IOCTL_READ_STRING,recv)0){printf(----用户态从内核态读取字符串数据失败----\n);return -1;}printf(从内核态读取数据%s\n,recv);//释放申请内存free(protocol_body);protocol_body NULL;close(fd);return 0;
}
上述代码实现把多个int或者char*类型的数据插入链表中但是实际使用中这个链表比没有用和用户态交互我只使用了string类型的数据再把数据存入到protocol_body中通过控制命令IOCTL_WRITE_STRING,实现把protocol_body写入到字符设备供内核模块读取同时内核模块返回一个随机数。
编译命令
gcc -o user_ipc user_ipc.c内核模块
//msg_recv_send.c
#include linux/init.h
#include linux/module.h
#include linux/cdev.h
#include linux/fs.h
#include linux/io.h
#include linux/ioctl.h
#include linux/slab.h
#include linux/uaccess.h
#include linux/unistd.h
#include linux/random.h
#include cmd.h
#include ctl_data.hdev_t dev_num;
struct cdev *cdevp NULL;/*
struct dir_node{int length; //长度char *dir_s; //目录字符串struct list_head list; //链表
};
*/
LIST_HEAD(msg_list_head);//处理
int handle_recv_msg(char *msg, int len){int ret 0;int dir_index0;//清空链表struct dir_node *entry, *tmp;list_for_each_entry_safe(entry, tmp, msg_list_head,list){list_del(entry-list);kfree(entry-dir_s);kfree(entry);}//解析数据int dir_length 0;int num_of_dirs 0;int char_index 2;num_of_dirs msg[0]8 | msg[1];for(dir_index0; dir_indexnum_of_dirs; dir_index){dir_length msg[char_index]8 | msg[char_index1];char_index char_index 2;struct dir_node * new_node kmalloc(sizeof(struct dir_node),GFP_KERNEL);new_node-dir_s kmalloc(sizeof(char)*(dir_length1),GFP_KERNEL);memset(new_node-dir_s, 0, dir_length1);new_node-length dir_length;INIT_LIST_HEAD(new_node-list);memcpy(new_node-dir_s, msgchar_index, dir_length);char_index char_index dir_length;list_add_tail(new_node-list, msg_list_head);}//遍历列表list_for_each_entry(entry, msg_list_head, list){printk(KERN_INFO 接收数据%s\n,entry-dir_s);} return ret;
}
static long my_ioctl(struct file * filp, unsigned int cmd, unsigned long arg){long ret 0;int err 0;int ioarg 0;char kernel_buffer[256];unsigned int random_value 0; if(_IOC_TYPE(cmd) ! IOCTL_TYPE){return -EINVAL;}if(_IOC_NR(cmd) IOCTL_MAXNR){return -EINVAL;}if(_IOC_DIR(cmd) _IOC_READ){err !access_ok((void*)arg, _IOC_SIZE(cmd));}else if(_IOC_DIR(cmd) _IOC_WRITE){err !access_ok((void*)arg, _IOC_SIZE(cmd));}if(err){return -EFAULT;}switch(cmd){case IOCTL_NO_ARG:printk(KERN_INFO print not arg cmd\n);break;case IOCTL_WRITE_INT:ret __get_user(ioarg, (int*)arg);printk(KERN_INFO get data from user space is :%d\n, ioarg);break;case IOCTL_READ_INT:ioarg 1101;ret __put_user(ioarg, (int *)arg);break;case IOCTL_WRITE_STRING:memset(kernel_buffer, 0, sizeof(kernel_buffer));unsigned char len[3]{0};ret copy_from_user(len, (char*)arg, 2);int recv_len 0;recv_len len[0]*256 len[1];printk(KERN_INFO 用户态写入的数据长度 %d,len[0]*256len[1]);char *recv_buffer kmalloc(sizeof(char)*recv_len,GFP_KERNEL);ret copy_from_user(recv_buffer, (unsigned char*)(arg2), recv_len);if(ret!0){printk(KERN_INFO 从用户态拷贝数据失败,失败字节数 %d\n,ret);}printk(KERN_INFO get data from user space is :%*ph\n,recv_len, recv_buffer);//处理接收到的字符串handle_recv_msg(recv_buffer, recv_len);kfree(recv_buffer);break;case IOCTL_READ_STRING://memset(random_value, 0, sizeof(random_value));memset(kernel_buffer, 0, sizeof(kernel_buffer));random_value get_random_int();snprintf(kernel_buffer, sizeof(kernel_buffer),返回随机字符串数值%u,random_value);printk(KERN_INFO kern_buffer : %s\n,kernel_buffer);ret copy_to_user((char *)arg,kernel_buffer,sizeof(kernel_buffer));if(ret 0){printk(KERN_INFO 写文本字符到用户态成功,[%s]。\n,(char*)arg);}else{printk(KERN_INFO 写文本字符到用户态失败未写入字节数 %d。\n,ret);}break;default:return -EINVAL;}return ret;
}static const struct file_operations fops {.owner THIS_MODULE,.unlocked_ioctl my_ioctl
};int __init ioctl_init(void ){int ret ;ret alloc_chrdev_region(dev_num, BASEMINOR, COUNT, NAME);if(ret 0){printk(KERN_ERR alloc_chrdev_region failed...\n);goto err1;}printk(KERN_INFO, major %d\n,MAJOR(dev_num));cdevp cdev_alloc();if(NULL cdevp){printk(KERN_ERR cdev_alloc failed...\n);ret -ENOMEM;goto err2;}cdev_init(cdevp, fops);ret cdev_add(cdevp, dev_num, COUNT);if(ret 0){printk(KERN_INFO cdev_add failed...\n);goto err2;}printk(KERN_INFO ------init completely\n);return 0;
err2:unregister_chrdev_region(dev_num, COUNT);
err1:return ret;
}void __exit ioctl_exit(void){cdev_del(cdevp);unregister_chrdev_region(dev_num, COUNT);printk(KERN_INFO exit success.);
}上述代码中alloc_chrdev_region分配一个名为NAME的字符设备
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
//dev 字符设备存储的指针高12位是主设备号低20位是从设备号
//baseminor是从设备号
//count 请求的设备号数量
//name 设备名内核模块主文件
//file_shield.c
#include linux/init.h
#include linux/module.h
#include linux/kernel.h
#include asm/unistd.h
#include asm/ptrace.h
#include linux/kallsyms.h
#include linux/uaccess.h
#include linux/string.h
#include linux/cred.h
#include hook_func.h
#include ctl_data.h
#include msg_recv_send.hMODULE_LICENSE(GPL);//增加字符设备处理逻辑代码static int __init file_shield_init(void){int ret 0;printk(KERN_INFO init completly);//创建字符设备ioctl_init();printk(KERN_INFO 模块已加载\n);return ret;
}
static void __exit file_shield_exit(void){//卸载字符设备ioctl_exit();printk(KERN_INFO 模块已卸载\n);
}
module_init(file_shield_init);
module_exit(file_shield_exit);内核模块Makefile
KERNELDIR:/lib/modules/$(shell uname -r)/build
EXTRA_CFLAGS -O1PWD $(shell pwd)obj-m file_hook.o
file_hook-objs:file_shield.o msg_recv_send.o all:make -C $(KERNELDIR) M$(PWD) modules
clean:make -C $(KERNELDIR) M$(PWD) clean
编译
sudo make输出 LD [M] /home/admin01/file-shield/file_hook.oBuilding modules, stage 2.MODPOST 1 modulesCC [M] /home/admin01/file-shield/file_hook.mod.oLD [M] /home/admin01/file-shield/file_hook.ko
make[1]: 离开目录“/usr/src/linux-headers-5.4.18-53-generic”
设备节点文件
在Linux系统中设备节点文件是一种用于与设备进行交互的接口。这些设备节点文件通常位于/dev目录下。在Linux系统中设备节点文件是一种用于与设备进行交互的接口。这些设备节点文件通常位于/dev目录下。
设备节点文件是Linux中的一种特殊文件用于与设备进行通信。它们允许用户空间程序通过标准的文件I/O操作如打开、读取、写入、关闭来与设备进行交互。在/dev目录下的每个设备节点文件都对应一个特定的设备或设备类。
在内核模块中注册字符设备时通常使用cdev_add函数它会告诉内核创建相应的设备节点文件。这些设备节点文件将在/dev目录下动态创建以便用户空间程序能够访问注册的设备。
例如如果你的设备被命名为my_device在/dev目录下将创建一个名为my_device的设备节点文件。用户空间程序可以通过打开/dev/my_device来访问你的设备。
需要手动创建一个设备节点文件
sudo mknod /dev/ioctl_test c 240 0加载内核模块
sudo insmod path/file_hook.ko卸载内核模块
sudo rmmod file_hook测试
用户态程序发送接收
内核模块发送与接收