关于用户态的文件操作函数我们知道有open、read、write这些。但是这些的实现都是依赖于库的实现,但是在内核态是没有库函数可用的。最近做测试,在内核态中,需要学习一下在内核态里面的文件操作函数。分为三对出现。
感谢前辈的优秀文章,参考链接在文末
这些的函数就在linux/fs.h和asm/uaccess.h中存在。
struct file* filp_open(const char* filename, int open_mode, int mode);
第一个参数表明要打开或创建文件的名称(包括路径部分)。第二个参数文件的打开方式,其取值与标准库中的open相应参数类似,可以取O_CREAT,O_RDWR,O_RDONLY等。第三个参数创建文件时使用,设置创建文件的读写权限,其它情况可以设为0
int filp_close(struct file *filp, fl_owner_t id);
第一个参数是filp_open返回的file结构体指针第二个参数基本上都是NULL
//读文件
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);
filp:文件指针,由filp_open()函数返回。buffer:缓冲区,从文件中读出的数据放到这个缓冲区,向文件中写入数据也在这个缓冲区。len:从文件中读出或者写入文件的数据长度。pos:为文件指针的位置,即从什么地方开始对文件数据进行操作。
在buffer前面都有一个__user修饰符,这要求buffer指针应该指向用户的空间地址。如果在内核中使用上述的两个函数直接进行文件操作,将内核空间的指针传入的时候,函数会返回失败EFAULT。但在Linux内核中,一般不容易生成用户空间的指针,或者不方便独立使用用户空间内存。为了使这两个函数能够正常工作,必须使得这两个函数能够处理内核空间的地址。
使用set_fs()函数可以指定上述两个函数对缓冲区地址的处理方式,原型如下:
内核空间文件续写的框架为:
mm_segmen_t old_fs;
old_fs = get_fs();
set_fs(KERNEL_DS);
...
set_fs(old_fs)
**注意:**使用vfs_read()和vfs_write()的时候,要注意最后的参数loff_t *pos,pos所指向的值必须要进行初始化,表明从文件的什么位置进行读写。使用此参数可以对续写文件的位置进行设定,这可以完成用户空间中lseek()函数的功能。
下面是一个使用内核空间的文件读函数从文件中读取数据的例子:
ssize_t ReadFile(struct file *filp, char __user *buffer, size_t len, loff_t *pos)
{ssize_t count = 0;oldfs = get_fs();set_fs(KERNEL_DS);count = file->f_op->read(filp, buf, len, &file->f_pos);set_fs(oldfs);return count;
}
源码
//kernel
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//user#define DBGPRINT printk//内核程序使用file_open来打开文件
struct file *SIPFW_OpenFile(const char *filename, int flags, int mode)
{struct file *f = NULL;DBGPRINT("==>SIPFW_OpenFile\n");f = filp_open(filename, flags, 0);if (!f || IS_ERR(f)){f = NULL;}DBGPRINT("<==SIPFW_OpenFile\n");return f;
}ssize_t SIPFW_ReadLine(struct file *f, char *buf, size_t len)
{
#define EOF (-1)ssize_t count = -1;mm_segment_t oldfs;struct inode *inode;//DBGPRINT("==>SIPFW_ReadLine\n");if (!f || IS_ERR(f) || !buf || len <= 0) {goto out_error;}if (!f || !f->f_inode){goto out_error;}inode = f->f_inode;if (!(f->f_mode & FMODE_READ)){goto out_error;}if (f->f_op /*&& f->f_op->read*/) {oldfs = get_fs();set_fs(KERNEL_DS);count = 0;if (vfs_read(f, buf, 1, &f->f_pos) <= 0){DBGPRINT("file read failure\n");goto out;}if (*buf == EOF){DBGPRINT("file EOF\n");goto out;}count = 1;while (*buf != EOF && *buf != '\0' && *buf != '\n' && *buf != '\r' && count < len && f->f_pos <= inode->i_size){buf += 1;count += 1;if (vfs_read(f, buf, 1, &f->f_pos) <= 0) {count -= 1;break;}}} else{DBGPRINT("goto out_error\n");goto out_error;}if (*buf == '\r'|| *buf =='\n' ||*buf == EOF ) {*buf = '\0';count -= 1;} else{buf += 1;*buf = '\0';}out:set_fs(oldfs);
out_error://DBGPRINT("<==SIPFW_ReadLine %d\n", count);return count;
}// ssize_t SIPFW_WriteLine(struct file *f, char *buf, size_t len)
// {
// ssize_t count = -1;
// mm_segment_t oldfs;
// struct inode *inode;
// DBGPRINT("==>SIPFW_WriteLine\n");// if (!f || IS_ERR(f) || !buf || len <= 0)
// {
// goto out_error;
// }// if (!f || !f->f_dentry || !f->f_dentry->d_inode)
// {
// goto out_error;
// }// inode = f->f_dentry->d_inode;// if (!(f->f_mode & FMODE_WRITE) || !(f->f_mode & FMODE_READ) )
// {
// goto out_error;
// }// if (f->f_op && f->f_op->read && f->f_op->write)
// {
// //f->f_pos = f->f_count;
// oldfs = get_fs();
// set_fs(KERNEL_DS);
// count = 0;// count = f->f_op->write(f, buf, len, &f->f_pos) ;// if (count == -1)
// {
// goto out;
// }
// }
// else
// {
// goto out_error;
// }// out:
// set_fs(oldfs);
// out_error:
// DBGPRINT("<==SIPFW_WriteLine\n");
// return count;
// }void SIPFW_CloseFile(struct file *f)
{DBGPRINT("==>SIPFW_CloseFile\n");if(!f)return;filp_close(f, current->files);DBGPRINT("<==SIPFW_CloseFile\n");
}int SIPFW_HandleConf(void)
{int retval = 0,count;int l = 0;char *pos = NULL;struct file *f = NULL;char line[256] = { 0 };DBGPRINT("==>SIPFW_HandleConf\n");// 提前建一个/etc/sipfw.conf文件吧f = SIPFW_OpenFile("/etc/sipfw.conf", O_RDWR, 0);if(f == NULL){retval = -1;DBGPRINT("SIPFW_OpenFile called failure\n");goto EXITSIPFW_HandleConf;}while((count = SIPFW_ReadLine(f, line, 256)) > 0){pos = line;DBGPRINT("line = %d, data: %s\n", l, line);\l++;memset(line, 0, sizeof(line));}SIPFW_CloseFile(f); EXITSIPFW_HandleConf:DBGPRINT("<==SIPFW_HandleConf\n");return retval;
}static int __init SIPFW_Init(void)
{int ret = -1;DBGPRINT("==>SIPFW_Init\n");ret = SIPFW_HandleConf();DBGPRINT("<==SIPFW_Init\n");return ret;
}static void __exit SIPFW_Exit(void)
{DBGPRINT("==>SIPFW_Exit\n");DBGPRINT("<==SIPFW_Exit\n");
}module_init(SIPFW_Init);
module_exit(SIPFW_Exit);
MODULE_LICENSE("GPL/BSD");
Makefile:
MODULE_NAME :=main_file
obj-m :=$(MODULE_NAME).oKERNELDIR = /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)all:$(MAKE) -C $(KERNELDIR) M=$(PWD) modulesclean:$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
测试中读取的文件:(/etc/sipfw.conf)

运行效果:
https://www.freesion.com/images/776/0b01da97ef33a4353bbbd347575fd938.png
参考链接:
https://blog.csdn.net/weixin_42343585/article/details/81205246
https://www.freesion.com/article/4416178909/