C 如何找到底层Linux内核是否支持写时复制?
例如,我正在研究一个古老的内核,想知道它是否真的实现了写时复制。有没有一种方法(最好是C语言中的编程方法)可以找到答案?没有,没有一种可靠的编程方法可以从userland进程中找到答案C 如何找到底层Linux内核是否支持写时复制?,c,linux,process,linux-kernel,fork,C,Linux,Process,Linux Kernel,Fork,例如,我正在研究一个古老的内核,想知道它是否真的实现了写时复制。有没有一种方法(最好是C语言中的编程方法)可以找到答案?没有,没有一种可靠的编程方法可以从userland进程中找到答案 COW背后的想法是,它应该对用户代码完全透明。您的代码涉及到各个页面,页面错误被调用,内核复制相应的页面,您的进程被恢复,就好像什么都没有发生一样。我偶然发现了这个相当老的问题,我看到其他人已经指出,“检测CoW”没有多大意义因为Linux已经暗示了CoW 然而,我发现这个问题非常有趣,虽然从技术上讲,人们不应该
COW背后的想法是,它应该对用户代码完全透明。您的代码涉及到各个页面,页面错误被调用,内核复制相应的页面,您的进程被恢复,就好像什么都没有发生一样。我偶然发现了这个相当老的问题,我看到其他人已经指出,“检测CoW”没有多大意义因为Linux已经暗示了CoW 然而,我发现这个问题非常有趣,虽然从技术上讲,人们不应该能够检测到这种内核机制,这种机制应该对用户空间进程完全透明,但实际上有一些特定于体系结构的方法(即侧通道)可以被利用来确定是否发生了写时复制 在支持的x86处理器上,您可以利用这样一个事实,即当出现异常(如页面错误)时,内存事务会中止。给定有效地址,此信息可用于检测页是否驻留在内存中(类似于使用),甚至可用于检测写时复制 这里有一个有效的例子。注意:检查处理器是否支持RTM,方法是查看
/proc/cpuinfo
中的RTM
标志,并使用GCC进行编译,而不进行优化并使用-mrtm
标志
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <immintrin.h>
/* Use x86 transactional memory to detect a page fault when trying to write
* at the specified address, assuming it's a valid address.
*/
static int page_dirty(void *page) {
unsigned char *p = page;
if (_xbegin() == _XBEGIN_STARTED) {
*p = 0;
_xend();
/* Transaction successfully ended => no context switch happened to
* copy page into virtual memory of the process => page was dirty.
*/
return 1;
} else {
/* Transaction aborted => page fault happened and context was switched
* to copy page into virtual memory of the process => page wasn't dirty.
*/
return 0;
}
/* Should not happen! */
return -1;
}
int main(void) {
unsigned char *addr;
addr = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (addr == MAP_FAILED) {
perror("mmap failed");
return 1;
}
// Write to trigger initial page fault and actually reserve memory
*addr = 123;
fprintf(stderr, "Initial state : %d\n", page_dirty(addr));
fputs("----- fork -----\n", stderr);
if (fork()) {
fprintf(stderr, "Parent before : %d\n", page_dirty(addr));
// Read (should NOT trigger Copy on Write)
*addr;
fprintf(stderr, "Parent after R: %d\n", page_dirty(addr));
// Write (should trigger Copy on Write)
*addr = 123;
fprintf(stderr, "Parent after W: %d\n", page_dirty(addr));
} else {
fprintf(stderr, "Child before : %d\n", page_dirty(addr));
// Read (should NOT trigger Copy on Write)
*addr;
fprintf(stderr, "Child after R : %d\n", page_dirty(addr));
// Write (should trigger Copy on Write)
*addr = 123;
fprintf(stderr, "Child after W : %d\n", page_dirty(addr));
}
return 0;
}
如您所见,写入标记为CoW的页面(在本例中是在fork之后)会导致事务失败,因为会触发页面错误异常并导致事务中止。在事务中止之前,硬件会还原更改。写入页面后,再次尝试执行相同操作将导致事务正确终止,并且函数返回1
当然,这不应该被认真地使用,而仅仅是作为一种有趣的练习。由于RTM事务被中止用于任何类型的异常和上下文切换,所以假阴性是可能的(例如,如果该过程在事务的中间被内核抢占)。保持事务代码非常短(在上述情况下,只有一个分支和一个赋值
*p=0
)是至关重要的。还可以进行多次测试以避免误报。没有不支持COW的Linux工作版本。任何旧版本都有太多严重的bug和安全漏洞,您无法使用。@R。。抱歉用了“古老”的字眼!可能是我的意图被误报了。我想更多地了解CoW(我指的是复制页面、地址空间等)@R。。我觉得奶牛的许多方面都会脱颖而出,试图回答这个问题!MMU硬件不支持COW!因此,MMU硬件不支持fork()
。→ 将vm.overmit\u内存设置为overmit\u ALWAYS(1),创建一个地址空间非常大的进程,fork()
it,触摸它的页面,查看OOM。即使我必须编写一个内核模块也可以:)另外,关于页面错误,我们是否有任何方法至少可以检测到这一点,以便确认页面上的副本是现在完成的,而不是在创建进程时完成的?您可以锁定页面,防止页面被调出,这是否足够?