如何查找底层Linux内核是否支持Copy on Write?

问题描述 投票:0回答:2

例如,我正在研究一个古老的内核,想知道它是否真的实现了Copy on Write。有没有一种方法(最好是在C语言中编程)来发现?

c linux process linux-kernel fork
2个回答
1
投票

没有,没有一个可靠的程序化的方法可以在用户区进程中找到。

COW背后的想法是,它应该对用户代码完全透明。你的代码接触到各个页面,页面故障被调用,内核复制相应的页面,然后你的进程就会恢复,就像什么都没发生一样。


0
投票

我偶然发现了这个比较老的问题,我看到其他人已经指出,"检测CoW "的意义不大,因为Linux已经暗示了CoW。

然而我觉得这个问题非常有趣,虽然从技术上讲,人们应该无法检测到这种应该对用户空间进程完全透明的内核机制,但实际上有一些架构上的特定方式(即侧通道)可以被利用来确定Copy on Write是否发生。

在x86处理器上,支持 受限的事务性记忆当出现异常(如页面故障)时,你可以利用内存事务被中止这一事实。给定一个有效的地址,这个信息可以用来检测一个页面是否驻留在内存中(类似于在内存中使用的 minicore(2)),甚至检测Copy on Write。

这里有一个工作实例。注意:检查你的处理器是否支持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;
}

在我的机器上的输出。

Initial state : 1
----- fork -----
Parent before : 0
Parent after R: 0
Parent after W: 1
Child before  : 0
Child after R : 0
Child after W : 1

正如你所看到的,对标记为CoW的页面进行写入(在本例中是在fork之后) 会导致事务失败,因为会触发一个页面故障异常,并导致事务中止。在事务中止之前,硬件会将更改的内容进行还原。在对页面进行写入后,再次尝试做同样的事情,结果是事务正确终止,函数返回了 1.

当然,这不应该被认真使用,而只是作为一个有趣的练习。因为RTM事务会因为任何类型的异常和上下文切换而被中止,所以假阴性是可能的(例如,如果进程在事务的中间被内核抢占了)。保持事务代码非常短(在上面的例子中,只有一个分支和一个赋值)。*p = 0)是必不可少的。也可以进行多次检测,以避免假阴性。

© www.soinside.com 2019 - 2024. All rights reserved.