【发布时间】:2017-06-13 21:26:50
【问题描述】:
我查看了 linux 内核 4.4.0-57-generic 的内核源代码,但在 writev() 源代码中没有看到任何锁。有什么我想念的吗?我看不出writev() 是原子的还是线程安全的。
【问题讨论】:
标签: linux linux-kernel kernel system-calls
我查看了 linux 内核 4.4.0-57-generic 的内核源代码,但在 writev() 源代码中没有看到任何锁。有什么我想念的吗?我看不出writev() 是原子的还是线程安全的。
【问题讨论】:
标签: linux linux-kernel kernel system-calls
这里不是内核专家,但无论如何我都会分享我的观点。随时发现任何错误。
浏览内核(v4.9 虽然我不希望它如此不同),并尝试跟踪 writev(2) 系统调用,我可以观察到创建以下路径的后续函数调用:
现在路径分支,取决于是否实现了write_iter 方法并挂钩到系统调用所指的struct file 的struct file_operations 字段。
NULL,则路径为:5a。 do_iter_readv_writev(..),调用方法filp->f_op->write_iter(..)at this point。
NULL,则路径为:5b。 do_loop_readv_writev(..),在循环中反复调用方法filp->f_op->writeat this point。
所以,据我了解,writev() 系统调用与底层write()(或write_iter())一样是线程安全的,当然可以通过多种方式实现,例如在设备驱动程序中,并且可能会或可能不会根据其需要和设计使用锁。
编辑:
在内核 v4.4 中,路径看起来非常相似:
然后取决于struct file的struct file_operations中作为字段的write_iter方法是否为NULL,就像上面描述的v4.9中的情况一样。
【讨论】:
filp->f_op->write 下面的 write 方法来获得原子性似乎很奇怪:*readv() 和 writev() 执行的数据传输是原子的: 数据由 writev() 写入的块被写入单个块,不会与其他进程中写入的输出混合(但请参阅 pipe(7) 以了解异常);类似地, readv() 保证从文件中读取连续的数据块,而不管在其他线程或具有文件描述符的进程中执行的读取操作如何。 . .
do_iter_readv_writev() 和方法write_iter 时如何实现原子性是显而易见的,因为后者可以正确获取必要的锁以原子地执行数据传输。然而,在write_iter 根本没有实现的情况下,因此writev(2) 基本上依赖于手动重复调用的write 方法,在一个循环中,老实说,我不知道它怎么能是原子的。我最好的猜测是,在这种情况下,write_iter 方法 必须 实现以实现原子性。 ...
VFS(虚拟文件系统)本身不保证writev() 调用的 原子性。它只是调用struct file_operations 的文件系统特定的.write_iter 方法。
make 方法原子地写入文件是特定文件系统实现的责任。
例如在ext4文件系统函数ext4_file_write_iter使用
mutex_lock(&inode->i_mutex);
让写作原子化。
【讨论】:
在 fs.h 中找到它:
static inline void file_start_write(struct file *file)
{
if (!S_ISREG(file_inode(file)->i_mode))
return;
__sb_start_write(file_inode(file)->i_sb, SB_FREEZE_WRITE, true);
}
然后在 super.c 中:
/*
* This is an internal function, please use sb_start_{write,pagefault,intwrite}
* instead.
*/
int __sb_start_write(struct super_block *sb, int level, bool wait)
{
bool force_trylock = false;
int ret = 1;
#ifdef CONFIG_LOCKDEP
/*
* We want lockdep to tell us about possible deadlocks with freezing
* but it's it bit tricky to properly instrument it. Getting a freeze
* protection works as getting a read lock but there are subtle
* problems. XFS for example gets freeze protection on internal level
* twice in some cases, which is OK only because we already hold a
* freeze protection also on higher level. Due to these cases we have
* to use wait == F (trylock mode) which must not fail.
*/
if (wait) {
int i;
for (i = 0; i < level - 1; i++)
if (percpu_rwsem_is_held(sb->s_writers.rw_sem + i)) {
force_trylock = true;
break;
}
}
#endif
if (wait && !force_trylock)
percpu_down_read(sb->s_writers.rw_sem + level-1);
else
ret = percpu_down_read_trylock(sb->s_writers.rw_sem + level-1);
WARN_ON(force_trylock & !ret);
return ret;
}
EXPORT_SYMBOL(__sb_start_write);
再次感谢。
【讨论】: