使用 mlockall() 进行实时应用的最佳方法(纳秒敏感)

Optimal Way of Using mlockall() for Real-time Application (nanosecond sensitive)

本文关键字:方法 最佳 应用 mlockall 实时 使用      更新时间:2023-10-16

我正在阅读mlockall()的手册页:http://man7.org/linux/man-pages/man2/mlock.2.html

它提到

Real-time processes that are using mlockall() to prevent delays on page 
faults should reserve enough locked stack pages before entering the time-
critical section, so that no page fault can be caused by function calls.  This 
can be achieved by calling a function that allocates a sufficiently large 
automatic variable (an array) and writes to the memory occupied by this array in 
order to touch these stack pages.  This way, enough pages will be mapped for the 
stack and can be locked into RAM.  The dummy writes ensure that not even copy-
on-write page faults can occur in the critical section.

我对这种说法有点困惑:

This can be achieved by calling a function that allocates a sufficiently large 
automatic variable (an array) and writes to the memory occupied by this array in 
order to touch these stack pages.

调用函数时,所有自动变量(堆栈上的变量)都是在堆栈上"动态"创建的。 那么我怎样才能实现最后一句话所说的呢?

例如,假设我有这个函数:

void foo() {
char a;
uint16_t b;
std::deque<int64_t> c;
// do something with those variables
}

或者这是否意味着在我调用任何函数之前,我应该在 main() 中调用这样的函数:

void reserveStackPages() {
int64_t stackPage[4096/8 * 1024 * 1024];
memset(stackPage, 0, sizeof(stackPage));
}

如果是,如果我首先在堆上分配 stackPage 变量,写入然后释放,这有什么区别吗? 可能是的,因为堆和堆栈是 RAM 中的 2 个不同区域?

std::d eque 上面存在只是为了提出另一个相关的问题——如果我想为同时使用堆栈页和堆页的东西保留内存怎么办。 调用 reserveStackPages() 的"堆"版本会有所帮助吗?

目标是最大限度地减少应用程序中的所有抖动(是的,我知道还有许多其他事情需要注意,例如TLB未命中等;只是试图一次处理一种抖动,然后慢慢发展到所有抖动)。

提前谢谢。

附言 如果重要的话,这是针对低延迟交易应用程序。

你通常不需要使用mlockall,除非你编写(或多或少困难的)实时应用程序(我实际上从未使用过它)。

如果你确实需要它,你最好用C(而不是正版C++)编写代码中最实时的部分,因为你肯定想了解内存分配的细节。请注意,除非您深入研究std::deque实现,否则您并不确切知道它的位置(可能大多数数据都是堆分配的,即使您的c是自动变量)。

您应该首先详细了解进程的虚拟地址空间。为此,proc(5) 很有用: 从你的进程内部,你会读取/proc/self/maps(见这里),从外部(例如某个终端)你会对 pid 1234 的进程做cat /proc/1234/maps。或者使用 pmap(1)。

因为堆和堆栈是 RAM 中的 2 个不同区域?

实际上,进程的地址空间包含许多段(在/proc/1234/maps中列出),远远超过两个段。通常,每个动态链接的共享库(如libc.so)都会带来一些段。

尝试在终端中cat /proc/self/mapscat /proc/$$/maps,以更好地了解虚拟地址空间。在我的机器上,第一个给出了cat过程的 19 个段 - 每个段显示为一条线 - 第二个 97 个段是zsh(我的 shell)进程。

为了确保您的堆栈有足够的空间,您确实可以调用一个函数来分配足够大的自动变量,例如您的reserveStackPages。请注意,调用堆栈的大小实际上是有限的(通常只有几兆字节,另请参阅 setrlimit(2))。

如果您确实需要mlockall(这不太可能),您可以考虑静态链接程序(以减少虚拟地址空间中的段)。

还要看看madvise(2)(也许还有mincore(2))。它通常比mlockall有用得多。顺便说一句,实际上,您的大部分虚拟内存都在 RAM 中(除非您的系统尝试抖动,然后您会立即看到它)。

另请阅读操作系统:三个简单部分以了解分页的作用。

纳秒敏感应用程序没有多大意义(因为软件无法控制的缓存未命中)。