内核中读写文件

1. 插曲

阅读Linux内核源码,可以知道read 和 write 这两个系统调用陷入内核实际执行的是 sys_read 和 sys_write 这两个函数,但是这两个函数没有使用 EXPORT_SYMBOL 导出,也就是说其他模块不能使用。

read系统调用的调用号定义:

//include\uapi\asm-generic\unistd.h
#define __NR_read 63
__SYSCALL(__NR_read, sys_read) /*[63] = sys_read*/

read系统调用的实现定义:

复制代码
//fs/read_write.c
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) /*实现sys_read*/
{
    struct fd f = fdget_pos(fd);
    ssize_t ret = -EBADF;

    if (f.file) {
        loff_t pos = file_pos_read(f.file);
        ret = vfs_read(f.file, buf, count, &pos);
        if (ret >= 0)
            file_pos_write(f.file, pos);
        fdput_pos(f);
    }
    return ret;
}
复制代码

 

2. 内核空间与用户空间地址区别

在 vfs_read 和 vfs_write 函数中,其参数 buf 指向的用户空间的内存地址,如果我们直接使用内核空间的指针,则会返回-EFALUT。这是因为使用的缓冲区超过了用户空间的地址范围。一般系统调用会要求你使用的缓冲区不能在内核区。这个可以用 set_fs()、get_fs() 来解决。有如下定义:

//include\asm-generic\uaccess.h
#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) })
#define KERNEL_DS    MAKE_MM_SEG(~0UL)
#define USER_DS MAKE_MM_SEG(PAGE_OFFSET)
#define get_ds() (KERNEL_DS)  /*获取地址空间最大范围值*/
#define get_fs() (current_thread_info()->addr_limit) /*获取当前进程能访问的地址范围限制*/
#define set_fs(fs) (current_thread_info()->addr_limit = fs)  /*设置当前进程能访问的地址范围限制*/

内核中的使用流程如下:

mm_segment_t fs = get_fs(); /*保存当前进程的地址限制,以便事后恢复*/
set_fs(KERNEL_DS); /*设置所有地址空间都能访问*/

vfs_write();
...
vfs_read();

set_fs(fs); /*恢复进程原地址设置值*/

 

3. 示例程序

复制代码
/* file name: kernel_file_operation_test.c */

#define pr_fmt(fmt) "kfop: " fmt

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

#define FILE_PATH "/work/7.kernel_file_op/hello.bin"

static char write_buf[100] ="Read The Fucking Source Code!\n";
static char read_buf[100];

static int __init kernel_file_op_init(void)
{
    struct file *fp;
    mm_segment_t fs; /*typedef unsigned long mm_segment_t;*/
    loff_t pos;

    pr_info("enter %s\n", __func__);
    fp = filp_open(FILE_PATH, O_RDWR | O_CREAT, 0644);
    if (IS_ERR(fp)){
        pr_info("open file error\n");
        return -1;
    }

    fs = get_fs();
    set_fs(KERNEL_DS);

    pos =0;
    vfs_write(fp, write_buf, sizeof(write_buf), &pos);
    pos =0;
    vfs_read(fp, read_buf, sizeof(read_buf), &pos);

    pr_info("read: %s\n", read_buf);
    filp_close(fp,NULL);

    set_fs(fs);

    return 0;
}

static void __exit kernel_file_op_exit(void)
{
    pr_info("kernel file operation module exit.\n");
}
 
module_init(kernel_file_op_init);
module_exit(kernel_file_op_exit);
 
MODULE_LICENSE("GPL");
复制代码
复制代码
# file name: Makefile

obj-m += kernel_file_operation_test.o

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

clean:
    rm -rf *.ko
复制代码

测试结果:

复制代码
root@ubuntu:/work/7.kernel_file_op# ls
hello.bin  kernel_file_operation_test.c  kernel_file_operation_test.ko  Makefile
root@ubuntu:/work/7.kernel_file_op# insmod kernel_file_operation_test.ko 
root@ubuntu:/work/7.kernel_file_op# dmesg -c
[390465.664047] kfop: enter kernel_file_op_init
[390465.664072] kfop: read: Read The Fucking Source Code!
[390465.664072] 
root@ubuntu:/work/7.kernel_file_op# cat hello.bin 
Read The Fucking Source Code!
root@ubuntu:/work/7.kernel_file_op# rmmod kernel_file_operation_test 
root@ubuntu:/work/7.kernel_file_op# dmesg -c
[390615.379792] kfop: kernel file operation module exit.
root@ubuntu:/work/7.kernel_file_op#
复制代码

 

4. 新版本内核(4.19以后)已经定义了kernel_write函数

复制代码
ssize_t kernel_write(struct file *file, const void *buf, size_t count, loff_t *pos) //fs/read_write.c
{
    mm_segment_t old_fs;
    ssize_t res;

    old_fs = get_fs();
    set_fs(get_ds());
    /* The cast to a user pointer is valid due to the set_fs() */
    res = vfs_write(file, (__force const char __user *)buf, count, pos);
    set_fs(old_fs);

    return res;
}
EXPORT_SYMBOL(kernel_write);
复制代码

 

posted on   Hello-World3  阅读(1552)  评论(0编辑  收藏  举报

编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
历史上的今天:
2019-04-13 java中的Object类和其clone()
2019-04-13 netstat

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示