• 内核中读写文件


    1. 插曲

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

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

    //includeuapiasm-genericunistd.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() 来解决。有如下定义:

    //includeasm-genericuaccess.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) "sfl: " 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!
    ";
    static char read_buf[100];
    
    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
    ", __func__);
        fp = filp_open(FILE_PATH, O_RDWR | O_CREAT, 0644);
        if (IS_ERR(fp)){
            pr_info("open file error
    ");
            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
    ", read_buf);
        filp_close(fp,NULL);
    
        set_fs(fs);
    
        return 0;
    }
    
    void __exit kernel_file_op_exit(void)
    {
        pr_info("kernel file operation module exit.
    ");
    }
     
    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] sfl: enter kernel_file_op_init
    [390465.664072] sfl: 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] sfl: kernel file operation module exit.
    root@ubuntu:/work/7.kernel_file_op#
  • 相关阅读:
    update语句条件判断更新
    添加ll命令
    在php cli下可以使用 STDIN 来实现标准输入
    windows修改PowerShell(命令提示符)默认中文编码方式
    命令创建控制器
    windows系统关闭某个端口的服务(以443端口为例子)
    unable to create ...erroractionpreference....
    【抓包工具】使用Fiddler关于“由于目标计算机积极拒绝,无法连接。”的解决方案
    切换国内镜像:Content-Length mismatch, received 431737 bytes out of the expected 760836
    匹配函数函数备注
  • 原文地址:https://www.cnblogs.com/hellokitty2/p/14655970.html
Copyright © 2020-2023  润新知