基于内核模块实现linux内核中文件的读写

您所在的位置:网站首页 内核模块的模板如何写好 基于内核模块实现linux内核中文件的读写

基于内核模块实现linux内核中文件的读写

2024-07-12 15:45| 来源: 网络整理| 查看: 265

在为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