引言
总所周知,用户态进程读写任何类型的文件系统都可以使用read和write着两个系统调用,但是在linux内核中没有这样的系统调用我们如何操作文件呢?我们知道read和write在进入内核态之后,实际执行的是sys_read和sys_write,但是查看内核源代码,发现这些操作文件的函数都没有导出(使用EXPORT_SYMBOL导出),也就是说在内核模块中是不能使用的。
接下来将在本文实现 Linux 内核态的文件读写操作,网上的很多教程都是过时的,尤其是对于 Linux 内核版本在 4.14 之后,已经不再支持 vfs_read 和 vfs_write 函数了,但网上大多数文章和示例都仍在使用,却并未做出任何说明。
相关函数介绍
/**
* 在内核中打开的文件时需要注意打开的时机,
* 很容易出现需要打开文件的驱动很早就加载并打开文件,但需要打开的文件所在设备还不有挂载到文件系统中,而导致打开失败。
*
* @param filename 表明要打开或创建文件的名称(包括路径部分)。
* @param mode 文件的打开方式,其取值与标准库中的open相应参数类似,可以取O_CREAT,O_RDWR,O_RDONLY等。
* @param flags 创建文件时使用,设置创建文件的读写权限,其它情况可以匆略设为0
* @return 返回strcut file*结构指针,供后继函数操作使用
*/
strcut file* filp_open(const char* filename, int mode, int flags);
/**
* 关闭文件
*
* @param filp strcut file*结构指针
* @param id 传 NULL 即可
*/
int filp_close(struct file*filp, fl_owner_t id);
/**
*
* @param filp strcut file*结构指针
* @param buffer 指向用户空间的缓冲区
* @param len 数据长度
* @param pos 读写起始位置
* @return 读取字节数
*/
ssize_t vfs_read(struct file* filp, char __user* buffer, size_t len, loff_t* pos);
/**
*
* @param filp strcut file*结构指针
* @param buffer 指向用户空间的缓冲区
* @param len 数据长度
* @param pos 读写起始位置
* @return 写入字节数
*/
ssize_t vfs_write(struct file* filp, const char __user* buffer, size_t len, loff_t* pos);
// Linux 4.14 及其之后的版本使用,参数含义同 vfs_read
ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos);
// Linux 4.14 及其之后的版本使用,参数含义同 vfs_write
ssize_t kernel_write(struct file *file, const void *buf, size_t count, loff_t *pos);
实现代码
相关头文件
#include <linux/fs.h>
#include <linux/buffer_head.h>
#include <asm/segment.h>
#include <asm/uaccess.h>
打开文件
struct file *file_open(const char *path, int mode, int flags)
{
struct file *filp = NULL;
mm_segment_t oldfs;
int err = 0;
oldfs = get_fs();
set_fs(get_ds());
filp = filp_open(path, mode, flags);
set_fs(oldfs);
if (IS_ERR(filp)) {
err = PTR_ERR(filp);
return NULL;
}
return filp;
}
关闭文件
void file_close(struct file *file)
{
filp_close(file, NULL);
}
读取文件,Linux 内核版本4.14及其之后
int file_read(struct file *file, unsigned long long offset, unsigned char *data, unsigned int size)
{
mm_segment_t oldfs;
int ret;
oldfs = get_fs();
set_fs(get_ds());
ret = kernel_read(file, data, size, &offset);
set_fs(oldfs);
return ret;
}
写入文件,Linux 内核版本4.14及其之后
int file_write(struct file *file, unsigned long long offset, unsigned char *data, unsigned int size)
{
mm_segment_t oldfs;
int ret;
oldfs = get_fs();
set_fs(get_ds());
ret = kernel_write(file, data, size, &offset);
set_fs(oldfs);
return ret;
}
读取文件,Linux 内核版本4.14之前
int file_read(struct file *file, unsigned long long offset, unsigned char *data, unsigned int size)
{
mm_segment_t oldfs;
int ret;
oldfs = get_fs();
set_fs(get_ds());
ret = vfs_read(file, data, size, &offset);
set_fs(oldfs);
return ret;
}
写入文件,Linux 内核版本4.14之前
int file_write(struct file *file, unsigned long long offset, unsigned char *data, unsigned int size)
{
mm_segment_t oldfs;
int ret;
oldfs = get_fs();
set_fs(get_ds());
ret = vfs_write(file, data, size, &offset);
set_fs(oldfs);
return ret;
}