天天看點

How to read/write files within a Linux kernel module?

You should be aware that that you should avoid file I/O when possible. The main idea is to go "one level deeper" and call VFS level functions instead of the syscall handler directly:

Includes:

#include <linux/fs.h>
#include <asm/segment.h>
#include <asm/uaccess.h>
#include <linux/buffer_head.h>
           

Opening a file (similar to open):

struct file* file_open(const char* path, int flags, int rights) {
    struct file* filp = NULL;
    mm_segment_t oldfs;
    int err = 0;

    oldfs = get_fs();
    set_fs(get_ds());
    filp = filp_open(path, flags, rights);
    set_fs(oldfs);
    if(IS_ERR(filp)) {
        err = PTR_ERR(filp);
        return NULL;
    }
    return filp;
}
           

Close a file (similar to close):

void file_close(struct file* file) {
    filp_close(file, NULL);
}
           

Reading data from a file (similar to pread):

int file_read(struct file* file, unsigned long long offset, unsigned char* data, unsigned int size) {
    mm_segment_t oldfs;
    int ret;

    oldfs = get_fs();
    set_fs(get_ds());

    ret = vfs_read(file, data, size, &offset);

    set_fs(oldfs);
    return ret;
}   
           

Writing data to a file (similar to pwrite):

int file_write(struct file* file, unsigned long long offset, unsigned char* data, unsigned int size) {
    mm_segment_t oldfs;
    int ret;

    oldfs = get_fs();
    set_fs(get_ds());

    ret = vfs_write(file, data, size, &offset);

    set_fs(oldfs);
    return ret;
}
           

Syncing changes a file (similar to fsync):

int file_sync(struct file* file) {
    vfs_fsync(file, 0);
    return 0;
}
           

[Edit] Originally, I proposed using file_fsync, which is gone in newer kernel versions. Thanks to the poor guy suggesting the change, but whose change was rejected. The edit was rejected before I could review it.

在VFS的支援下,使用者态程序讀寫任何類型的檔案系統都可以使用read和write着兩個系統調用,但是在linux核心中沒有這樣的系統調用我們如何操作檔案呢?我們知道read和write在進入核心态之後,實際執行的是sys_read和sys_write,但是檢視核心源代碼,發現這些操作檔案的函數都沒有導出(使用EXPORT_SYMBOL導出),也就是說在核心子產品中是不能使用的,那如何是好?

通過檢視sys_open的源碼我們發現,其主要使用了do_filp_open()函數,該函數在fs/namei.c中,而在改檔案中,filp_open函數也是調用了do_filp_open函數,并且接口和sys_open函數極為相似,調用參數也和sys_open一樣,并且使用EXPORT_SYMBOL導出了,是以我們猜想該函數可以打開檔案,功能和open一樣。使用同樣的查找方法,我們找出了一組在核心中操作檔案的函數,如下:

功能 函數原型
打開檔案 struct file *filp_open(const char *filename,int flags, int mode)
讀取檔案 ssize_t vfs_read(struct file *file,char __user *buf, size_t count, loff_t *pos)
寫檔案 ssize_t vfs_write(struct file *file,const char __user *buf,size_t count, loff_t *pos)
關閉檔案 int filp_close(struct file *filp, fl_owner_t id)

我們注意到在vfs_read和vfs_write函數中,其參數buf指向的使用者空間的記憶體位址,如果我們直接使用核心空間的指針,則會傳回-EFALUT。是以我們需要使用

set_fs()和get_fs()宏來改變核心對記憶體位址檢查的處理方式,是以在核心空間對檔案的讀寫流程為:

  1. mm_segment_tfs = get_fs();
  2. set_fs(KERNEL_FS);
  3. //vfs_write();
  4. vfs_read();
  5. set_fs(fs);

下面為一個在核心中對檔案操作的例子:

  1. #include <linux/module.h>
  2. #include <linux/init.h>
  3. #include <linux/fs.h>
  4. #include <linux/uaccess.h>
  5. static charbuf[] ="你好";
  6. static charbuf1[10];
  7. int __inithello_init(void)
  8. {
  9.     struct file *fp;
  10.     mm_segment_t fs;
  11.     loff_t pos;
  12.     printk("hello enter/n");
  13.     fp =filp_open("/home/niutao/kernel_file",O_RDWR | O_CREAT,0644);
  14.     if (IS_ERR(fp)){
  15.         printk("create file error/n");
  16.         return -1;
  17.     }
  18.     fs =get_fs();
  19.     set_fs(KERNEL_DS);
  20.     pos =0;
  21.     vfs_write(fp,buf, sizeof(buf), &pos);
  22.     pos =0;
  23.     vfs_read(fp,buf1, sizeof(buf), &pos);
  24.     printk("read: %s/n",buf1);
  25.     filp_close(fp,NULL);
  26.     set_fs(fs);
  27.     return 0;
  28. }
  29. void __exithello_exit(void)
  30. {
  31.     printk("hello exit/n");
  32. }
  33. module_init(hello_init);
  34. module_exit(hello_exit);
  35. MODULE_LICENSE("GPL");