# 函数

功能 函数
打开文件 strcut file* **filp_open**(const char* filename, int open_mode, int mode);
读取文件 ssize_t vfs_read(struct file* filp, char __user* buffer, size_t len, loff_t* pos);
写文件 ssize_t vfs_write(struct file* filp, const char __user* buffer, size_t len, loff_t* pos);
关闭文件 int filp_close(struct file*filp, fl_owner_t id);

# 读写

注意读写函数的第二个参数 buffer,前面有 __user 修饰符,这就要求这两个 buffer 指针都应该指向用户空间的内存;如果对该参数传递 kernel 空间的指针,这两个函数都会返回失败-EDAULT。

但在 Kernel 中,我们一般不容易生成用户空间的指针,或者不方便独立使用用户空间内存。

## set_fs()、get_fs()

#define get_fs()  (current_thread_info()->addr_limit)
#define set_fs(x) (current_thread_info()->addr_limit = (x))

要使得这个函数使用 Kernel 空间的 buffer 也能正常工作,需要使用 set_fs() 函数(也可能是宏定义),该函数只有一个参数,两种取值可能:USER_DS,KERNEL_DS,分别代表用户空间和内核空间,默认情况下,kernle取值为 USER_DS,即对用户空间地址检查并做变换。那么要在这种对内存地址做检查变换的函数中使用内核空间地址,就需要使用 set_fs(KERNEL_DS)进行设置,即改变(current_thread_info()->addr_limit的大小。 get_fs() 一般也可能是宏定义,它的作用是取得当前的设置。

#define KERNEL_DS   ((mm_segment_t) { 0UL })
#define USER_DS     ((mm_segment_t) { -0x40000000000UL })

KERNEL_DS 范围很大,意味着可以访问整个内存空间,USER_DS 只能访问用户空间内存。

filp.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/dcache.h>
#include <linux/fs.h>
#include <linux/err.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <asm/fcntl.h>
#include <asm/processor.h>
#include <asm/uaccess.h>

int __init hello_init(void)
{
    unsigned char buf1[12]="hello world.";
    unsigned char buf2[12]="kernel file.";

    struct file *fp;
    mm_segment_t fs;
    loff_t pos;

    printk("hello enter\n");
    fp = filp_open("/home/wifi_group/liyongjun/filp/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 = fp->f_pos;
    vfs_write(fp, buf1, sizeof(buf1), &pos);
    fp->f_pos = pos;

    pos = fp->f_pos;
    vfs_write(fp, buf2, sizeof(buf2), &pos);
    fp->f_pos = pos;

    set_fs(fs);

    filp_close(fp, NULL);
    return 0;
}

void __exit hello_exit(void)
{
    printk("hello exit\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");

Makefile

CROSS_COMPILE=
AR=$(CROSS_COMPILE)ar
ARCH=x86_64
LD=$(CROSS_COMPILE)ld
CC=$(CROSS_COMPILE)gcc


obj-m := filp.o


KDIR := /lib/modules/$(shell uname -r)/build
all:
        make -C $(KDIR) M=$(PWD) modules
clean:
        rm -rf $(srcdir)*.o $(srcdir)*.mod.o $(srcdir)*.ko $(srcdir)*.mod.c *.o *.mod.o *.ko *.mod.c *.symvers *.order .*.cmd

参考:

linux内核态文件操作filp_open/filp_close/vfs_read/vfs_write