一、文件打开函数
struct file *filp_open(const char *filename, int flags, umode_t mode)
定义在:fs/open.c
头文件:
详解:
1.在用户态open函数是个变参函数,umode_t mode参数只有在O_CREAT标志指定时才出现。在内核态如果O_CREAT标志指定mode参数指定权限,否则为0
2.filename参数和flags和用户态一样,指定文件路径和打开标志
3.返回值,如果成功返回struct file 类型变量。失败时要注意,它失败了返回的不是NULL,而是一个错误码。所以对于返回值的判断应该用下面的方式
if(IS_ERR(file))
IS_ERR(file)为真打开失败,为假打开成功。linux内核为什么这么设计,可以参考https://blog.csdn.net/yaozhenguo2006/article/details/7967547
二、读写函数
用户态读写文件只提供了一种方法read、write。而在内核态读写文件可以有好几种,接下来分别看看这几张方法,并对其进行一下比较
方法1:当打开文件成功后,返回了struct file类型变量,此变量中包含了struct file_operations文件操作函数集所以可以如下操作
file->f_op->read(file,buf,size,&pos);
file->f_op->write(file,buf,size,&pos);
方法二:使用vfs_read、vfs_write函数,他是内核对file->f_op->read、write的封装。定义在fs/read_write.c,头文件
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);
注意:以上两种方法中,函数对buf参数进行了__user限定,内核会对buf地址进行检测。也就是说buf的地址必须是用户空间的地址,如果在内核中使用会返回-EFAULT错误。所以在调用以上两种方法中的函数时可以使用set_fs()、get_fs()来解决。
使用方法:
mm_segmet_t old_fs;
.........
old_fs = get_fs();
set_fs(get_ds());
vfs_read(file,buf,size,&pos);
set_fs(old_fs);
进行了set_fs设置后,内核就不在对内存地址进行检测。关于set_fs()可以参考:https://www.cnblogs.com/soul-stone/p/6367696.html
方法三:先说读函数,可以使用kernel_read函数,这个函数其实就是加入了set_fs()、get_fs()函数。在4.14前
定义为int kernel_read(struct file *file, loff_t offset, char *addr, unsigned long conut),定义在fs/exec.c中,头文件
在4.14及之后的版本中定义为ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos),定义在fs/read_write.c中,头文件
接下来再说写函数,__kernel_write和kernel_write函数,在3.9之前没有这两个函数,3.9版本中定义了__kernel_write函数,但是没有导出符合,模块编程中也调用不了,我们可以仿照此函数编写一个自己的kern_write函数。在3.18版本后__kernel_write函数导出了。其定义:ssize_t __kernle_write(struct file *file, const char *buf, size_t count, loff_t *pos),定义在fs/read_write.c中,头文件
三、文件关闭
int filp_close(struct file *file, fl_owner_t id)。定义在fs/open.c,头文件
用法:一般为 filp_close(file,NULL);