当我在调用时跟踪函数图时write()
,我发现在函数内ext4_file_write_iter()
它在开始时inode->i_rwsem
通过调用锁定了。inode_lock(inode)
之后调用__generic_file_write_iter()
将数据写入文件。并inode
最终解锁。
那么它是inode->i_rwsem
用来保护对同一文件的并发写入吗?
但是我编写了一个程序,该程序同时将数据写入文件的同一区域(pwrite(fd,buf,SIZE,0)
),结果显示写入未序列化。我发现它必须使用flock
/fcntl
来序列化并发写入,这取决于inode->i_flctx
.
我想问的是,这样做的目的是什么inode->i_rwsem
?
inode->i_rwsem
、inode->i_flctx
和之间有什么不同inode->i_lock
?
谢谢。
答案1
inode->i_rwsem
由内核内部使用,以确保内核本身不会同时从文件读取或写入文件,以避免任何损坏或竞争条件。它不影响用户空间;您仍然可以同时由多个进程打开该文件以进行读/写。但是,如果多个进程尝试同时读取/写入文件,内核实际上会在幕后串行执行此操作。
在您的情况下,如果有两个进程尝试使用 写入同一区域pwrite(fd,buf,SIZE,0)
,而没有内部锁定机制(例如i_rwsem
用于什么),则内核可能会开始从第一个进程写入一些数据,并且同时时间从第二个进程开始写入数据,没有写操作第一的过程完成。它会影响整个文件系统的完整性,甚至可能导致内核崩溃竞争条件。
内核中的内部锁定可以防止这些情况发生。第一个进程的第一次写入将完成,然后才会执行第二次写入(如果它们都写入文件中完全相同的区域,则可能会覆盖第一个进程的“写入”)。
inode->i_flctx
正如您已经发现的那样,当进程本身想要限制可以同时打开文件的进程数量时,它是由来自用户空间的flock
/调用控制的。fcntl
例如,一个进程可以锁定文件以进行写入,如果另一个进程想要在另一个进程释放同一文件之前锁定该文件,它将被拒绝或阻止。
让我们以两个进程写入同一文件并执行不同写入的情况为例。每个进程都可以覆盖其他进程写入的数据。为了避免在用户空间, 这应用程序本身可以使用flock
/fcntl
来防止两个进程打开同一个文件。
这是另一个例子:
- 一个进程写入文件,第二个进程读取同一文件。
- 第二个进程可以读取部分数据,因为第一个进程尚未完成写入。
在这种情况下,为了防止出现这种情况:
- 第一个进程必须获取文件的锁,以防止其他进程在完成写入之前打开该文件。
- 第二个进程将尝试获取同一文件的锁,并且将被阻止(或失败,取决于它尝试锁定文件的方式),因为它已经被另一个进程锁定。
- 第一个进程完成写入,释放锁(同样,显式地在用户空间通过调用提到的系统调用之一)
- 只有这样第二个进程才能锁定文件以进行读取。
- 当第二个进程正在读取文件时,尝试获取该文件锁的其他进程将再次被阻止,直到:
- 阅读过程完成阅读。
所以用flock
/fcntl
你可以处理这些情况以编程方式在应用程序的源代码中,内核用于i_flctx
了解某个进程是否获取了文件的锁,并防止其他进程获取另一个锁,直到第一个进程释放它。
inode->i_lock
,就像 一样inode->i_rwsem
,仅由内核用来在处理内核中的 inode 状态时保护内核免受竞争条件的影响。i_rwsem
用于保护写入,i_lock
用于保护inode状态的改变。
换句话说,除非你是内核开发人员,否则你不应该担心inode->i_lock
or inode->i_rwsem
,它们只是内核 inode 实现机制的一部分,也不用担心inode->i_flctx
哪一个是内核从用户空间锁定文件的内部实现机制的一部分。