需要了解的Linux内核读写文件

1. 序曲
在用户态,读写文件可以通过read和write这两个系统调用来完成(c库函数实际上是对系统调用的封装)。但是,在内核态没有这样的系统调用,我们又该如何读写文件呢?
阅读linux内核源码,可以知道陷入内核执行的是实际执行的是sys_read和sys_write这两个函数,但是这两个函数没有使用export_symbol导出,也就是说其他模块不能使用。
在fs/open.c中系统调用具体实现如下(内核版本2.6.34.1):
syscall_define3(open, const char __user *, filename, int, flags, int, mode)
{
long ret;
if (force_o_largefile())
flags |= o_largefile;
ret = do_sys_open(at_fdcwd, filename, flags, mode);
/* avoid regparm breakage on x86: */
asmlinkage_protect(3, ret, filename, flags, mode);
return ret;
}
跟踪do_sys_open()函数,就会发现它主要使用了do_filp_open()函数该函数在fs/namei.c中,而在该文件中,filp_open函数也是调用了do_filp_open函数,并且接口和sys_open函数极为相似,调用参数也和sys_open一样,并且使用export_symbol导出了,所以我们猜想该函数可以打开文件,功能和open一样。
使用同样的方法,找出了一组在内核操作文件的函数,如下:
功能函数原型打开文件struct file *filp_open(const char *filename, int flags, int mode)读文件ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)写文件ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)关闭文件int filp_close(struct file *filp, fl_owner_t id)
2. 内核空间与用户空间
在vfs_read和vfs_write函数中,其参数buf指向的用户空间的内存地址,如果我们直接使用内核空间的指针,则会返回-efalut。这是因为使用的缓冲区超过了用户空间的地址范围。一般系统调用会要求你使用的缓冲区不能在内核区。这个可以用set_fs()、get_fs()来解决。
在include/asm/uaccess.h中,有如下定义:
#define make_mm_seg(s) ((mm_segment_t) { (s) })
#define kernel_ds make_mm_seg(0xffffffff)
#define user_ds make_mm_seg(page_offset)
#define get_ds() (kernel_ds)
#define get_fs() (current->addr_limit)
#define set_fs(x) (current->addr_limit = (x))
如果使用,如下:
mm_segment_t fs = get_fs();
set_fs(kernel_fs);
//vfs_write();
vfs_read();
set_fs(fs);
详尽解释:系统调用本来是提供给用户空间的程序访问的,所以,对传递给它的参数(比如上面的buf),它默认会认为来自用户空间,在read或write()函数中,为了保护内核空间,一般会用get_fs()得到的值来和user_ds进行比较,从而防止用户空间程序“蓄意”破坏内核空间;而现在要在内核空间使用系统调用,此时传递给read或write()的参数地址就是内核空间的地址了,在user_ds之上(user_ds ~ kernel_ds),如果不做任何其它处理,在write()函数中,会认为该地址超过了user_ds范围,所以会认为是用户空间的“蓄意破坏”,从而不允许进一步的执行;为了解决这个问题; set_fs(kernel_ds);将其能访问的空间限制扩大到kernel_ds,这样就可以在内核顺利使用系统调用了!
3. 完整的例子
#include
#include
#include
#include
#include
#include oracle
#include ssh
#include linux
#include
#define my_file /root/tmp.txt
char buf[128];
struct file *file = null;
static int __init init(void)
{
mm_segment_t old_fs;
printk(hello, i'm the module that intends to write messages to file/n);
if(file == null)
file = filp_open(my_file, o_rdwr | o_append | o_creat, 0644);
if (is_err(file)) {
printk(erroroccured while opening file %s./n, my_file);
return 0;
}
sprintf(buf,%s, the messages.);
old_fs = get_fs();
set_fs(kernel_ds);
file->f_op->write(file, (char *)buf, sizeof(buf), &file->f_pos);
set_fs(old_fs);
return 0;
}
static void __exit exit(void)
{
if(file != null)
filp_close(file, null); oracle
}
module_init(init);
module_exit(exit);
module_license(gpl);
注意:
1.一个是要记得编译的时候加上-d__kernel_syscalls__

孙正义辞去软银公司董事长,CEO 宫内谦继任
差分信号PCB布局布线时的几个常见误区
iphone3.12无法定位故障解决
供电和数据线缆架构会不会终结PCB时代
对于可穿戴设备而言,传感器的重要性有多大
需要了解的Linux内核读写文件
电视还是DLP?康佳投影仪等LCD品牌实现“弯道超车”!
CRC原理
未来十年将是人工智能占据主导地位的十年
曾经风靡的存储卡现如今为什么会无人使用
2024年全球AI服务器或超160万台,AI PC有望2025年普及
提升发电效能 嵌入式让太阳能追日系统更完善
三星S8能否救场 外媒体验过后这样说
Vishay新型电池分流器上市,精度更高,RTC性能更优
分享一个音频控制电源开关电路
统领4.5万机器人大军的贝索斯又押宝家庭机器人,试图扩展更多的场景
基于STM32+CS创世 SD NAND(贴片SD卡)完成FATFS文件系统移植与测试(下篇)
解决路由器DNS劫持的两种方法
VC5402与外部存储器的接口设计
Flash页、扇区、块的区别