基于内核模块实现linux内核中文件的读写 |
您所在的位置:网站首页 › 内核模块的模板如何写好 › 基于内核模块实现linux内核中文件的读写 |
在为linux内核编写的模块中,用户空间的open,read,write,llseek等函数都是不可以使用的。而必须使用其在内核中对应的函数。可以使用filp->open配合struct file里的read/write来进行对文件的读写操作。 直接上干货(内容自己悟!): 例1 : filp->f_op->read filp_open filp->f_op->write #include #include #include #include #include MODULE_AUTHOR("[email protected]."); MODULE_DESCRIPTION("Kernel study and test."); void fileread(const char * filename) { struct file *filp; struct inode *inode; mm_segment_t fs; off_t fsize; char *buf; unsigned long magic; printk("start....\n"); filp=filp_open(filename,O_RDONLY,0); inode=filp->f_dentry->d_inode; magic=inode->i_sb->s_magic; printk("file system magic:%li \n",magic); printk("super blocksize:%li \n",inode->i_sb->s_blocksize); printk("inode %li \n",inode->i_ino); fsize=inode->i_size; printk("file size:%i \n",(int)fsize); buf=(char *) kmalloc(fsize+1,GFP_ATOMIC); //内核空间的地址 fs=get_fs(); set_fs(KERNEL_DS); //设置地址空间,规避检查 filp->f_op->read(filp,buf,fsize,&(filp->f_pos)); //读到内核空间的buf中 set_fs(fs); buf[fsize]='\0'; printk("The File Content is:\n"); printk("%s",buf); filp_close(filp,NULL); } void filewrite(char* filename, char* data) { struct file *filp; mm_segment_t fs; filp = filp_open(filename, O_RDWR|O_APPEND, 0644); if(IS_ERR(filp)) { printk("open error...\n"); return; } fs=get_fs(); set_fs(KERNEL_DS); filp->f_op->write(filp, data, strlen(data),&filp->f_pos); set_fs(fs); filp_close(filp,NULL); } int init_module() { char *filename="/root/test1.c"; printk("Read File from Kernel.\n"); fileread(filename); filewrite(filename, "kernel write test\n"); return 0; } void cleanup_module() { printk("Good,Bye!\n"); }例 2 #include #include #include #include #include #include #include /* get_fs(),set_fs(),get_ds() */ #define FILE_DIR "/root/test.txt" MODULE_LICENSE("GPL"); MODULE_AUTHOR("[email protected]"); char *buff = "module read/write test"; char tmp[100]; static struct file *filp = NULL; static int __init wr_test_init(void) { mm_segment_t old_fs; ssize_t ret; filp = filp_open(FILE_DIR, O_RDWR | O_CREAT, 0644); // if(!filp) if(IS_ERR(filp)) printk("open error...\n"); old_fs = get_fs(); set_fs(get_ds()); filp->f_op->write(filp, buff, strlen(buff), &filp->f_pos); filp->f_op->llseek(filp,0,0); ret = filp->f_op->read(filp, tmp, strlen(buff), &filp->f_pos); set_fs(old_fs); if(ret > 0) printk("%s\n",tmp); else if(ret == 0) printk("read nothing.............\n"); else { printk("read error\n"); return -1; } return 0; } static void __exit wr_test_exit(void) { if(filp) filp_close(filp,NULL); } module_init(wr_test_init); module_exit(wr_test_exit);Makefile: obj-m := os_attack.o KDIR := /lib/modules/$(uname -r)/build/ PWD := $(shell pwd) all:module module: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: rm -rf *.ko *.mod.c *.o Module.* modules.* .*.cmd .tmp_versions也可以使用: filp_open vfs_wirte vfs_read 例: #include #include #include #include static charbuf[] ="你好"; static charbuf1[10]; int __inithello_init(void) { struct file *fp; mm_segment_t fs; loff_t pos; printk("hello enter/n"); fp =filp_open("/home/niutao/kernel_file",O_RDWR | O_CREAT,0644); if (IS_ERR(fp)){ printk("create file error/n"); return -1; } fs =get_fs(); set_fs(KERNEL_DS); pos =0; vfs_write(fp,buf, sizeof(buf), &pos); pos =0; vfs_read(fp,buf1, sizeof(buf), &pos); printk("read: %s/n",buf1); filp_close(fp,NULL); set_fs(fs); return 0; } void __exithello_exit(void) { printk("hello exit/n"); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("GPL"); Makefile: ifneq ($(KERNELRELEASE),) obj-m:=extent_tree_test.o //修改此处 else #generate the path CURRENT_PATH:=$(shell pwd) #the absolute path LINUX_KERNEL_PATH:=/lib/modules/$(shell uname -r)/build #complie object default: make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules clean: make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) clean endif注意: 在调用filp->f_op->read和filp->f_op->write等对文件的操作之前,应该先设置FS。 默认情况下,filp->f_op->read或者filp->f_op->write会对传进来的参数buff进行指针检查。如果不是在用户空间会拒绝访问。因为是在内核模块中,所以buff肯定不在用户空间,所以要增大其寻址范围。 拿filp->f_op->write为例来说明: filp->f_op->write最终会调用access_ok ==> range_ok. 而range_ok会判断访问的地址是否在0 ~ addr_limit之间。如果在,则ok,继续。如果不在,则禁止访问。而内核空间传过来的buff肯定大于addr_limit。所以要set_fs(get_ds())。 这些函数在asm/uaccess.h中定义。以下是这个头文件中的部分内容: #define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) #define KERNEL_DS MAKE_MM_SEG(-1UL) #define USER_DS MAKE_MM_SEG(PAGE_OFFSET) #define get_ds() (KERNEL_DS) #define get_fs() (current_thread_info()->addr_limit) #define set_fs(x) (current_thread_info()->addr_limit = (x)) #define segment_eq(a, b) ((a).seg == (b).seg)可以看到set_fs(get_ds())改变了addr_limit的值。这样就使得从模块中传递进去的参数也可以正常使用了。 在写测试模块的时候,要实现的功能是写进去什么,然后读出来放在tmp数组中。但写完了以后filp->f_ops已经在末尾了,这个时候读是什么也读不到的,如果想要读到数据,则应该改变filp->f-ops的值,这就要用到filp->f_op->llseek函数了。上网查了下,其中的参数需要记下笔记: 系统调用: off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin) offset是偏移量。 若origin是SEEK_SET(0),则将该文件的位移量设置为距文件开始处offset 个字节。 若origin是SEEK_CUR(1),则将该文件的位移量设置为其当前值加offset, offset可为正或负。 若origin是SEEK_END(2),则将该文件的位移量设置为文件长度加offset, offset可为正或负。
|
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |