113

我知道所有关于为什么不应该从内核读取/写入文件的讨论,而是如何使用/procnetlink来做到这一点。无论如何,我想读/写。我还阅读了 让我发疯 - 你永远不应该在内核中做的事情

但是,问题是 2.6.30 不导出sys_read(). 而是包裹在SYSCALL_DEFINE3. 因此,如果我在我的模块中使用它,我会收到以下警告:

WARNING: "sys_read" [xxx.ko] undefined!
WARNING: "sys_open" [xxx.ko] undefined!

显然insmod无法加载模块,因为链接没有正确发生。

问题:

  • 如何在 2.6.22 之后在内核中读/写(其中sys_read()/sys_open()未导出)?
  • 一般来说,如何SYSCALL_DEFINEn()在内核中使用封装在宏中的系统调用?
4

2 回答 2

137

您应该意识到,您应该尽可能避免来自 Linux 内核的文件 I/O。主要思想是“更深一层”并直接调用VFS 级函数而不是 syscall 处理程序:

包括:

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

打开文件(类似于打开):

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;
}

关闭文件(类似于关闭):

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

从文件中读取数据(类似于 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;
}   

将数据写入文件(类似于 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;
}

同步更改文件(类似于 fsync):

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

[编辑] 最初,我建议使用 file_fsync,它在较新的内核版本中消失了。感谢那个可怜的家伙建议改变,但他的改变被拒绝了。编辑在我审查之前被拒绝。

于 2009-07-26T11:31:03.863 回答
41

从 Linux 内核版本 4.14 开始,vfs_read不再导出vfs_write函数以供在模块中使用。相反,提供了专门用于内核文件访问的函数:

# Read the file from the kernel space.
ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos);

# Write the file from the kernel space.
ssize_t kernel_write(struct file *file, const void *buf, size_t count,
            loff_t *pos);

此外,filp_open不再接受用户空间字符串,因此它可以直接用于内核访问无需与 跳舞set_fs)。

于 2018-12-24T20:54:44.110 回答