const struct file_operations ext2_file_operations = {
.llseek = generic_file_llseek,
.read = do_sync_read,
.write = do_sync_write,
.aio_read = generic_file_aio_read,
.aio_write = generic_file_aio_write,
.unlocked_ioctl = ext2_ioctl,
.mmap = generic_file_mmap,
.open = generic_file_open,
.release = ext2_release_file,
.fsync = ext2_sync_file,
.splice_read = generic_file_splice_read,
.splice_write = generic_file_splice_write,
};
======================================
loff_t generic_file_llseek(struct file *file, loff_t offset, int origin)
{
loff_t rval;
mutex_lock(&file->f_dentry->d_inode->i_mutex);
rval = generic_file_llseek_unlocked(file, offset, origin);
mutex_unlock(&file->f_dentry->d_inode->i_mutex);
return rval;
}
这个函数,只用于普通的文件,然后调用generic_file_llseek_unlocked函数,来更新文件的偏移指针。通过指定参数offset和origin。
loff_t generic_file_llseek_unlocked(struct file *file, loff_t offset, int origin)
{
01 struct inode *inode = file->f_mapping->host;
02 switch (origin) {
03 case SEEK_END:
04 offset += inode->i_size;
05 break;
06 case SEEK_CUR:
07 if (offset == 0)
08 return file->f_pos;
09 offset += file->f_pos;
10 break;
11 }
12 if (offset < 0 || offset > inode->i_sb->s_maxbytes)
13 return -EINVAL;
14 if (offset != file->f_pos) {
15 file->f_pos = offset;
16 file->f_version = 0;
17 }
18 return offset;
19}
根据origin参数的值的,是文件的开始,或结尾,然后指针指向相应的位置。
=======================================
ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
{
01 struct iovec iov = { .iov_base = buf, .iov_len = len };
02 struct kiocb kiocb;
03 ssize_t ret;
05 init_sync_kiocb(&kiocb, filp);
06 kiocb.ki_pos = *ppos;
07 kiocb.ki_left = len;
09 for (;;) {
10 ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos);
11 if (ret != -EIOCBRETRY)
12 break;
13 wait_on_retry_sync_kiocb(&kiocb);
14 }
15
16 if (-EIOCBQUEUED == ret)
17 ret = wait_on_sync_kiocb(&kiocb);
18 *ppos = kiocb.ki_pos;
19 return ret;
20}
=======================================
ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{
01 struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len };
02 struct kiocb kiocb;
03 ssize_t ret;
04
05 init_sync_kiocb(&kiocb, filp);
06 kiocb.ki_pos = *ppos;
07 kiocb.ki_left = len;
08
09 for (;;) {
10 ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos);
11 if (ret != -EIOCBRETRY)
12 break;
13 wait_on_retry_sync_kiocb(&kiocb);
14 }
15
16 if (-EIOCBQUEUED == ret)
17 ret = wait_on_sync_kiocb(&kiocb);
18 *ppos = kiocb.ki_pos;
19 return ret;
20}
第1行iovec局部变量,包含用户态缓冲区的地址和长度。
第2行kiocb描述符用来跟踪正在运行的同步和异步I/O操作的完成状态。
第5行调用宏init_sync_kiocb来初始化描述符kiocb,并设置一个同步操作对象的有关字段。主要设置ki_key字段和ki_filp字段及ki_obj字段。
第10行这个函数指针会调用generic_file_aio_read()函数将刚填完的iovec和kiocb描述符地址传给它。后面这个函数返回一个值,这个值通常就是从文件有效读入的字节数。
第13行等待,测试&(iocb)->ki_flags标识位,如果为假,则进程切换。然后设置进程为运行状态。
第19行返回。
=======================================
ssize_t generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos)
{
01 struct file *filp = iocb->ki_filp;
02 ssize_t retval;
03 unsigned long seg;
04 size_t count;
05 loff_t *ppos = &iocb->ki_pos;
07 count = 0;
08 retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE);
09 if (retval)
10 return retval;
13 if (filp->f_flags & O_DIRECT) {
14 loff_t size;
15 struct address_space *mapping;
16 struct inode *inode;
18 mapping = filp->f_mapping;
19 inode = mapping->host;
20 if (!count)
21 goto out; /* skip atime */
22 size = i_size_read(inode);
23 if (pos < size) {
24 retval = filemap_write_and_wait_range(mapping, pos,
25 pos + iov_length(iov, nr_segs) - 1);
26 if (!retval) {
27 retval = mapping->a_ops->direct_IO(READ, iocb,iov, pos, nr_segs);
29 }
30 if (retval > 0)
31 *ppos = pos + retval;
32 if (retval) {
33 file_accessed(filp);
34 goto out;
35 }
36 }
37 }
39 for (seg = 0; seg < nr_segs; seg++) {
40 read_descriptor_t desc;
42 desc.written = 0;
43 desc.arg.buf = iov[seg].iov_base;
44 desc.count = iov[seg].iov_len;
45 if (desc.count == 0)
46 continue;
47 desc.error = 0;
48 do_generic_file_read(filp, ppos, &desc, file_read_actor);
49 retval += desc.written;
50 if (desc.error) {
51 retval = retval ?: desc.error;
52 break;
53 }
54 if (desc.count > 0)
55 break;
56 }
57out:
58 return retval;
59}
第8行使用generic_segment_checks执行必要的检查。调整一些段和字节的写。
第13行判断是否为直接读。
第24行等待文件的范围
第27行直接读取
第39-55行循环读取段,调用do_generic_file_read函数,通过传递给它对象指针,文件偏移,刚分配的读操作符的地址和函数file_read_actor函数的地址,来进行读取。
=======================================
ssize_t generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
01 struct file *file = iocb->ki_filp;
02 struct address_space *mapping = file->f_mapping;
03 struct inode *inode = mapping->host;
04 ssize_t ret;
05 BUG_ON(iocb->ki_pos != pos);
06 mutex_lock(&inode->i_mutex);
07 ret = __generic_file_aio_write_nolock(iocb, iov, nr_segs,
08 &iocb->ki_pos);
09 mutex_unlock(&inode->i_mutex);
10 if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
11 ssize_t err;
12 err = sync_page_range(inode, mapping, pos, ret);
13 if (err < 0)
14 ret = err;
15 }
16 return ret;
17}
写函数和读函数有点类似的。
第6行调用__generic_file_aio_write_nolock将涉及的页标记为脏,并传递相应的参数,iovec和kiocb类型的局部变量地址、用户态缓冲区的段数和ppos.
第12行函数sync_page_range()用来强制内核将页高速缓存中的的页刷新,阻塞当前进程直到I/O数据传输结束。
=======================================
EXT2的文件操作方法
最新推荐文章于 2022-09-09 10:53:46 发布