Linux内核 -- 读写文件系统文件之kernel_read与kernel_write

Linux 内核中文件读写详解

在 Linux 内核中,读写文件的操作与用户态的文件操作有所不同。内核中操作文件需要使用专门的接口和 API,不能直接使用如 fopenfreadfwrite 等用户态的标准 C 库函数。内核内的文件操作主要通过 file 结构体进行,配合 vfs(虚拟文件系统)的相关 API 来读写文件。

1. 打开文件:filp_open

在内核中要操作文件,首先要打开文件。内核中使用的是 filp_open 函数。

struct file *filp_open(const char *filename, int flags, umode_t mode);
  • filename:文件路径。
  • flags:文件打开的标志(例如 O_RDONLYO_WRONLYO_RDWR 等,可以与用户态中的标志相同)。
  • mode:当创建新文件时的权限(可选)。

示例:

struct file *file;
file = filp_open("/path/to/file", O_RDONLY, 0);
if (IS_ERR(file)) {
    printk("Failed to open file\n");
    return PTR_ERR(file);
}

2. 读取文件:kernel_read

读取文件的主要接口是 kernel_read,该函数从已打开的文件中读取数据。

ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos);
  • file:文件结构指针。
  • buf:读取数据的缓冲区。
  • count:读取的字节数。
  • pos:文件偏移量指针。

示例:

loff_t pos = 0;
char buf[128];
ssize_t ret = kernel_read(file, buf, sizeof(buf), &pos);
if (ret >= 0) {
    printk("Read %zd bytes from file\n", ret);
} else {
    printk("Failed to read from file\n");
}

3. 写入文件:kernel_write

写入文件可以使用 kernel_write 接口,该函数将数据写入到文件中。

ssize_t kernel_write(struct file *file, const void *buf, size_t count, loff_t *pos);
  • file:文件结构指针。
  • buf:要写入的数据缓冲区。
  • count:要写入的字节数。
  • pos:文件偏移量指针。

示例:

loff_t pos = 0;
const char *data = "Hello, kernel!";
ssize_t ret = kernel_write(file, data, strlen(data), &pos);
if (ret >= 0) {
    printk("Wrote %zd bytes to file\n", ret);
} else {
    printk("Failed to write to file\n");
}

4. 同步文件:vfs_fsync

为了确保文件写入磁盘,可以使用 vfs_fsync 来同步文件数据。

int vfs_fsync(struct file *file, int datasync);
  • file:文件结构指针。
  • datasync:如果为 1,只同步文件数据;如果为 0,则同步所有文件元数据和数据。

示例:

vfs_fsync(file, 0);

5. 关闭文件:filp_close

操作完文件后,需要关闭文件,释放资源。

int filp_close(struct file *filp, fl_owner_t id);

示例:

filp_close(file, NULL);

6. 获取和设置文件偏移量:vfs_llseek

文件的读写偏移量可以通过 vfs_llseek 函数来获取和设置。

loff_t vfs_llseek(struct file *file, loff_t offset, int whence);
  • file:文件结构指针。
  • offset:偏移量。
  • whence:基准位置,如 SEEK_SETSEEK_CURSEEK_END

示例:

loff_t new_pos;
new_pos = vfs_llseek(file, 0, SEEK_END); // 移动到文件末尾

7. 不需要 get_fsset_fs 的保护说明

在旧版本的 Linux 内核中,内核与用户态之间的内存空间隔离机制要求在进行文件读写时使用 get_fs()set_fs() 函数来切换内存访问权限。这些函数将内核代码的内存访问限制临时解除,允许访问用户态内存地址。

但是,在现代的 Linux 内核(从 5.10 版本起),已经废除了这种方式,内核提供的 kernel_readkernel_write 等接口不再需要 get_fsset_fs 的切换。这些 API 只在内核空间中工作,不再涉及用户空间的内存访问,因此没有了跨空间访问的风险,开发者不需要手动进行切换保护。

这大大简化了内核中文件操作的代码逻辑,并减少了因跨空间访问而导致的安全隐患。

8. 综合示例

下面是一个完整的内核模块示例,它打开一个文件,读取其中的内容,写回一些数据并关闭文件。

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

static int __init file_rw_init(void) {
    struct file *file;
    char *buf;
    loff_t pos = 0;
    ssize_t ret;

    // 打开文件
    file = filp_open("/path/to/file", O_RDWR, 0);
    if (IS_ERR(file)) {
        printk("Failed to open file\n");
        return PTR_ERR(file);
    }

    // 分配缓冲区
    buf = kmalloc(128, GFP_KERNEL);
    if (!buf) {
        filp_close(file, NULL);
        return -ENOMEM;
    }

    // 读取文件
    ret = kernel_read(file, buf, 128, &pos);
    if (ret >= 0) {
        printk("Read %zd bytes from file: %s\n", ret, buf);
    } else {
        printk("Failed to read file\n");
    }

    // 写入文件
    pos = 0;
    ret = kernel_write(file, "Hello, kernel!", 14, &pos);
    if (ret >= 0) {
        printk("Wrote %zd bytes to file\n", ret);
    } else {
        printk("Failed to write file\n");
    }

    // 关闭文件
    filp_close(file, NULL);
    kfree(buf);
    return 0;
}

static void __exit file_rw_exit(void) {
    printk("File read/write module exited\n");
}

module_init(file_rw_init);
module_exit(file_rw_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple file read/write kernel module");

总结

在 Linux 内核中进行文件操作,主要依赖 filp_openkernel_readkernel_write 等函数完成。与用户态不同,内核中需要通过 struct filevfs 接口来操作文件。现代内核中已不再需要 get_fsset_fs 的切换保护,进一步简化了文件操作代码。正确处理文件的打开、关闭,以及内核内存的分配和释放,是避免内核崩溃或其他问题的关键。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值