内核态文件操作

文件开关

extern struct file *fil_open(const char*, int, umode_t );

  • 第一个参数表明要打开或创建文件的名称(包括路径部分)。
  • 第二个参数文件的打开方式,其取值与标准库中的open相应参数类似,可以取O_CREAT,O_RDWR,O_RDONLY等。
  • 第三个参数创建文件时使用,设置创建文件的读写权限,其它情况可以设为0

extern int flip_close(struct file *, fl_owner_t id);

  • 第一个参数是filp_open返回的file结构体指针
  • 第二个参数基本上都是NULL

文件读写

extern ssize_t vfs_read(struct file*, char __user *, size_t, loff_t *)
extern ssize_t vfs_write(struct file*, const char __user *, size_t, loff_t *)

  • 第一个参数是filp_open返回的file结构体指针
  • 第二个参数是buf,注意,这个参数有用__user修饰,表明buf指向用户空间的地址,如果传入内核空间的地址,就会报错,并返回-EFAULT,但在kernel中,要使这两个读写函数使用kernel空间的buf指针也能正确工作,需要使用set_fs()。
  • set_fs(): 该函数的作用是改变kernel对内存地址检查的处理方式,其实该函数的参数fs只有两个取值:USER_DS,KERNEL_DS,分别代表用户空间和内核空间,默认情况下,kernel取值为USER_DS,即对用户空间地址检查并做变换。
    那么要在这种对内存地址做检查变换的函数中使用内核空间地址,就需要使用set_fs(KERNEL_DS)进行设置,它的作用是取得当前的设置,这两个函数的一般用法为:
filp_open()

mm_segment_t old_fs;
old_fs = get_fs();
set_fs(KERNEL_DS);//set_fs(get_ds());
...... //与内存有关的操作
set_fs(old_fs);//恢复

filp_close

补充

  1. Linux Kernel组成员不赞成在kernel中独立的读写文件(这样做可能会影响到策略和安全问题),对内核需要操作的文件内容,最好由应用层配合完成
  2. 这些函数的正确运行需要依赖于进程环境,因此,有些函数不能在中断的handle或Kernel中不属于任何进程的代码中执行,否则可能出现崩溃,要避免这种情况发生,可以在kernel中创建内核线程,将这些函数放在线程环境下执行。

小结

open,write的参数在用户空间,在这些系统调用的实现里需要对参数进行检查,就是检查它的参数指针地址是不是用户空间的。
系统调用本来是提供给用户空间的程序访问的,所以,对传递给它的参数(比如上面的buf、buf1),它默认会认为来自用户空间。
在vfs_write()函数中,为了保护内核空间,一般会用get_fs()得到的值来和USER_DS进行比较,从而防止用户空间程序“蓄意”破坏内核空间。

为了解决这个问题, set_fs(KERNEL_DS)将其能访问的空间限制扩大到KERNEL_DS,这样就可以在内核顺利使用系统调用了!
内核使用系统调用参数肯定是内核空间,为了不让这些系统调用检查参数所以必须设置 set_fs(KERNEL_DS)才能使用该系统调用。
vfs_write的流程可调用access_ok,而access_ok会判断访问的buf是否在0~addr_limit之间,如果是就ok;否则-EFAULT,这显然是为用户准备的检查。
addr_limit一般设为USER_DS,在内核空间,buf肯定>USER_DS,必须修改addr_limit,这就是set_fs的由来。

vfs_write()操作的参数一般是用户区的数据,当想读写内核区数据时,就需要set_fs(KERNEL_DS)

例子

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

static char buf[] ="你好";
static char buf1[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");
posted @ 2020-02-28 09:07  friedCoder  阅读(757)  评论(0)    收藏  举报