目录
本专栏文章将有70篇左右,欢迎+关注,查看后续文章。
8.5 标准函数
大多数文件系统中 file_operations 的 read,write 分别为:
do_sync_read,do_sync_write。
如:
const struct file_operations ext4_file_operations = {
.read = do_sync_read,
.write = do_sync_write,
...
}
inode 是文件属性,而不是数据本身,数据存在块设备中。
内存保存块设备数据的方式:
1. mmap映射:
即映射到用户虚拟地址空间。
特点:适用大文件,不预加载,访问时加载,不浪费内存。
2. 页缓存:
缓存到内核内存中。
特点:
预加载,加速读写,进程通过标准文件读写操作。
适用小文件,不立即写回块设备。
所以读写文件时,需先查询数据是否缓存在内存?
若在缓存,则读写页缓存,否则向块设备发出读请求。
拓展
fd = open("myfile", O_RDWR | O_CREAT | O_SYNC | O_DIRECT, S_IRUSR | S_IWUSR);
O_DIRECT:不使用内核缓冲区,直接读写磁盘文件。
O_SYNC:以同步IO方式,打开文件。
O_DIRECT:
含义:
用户空间直接读磁盘文件,绕过页高速缓存。也称裸IO(RAW IO)。
应用场景:
性能要求极高的场景,如数据库系统(需快速访问大量数据)。
注意:
1. 一个进程以O_DIRECT访问文件,而另一进程以页缓存打开同一文件,两个进程读写数据可能不一致。
2. 某些文件系统可能不支持O_DIRECT。如某些网络文件系统。
3. 通常结合O_SYNC或O_DSYNC,以确保数据的完整性和一致性。
O_SYNC:
含义:
写操作后,数据不仅写到内核缓冲区,还要同步刷新到磁盘。
好处:
实现数据同步。
O_SYNC 和 O_DSYNC 区别:
O_DSYNC 不要求元数据的同步。
O_SYNC 要求数据和元数据都要同步。
8.5.1 通用读取例程
struct file_operations ext4_file_operations = {
.read = do_sync_read,
// 同步读,将阻塞调用进程。
.aio_read = generic_file_aio_read,
// 异步读,提交请求后,立即返回,不必等待数据读取完成。
};
ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
{
struct iovec iov = { .iov_base = buf, .iov_len = len };
struct kiocb kiocb;
init_sync_kiocb( &kiocb, filp );
kiocb.ki_pos = *ppos;
kiocb.ki_left = len;
kiocb.ki_nbytes = len;
ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos);
//aio_read 指向 generic_file_aio_read()
if (-EIOCBQUEUED == ret) 若读请求正在排队
ret = wait_on_sync_kiocb(&kiocb); //阻塞,等待读完成。
*ppos = kiocb.ki_pos;
return ret;
}
1. 异步读取
内核异步读函数如下:
2. 从映射中读取
do_generic_mapping_read():已被淘汰。
原理如下:
操作流程:
1. 检查文件内容是否在页缓存中。
struct page *find_get_page(struct address_space *mapping, pgoff_t offset)
使用方法:
page = find_get_page(filp->i_mapping, index);
作用:
检查某文件的指定偏移是否被缓存到内存页上。
2. 若没有缓存,则预读并缓存。
page_cache_sync_readahead()
3. 用 Page_Uptodate 检查,页缓存数据是否是最新的。
若不是最新,调用mapping->a_ops_readpage:从块设备读数据到页。
4. 若页是最新,调用 mark_page_accessed。
void mark_page_accessed(struct page *page);
作用:
更新页访问的时间戳,选择换出的页时使用该标记。
注意:
匿名页不需要更新访问时间戳。
5. 将页映射到用户地址空间
即actor函数指针。
PageAnon():
作用:检查页面是否是匿名页面。
PageReferenced()
作用:检查是否已被访问过。
预读机制:
8.5.2 失效机制
filemap_fault:
作用:
读取未保存在缓存中的页。
最终也调用:
struct address_space mapping -> a_ops -> readpage();
8.5.3 权限检查
int inode_permission(struct inode *inode, int mask)
作用:
检查是否允许以指定权限访问inode。
使用场景:
chdir/chroot系统调用。
int inode_permission(struct inode *inode, int mask)
{
sb_permission(inode->i_sb, inode, mask);
__inode_permission(inode, mask);
}
mask:
即检查是否可以访问的权限。
如MAY_READ,MAY_WRITE,MAY_EXEC。
1. sb_permission:
若mask = MAY_WRITE,根据 sb->s_flags 确认不是只读文件系统。
2. __inode_permission:
通常调用:generic_permission(inode, mask);
包括 check_acl()
struct inode_operations sysfs_inode_operations = {
.permission = sysfs_permission,
//也调用 generic_permission(inode, mask);
};
sysfs_permission -> generic_permission -> check_acl
检查用户,组,other的 inode 权限。