Linux kernel 将buf保存到文件

 old_fs = get_fs();
    set_fs(get_ds());
   
    filp->f_op->write(filp, buff, strlen(buff), &filp->f_pos);
   
    filp->f_op->llseek(filp,0,0);
    ret = filp->f_op->read(filp, tmp, strlen(buff), &filp->f_pos);
   
    set_fs(old_fs);

要注意filp->f_op->write 可能为空
一般用以下方法将buf保存到文件


old_fs = get_fs();
     set_fs(KERNEL_DS);
 
 
     if( first_flag==0){
     first_flag=1;
     /* open file to write */
     fp = filp_open("/data/at_log1", O_WRONLY|O_CREAT, 0640);
     if (!fp) {
         printk("%s: open file error\n", __FUNCTION__);
         ret = -1;
         goto exit;
         }
     }
     pos=(unsigned long)offset;
 
     /* Write buf to file */
     nwrite=vfs_write(fp, buf, size, &pos);

访问文件系统主要用到以下函数。
1 打开文件,与c库类似
strcut file* filp_open(const char* filename, int open_mode, int mode);
2 读写。pos为偏移,需要初始化。更需要注意的是buffer是__user* ,指用户空间地址, 如果我们直接使用内核空间的指针,则会返回-EFALUT。则需要多做一步修改内核地址检查的操作。
ssize_t vfs_read(struct file* filp, char __user* buffer, size_t len, loff_t* pos);
ssize_t vfs_write(struct file* filp, const char __user* buffer, size_t len, loff_t* pos);
get_fs()、set_fs() 的作用
内存地址检查的处理方式。这点补充有关内核地址和用户空间地址的操作。在mm_segment_t fs中只有两个值,USER_DS,KERNEL_DS,分别代表用户空间和内核空间,默认情况为USER_DS,即对用户空间地址检查并做变换。
老版本的内核用上述方法是OK的。我用的是5.15的内核,编译到get_fs、set_fs会报错。
在这里插入图片描述
5.10后的版本不能使用set_fs了。
在这里插入图片描述
看到一种方案,根据内核版本做以下调整。Linux-5.0.0前的内核使用set_fs( get_ds() ,介于linux-5.0.0和linux-5.10.0之间的内核使用set_fs( KERNEL_DS ),linux-5.10.0之后的内核使用old_fs = force_uaccess_begin();
在我用的内核下编译还是有问题,应该是我的内核版本太新了。


#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0)
    old_fs = get_fs();
    set_fs( get_ds() );
#elif LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0)
    old_fs = get_fs();
    set_fs( KERNEL_DS );
#else
    old_fs = force_uaccess_begin();
#endif
...
        ret = fd->f_op->unlocked_ioctl(fd, TCSETS, (unsigned long int) &newtio);
...
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0)
    set_fs(old_fs);
#else
    force_uaccess_end(old_fs);
#endif

coredump保存内存到文件的方法
kernel里有大量保存内存的文件的操作,参考coredump的用法。
在这里插入图片描述
coredump中使用了__kernel_write,这个函数不依赖set_fs的操作。
一个完整的·demo


1 #include <linux/init.h>
  2 #include <linux/module.h>
  3 #include <linux/sched.h>
  4 #include <linux/dcache.h>
  5 #include <linux/fs.h>
  6 #include <linux/err.h>
  7 #include <linux/string.h>
  8 #include <linux/errno.h>
  9 #include <asm/fcntl.h>
 10 #include <asm/processor.h>
 11 #include <asm/uaccess.h>
 12 #include <linux/thread_info.h>
 13
 14 int __init hello_init(void)
 15 {
 16     unsigned char buf1[12]="hello world.";
 17     unsigned char buf2[12]="kernel file.";
 18
 19     struct file *fp;
 20     //mm_segment_t fs;
 21     loff_t pos;
 22
 23     printk("hello enter\n");
 24     fp = filp_open("/root/perf_file", O_RDWR | O_CREAT, 0644);
 25     if (IS_ERR(fp)) {
 26         printk("create file error\n");
 27         return -1;
 28     }
 29     //fs = force_uaccess_begin();
 30
 31    // current_thread_info()->addr_limit = 0;
 32     //fs = get_fs();
 33     //set_fs(KERNEL_DS);
 34
 35     pos = fp->f_pos;
 36     __kernel_write(fp, buf1, sizeof(buf1), &pos);
 37     fp->f_pos = pos;
 38
 39     pos = fp->f_pos;
 40     __kernel_write(fp, buf2, sizeof(buf2), &pos);
 41     fp->f_pos = pos;
 42
 43     //set_fs(fs);
 44     //force_uaccess_end(fs);
 45
 46     filp_close(fp, NULL);
 47     return 0;
 48 }
 49
 50 void __exit hello_exit(void)
 51 {
 52     printk("hello exit\n");
 53 }
 54
 55 module_init(hello_init);
 56 module_exit(hello_exit);
 57
 58 MODULE_LICENSE("GPL");

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值