内核空间读写文件的常规操作步骤同用户空间一样
第一步:打开文件,获取文件指针
第二步:将文件读入到一段内存中
第三步:将一段内存中的数据写入到另一个文件中。
完成上述功能要用的内核函数有:
◆打开文件filp_open()
◆关闭文件filp_close()
◆读文件内容到内存中vfs_read()
◆写内存中的数据到文件vfs_write()
函数功能:在内核空间中打开文件
函数原形:
strcut file* filp_open(const char* filename, int open_mode, int mode);
返回值:strcut file*结构指针,供后继函数操作使用,该返回值用IS_ERR()来检验其有效性。
参数:
filename:表明要打开或创建文件的名称(包括路径部分)。
open_mode:文件的打开方式,O_RDONLY 只读打开、O_WRONLY 只写打开、O_RDWR 读写打开、O_CREAT 文件不存在则创建。
mode:创建文件时使用,设置创建文件的权限,其它情况可以匆略设为0
示例
struct file *file = NULL; file = filp_open(/root/test.txt,O_RDWR|O_CREAT,0); //以读写方式(没有则创建)打开文件/root/test.txt。并返回test.txt的文件指针给file.
函数功能:关闭之前打开文件
函数原型:int filp_close(struct file*filp, fl_owner_t id);
参数:
struct file*filp:打开文件的文件指针
fl_owner_t id:一般传递NULL值,也可用current->files作为实参。
示例
filp_close(file, NULL); //关闭指针为file的文件。
函数功能:读取已经打开的文件到内存中
函数原型:
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { ssize_t ret; if (!(file->f_mode & FMODE_READ)) //判断文件是否可读 return -EBADF; if (!file->f_op || (!file->f_op->read && !file->f_op->aio_read)) //是否定义文件读方法 return -EINVAL; if (unlikely(!access_ok(VERIFY_WRITE, buf, count))) return -EFAULT; ret = rw_verify_area(READ, file, pos, count); //读校验 , if (ret >= 0) { count = ret; if (file->f_op->read) ret = file->f_op->read(file, buf, count, pos); //调用文件读操作方法 else ret = do_sync_read(file, buf, count, pos); //通用文件模型读方法 if (ret > 0) { fsnotify_access(file->f_path.dentry); add_rchar(current, ret); } inc_syscr(current); } return ret; }
通过filp_open我们已经可以在当前进程的文件描述表中找到了file , 于是我们就可以调用保存在file中的文件操作方法(file_operation) file->f_op->read(file, buf, count, pos)来具体的操作文件。
上面的代码实现并不复杂,在做了一些条件判断以后,如果该文件索引节点inode定义了文件的读实现方法的话,就调用此方法。Linux下特殊文件读往往是用此方法, 一些伪文件系统如:proc,sysfs等,读写文件也是用此方法。而如果没有定义此方法就会调用通用文件模型的读写方法.它最终就是读内存,或者需要从存储介质中去读数据.
参数:
struct file *file:打开的文件返回的文件指针,(读的目标文件)
char __user *buf:在用户空间开辟的一段内存空间的首地址,用来保存文件数据。
size_t count:指定读取文件中的多少内容。单位字节
loff_t *pos:文件起始位置偏移值,若从文件头读取,则偏移值为0.可以在文件自身的信息中获取
示例
int *buf; loff_t *pos = &(file->f_pos); buf = (int *)kmalloc(fsize+100,GFP_KERNEL); //分配一个文件自身大小+100字节边界的内存空间,将用来存放打开的文件,内存分配方式为kmalloc的flag标志GFP_KERNEL。 vfs_read(file, buf, fsize, pos); //读文件(指针为file)到内存(buf为起始地址)中,读取字节数定为文件自身大小,偏移为自身.
函数功能:将内存中的一段数据写到文件中
函数原形:
ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { ssize_t ret; if (!(file->f_mode & FMODE_WRITE)) return -EBADF; if (!file->f_op || (!file->f_op->write && !file->f_op->aio_write)) return -EINVAL; if (unlikely(!access_ok(VERIFY_READ, buf, count))) return -EFAULT; ret = rw_verify_area(WRITE, file, pos, count); if (ret >= 0) { count = ret; if (file->f_op->write) ret = file->f_op->write(file, buf, count, pos); else ret = do_sync_write(file, buf, count, pos); if (ret > 0) { fsnotify_modify(file->f_path.dentry); add_wchar(current, ret); } inc_syscw(current); } return ret; }
可以看出这个函数和vfs_read()是差不多的,只是调用的文件操作方法不同而已(file->f_op->write) ,如果没有定义file->f_op->write ,同样也需要do_sync_write()调用同样文件写操作, 首先把数据写到内存中,然后在适当的时候把数据同步到具体的存储介质中去.
参数:
struct file *file:打开的文件返回的文件指针,(写的目标文件)
char __user *buf:数据在内存中的位置,以该地址为起始的一段内存数据将要写到文件中
size_t count:指定写入文件中的多少内容。单位字节
loff_t *pos:文件起始位置偏移值,若从文件头读取,则偏移值为0.可以在文件自身的信息中获取
示例
loff_t *pos = &(file->f_pos); vfs_write(file,buf,fsize,pos);
我们可以利用文件的inode结构获得文件的大小,参考代码如下
struct file *file = NULL; struct inode *inode = NULL; file = filp_open(file_path,O_RDWR|O_CREAT,0); inode = file->f_dentry->d_inode; fsize = inode->i_size; printk(KERN_ALERT "size=%d/n",(int)fsize);
此ko模块代码在arm架构的fpga上已经跑通。因为涉及的参数比较多,为了清楚地重现重要步骤,对每步骤的函数进行了简单的封装。参数的传递只要理解上面的介绍即可区分清楚。执行流程在static int hello_init(void)函数中
/* * kernel_hello_file.c * * Created on: 2010-11-9 * Author: Wang BaoYi(zats) * Email:[email protected] */ #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/syscalls.h> #include <asm/unistd.h> #include <asm/uaccess.h> #define FILE_PATH_READ "/file_read_test" //打开文件路径(包括文件名),未来将要读的 #define FILE_PATH_WRITE "/new_file_test" //打开文件路径(包括文件名),未来将要写的 struct file *file = NULL; //保存打开文件的文件指针变量 struct inode *inode = NULL; //为了获取文件大小用的inode结构变量 int *file_buf; //保存开辟的内存空间的地址的指针变量 loff_t fsize; //保存文件大小的变量 mm_segment_t old_fs; //保存内存边界的变量 /* * kernel_file_open封装了文件打开函数 * 参数为文件路径(包含文件名)。 * 操作file类型结构变量。 * 打开方式为读写(没有则创建) */ static int kernel_file_open(char *file_path) { file = filp_open(file_path,O_RDWR|O_CREAT,0); if (IS_ERR(file)) { printk("Open file %s failed./n", file_path); return 0; } } /* * kernel_file_size封装了获取文件大小函数 * 参数为待获取大小的文件指针。 * 操作inode类型结构变量。 * 返回值为文件大小,单位字节 */ static loff_t kernel_file_size(struct file *file) { inode = file->f_dentry->d_inode; fsize = inode->i_size; printk(KERN_ALERT "size=%d/n",(int)fsize); return fsize; } /* * kernel_addr_limit_expend封装了内存边界扩展函数 * 参数无。 */ static int kernel_addr_limit_expend(void) { old_fs = get_fs(); set_fs(KERNEL_DS); return 0; } /* * kernel_addr_limit_resume封装了内存边界恢复函数 * 参数无。 */ static int kernel_addr_limit_resume(void) { set_fs(old_fs); } /* * kernel_file_read封装了读文件函数 * 参数为open的文件指针,获取的文件大小 * 返回值为读入到内存中的首地址。 */ void *kernel_file_read(struct file *file,loff_t fsize) { int *buf; loff_t *pos = &(file->f_pos); buf = (int *)kmalloc(fsize+100,GFP_KERNEL); vfs_read(file, buf, fsize, pos); return buf; } /* * kernel_file_ write封装了读文件函数 * 参数为open的文件指针,数据在内存中的地址,写入到文件的字节数 */ static int kernel_file_write(struct file *file,int *buf,loff_t fsize) { loff_t *pos = &(file->f_pos); vfs_write(file,buf,fsize,pos); } /* * ko的主函数 */ static int hello_init(void) //ko的主函数 { printk(KERN_ALERT "Y(^_^)Y Hello Wang`s file./n"); kernel_file_open(FILE_PATH_READ); //打开文件file_read_test kernel_file_size(file); //获取file_read_test的大小 /*read file to mem*/ kernel_addr_limit_expend(); //边界扩展 file_buf = kernel_file_read(file,fsize); //读操作 filp_close(file, NULL); //关闭文件file_read_test kernel_addr_limit_resume(); //边界恢复 /*write mem to file*/ kernel_file_open(FILE_PATH_WRITE); //打开文件new_file_test,没有则创建 kernel_addr_limit_expend(); //边界扩展 kernel_file_write(file,file_buf,fsize); //将前面读到内存中的数据,写入到文件new_file_test中 filp_close(file, NULL); //关闭文件 kernel_addr_limit_resume(); //边界恢复 return 0; } static void hello_exit(void) { printk(KERN_ALERT "BYE BYE file Y(^_^)Y/n"); } module_init(hello_init); module_exit(hello_exit); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("wby"); MODULE_DESCRIPTION("A simple hello world Module with File");