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");