Linux内核-msync锁定行为



我正在研究一个应用程序,该应用程序将固定大小的块(例如4k(中的随机数据写入大型缓冲文件中的随机位置。我有几个进程(而不是线程(在做这件事,每个进程都分配了自己的缓冲区文件。

如果我使用mmap+msync将数据写入并持久化到磁盘,我会看到16个进程的性能峰值,而更多线程(32个进程(的性能下降。

如果我使用open+write+fsync,我不会看到这样的峰值,而是性能平稳期(mmap比open/write慢(。

我读过很多遍[1,2],说mmap和msync都可以取锁。使用vtune,我分析了我们确实是自旋锁定的,并且在clear_page_ermsxas_load函数上花费了最多的时间。

然而,在阅读msync[3]的源代码时,我无法理解这些锁是全局锁还是按文件锁。论文[2]指出,锁在内核中的基数树上,这些基数树是每个文件的,然而,当我在内核中观察到一些自旋锁时,我相信有些锁可能是全局的,因为我每个进程有一个文件。

你有没有解释一下为什么我们在16个进程中对mmap有这样的峰值,以及对msync的锁定行为的输入?

谢谢!

最佳,Maximilian

[1]https://kb.pmem.io/development/100000025-Why-msync-is-less-optimal-for-persistent-memory/

[2] 为快速存储设备优化内存映射I/O,Papagiannis等人,ATC’20

[3]https://elixir.bootlin.com/linux/latest/source/mm/msync.c

在4.19内核源中,当前任务保留自己的mm_struct,它包含一个信号量,用于仲裁对所有同步内存区域的访问。因此,进程中作用于某个缓冲区文件的所有线程都将使用该信号量,对文件的某些区域进行操作,并释放该信号量。

虽然我无法合理化达到性能悬崖的16个进程的确切数量,但很明显,当您使用mmap((时,您正在强制进入VM_SHARED的msync(M_SYNC(代码部分。这会调用vfs_fsync_range((,并保证实际的同步磁盘I/O会发生,这通常会减慢表演的速度:它不允许对I/O进行有利的分组以节省开支,并倾向于最大限度地增加等待磁盘I/O完成的实际时间。

为了避免这种情况,请确保进程中的每个线程都管理缓冲区文件中4K块的专用子集,避免缓冲区文件上的mmap((,并调度异步I/O。只要您在缓冲区文件本身上避免mmap((,每个线程都将单独写入缓冲区文件的自己部分(如果您设计得好,则可以安全地写入(。因此,您将能够将所有I/O指定为异步,这将允许更好的聚合并显著提高应用程序的性能,即避免在16个进程或最终的任何数量时出现悬崖。显然,如果出现对同一块进行另一次写入的请求,那么您仍然必须确保任何写入其某个块的线程要么完成了写入,要么尚未开始写入(并放弃任何这样做的请求(。

最新更新