zoukankan      html  css  js  c++  java
  • 内核模块中filp->open对文件的读写【转】

    转自:http://guiltcool.blog.chinaunix.net/uid-9950859-id-98917.html

    平时网络部分的东西碰的多些,这块一开始还真不知道怎么写,因为肯定和在用户空间下是不同的。google过后,得到以下答案。一般可以用两种方法:第一种是用系统调用。第二种方法是filp->open()等函数。下面分别来说下这两种方法。


    1 利用系统调用:
    sys_open,sys_write,sys_read等。
    其实分析过sys_open可以知道,最后调用的也是filp->open。
    sys_open ==> do_sys_open ==> filp->open
    在linuxsir上的一个帖子,上面一个版主说:sys_open和进程紧密相关,往往不在内核中使用。
    而其实sys_open最后也是调用了filp->open。
    其实好像Linux2.6.20后面就不推荐使用sys_open,那我们这里就就后者进行详细的介绍

    2 filp->open等函数。
    在模块中,用户空间的open,read,write,llseek等函数都是不可以使用的。应该使用其在内核中对应的函数。可以使用filp->open配合struct file里的read/write来进行对文件的读写操作。


    例子1:

    #include 
    #include 
    #include 
    #include 
    #include 

    MODULE_AUTHOR("Kenthy@163.com."); 
    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("<1>start.... "); 
      filp=filp_open(filename,O_RDONLY,0); 
      inode=filp->f_dentry->d_inode;  
      
      magic=inode->i_sb->s_magic; 
      printk("<1>file system magic:%li ",magic); 
      printk("<1>super blocksize:%li ",inode->i_sb->s_blocksize); 
      printk("<1>inode %li ",inode->i_ino); 
      fsize=inode->i_size; 
      printk("<1>file size:%i ",(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)); 
      set_fs(fs); 

      buf[fsize]=''; 
      printk("<1>The File Content is: "); 
      printk("<1>%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... "); 
          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("<1>Read File from Kernel. "); 
      fileread(filename); 
      filewrite(filename, "kernel write test ");
      return 0; 


    void cleanup_module() 

      printk("<1>Good,Bye! "); 
    }


    eg2:

    #include
    #include
    #include

    #include

    #include
    #include
    #include /* get_fs(),set_fs(),get_ds() */

    #define FILE_DIR "/root/test.txt"

    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("kenthy@163.com");

    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... ");
        
        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 ",tmp);
        else if(ret == 0)
            printk("read nothing............. ");
        else 
            {
                printk("read error ");
                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);


    3.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->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可为正或负。

    ok,that's all.

  • 相关阅读:
    Spark2 Dataset DataFrame空值null,NaN判断和处理
    Spark2 文件处理和jar包执行
    &与&&, |与||区别
    Scala实现乘法口诀
    Hive desc
    Hive FUNCTIONS函数
    Hive show
    MySQL行列转换拼接
    MySQL字符串连接
    SQLServer之索引简介
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/10768017.html
Copyright © 2011-2022 走看看