C 如何找到底层Linux内核是否支持写时复制?

C 如何找到底层Linux内核是否支持写时复制?,c,linux,process,linux-kernel,fork,C,Linux,Process,Linux Kernel,Fork,例如,我正在研究一个古老的内核,想知道它是否真的实现了写时复制。有没有一种方法(最好是C语言中的编程方法)可以找到答案?没有,没有一种可靠的编程方法可以从userland进程中找到答案 COW背后的想法是,它应该对用户代码完全透明。您的代码涉及到各个页面,页面错误被调用,内核复制相应的页面,您的进程被恢复,就好像什么都没有发生一样。我偶然发现了这个相当老的问题,我看到其他人已经指出,“检测CoW”没有多大意义因为Linux已经暗示了CoW 然而,我发现这个问题非常有趣,虽然从技术上讲,人们不应该

例如,我正在研究一个古老的内核,想知道它是否真的实现了写时复制。有没有一种方法(最好是C语言中的编程方法)可以找到答案?

没有,没有一种可靠的编程方法可以从userland进程中找到答案


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。即使我必须编写一个内核模块也可以:)另外,关于页面错误,我们是否有任何方法至少可以检测到这一点,以便确认页面上的副本是现在完成的,而不是在创建进程时完成的?您可以锁定页面,防止页面被调出,这是否足够?