例如,我正在研究一个古老的内核,并想知道它是否真的实现了Copy on Write。找出方法(最好用C编程)?
答案 0 :(得分:1)
不,没有一种可靠的编程方式可以在用户空间进程中找到它。
COW背后的想法是它应该对用户代码完全透明。您的代码触及各个页面,调用页面错误,内核复制相应的页面,您的进程将恢复,就像没有发生任何事情一样。
答案 1 :(得分:0)
我偶然发现了这个相当古老的问题,并且我看到其他人已经指出,“检测CoW”没有太大意义,因为Linux已经暗示了CoW。
但是我发现这个问题很有趣,尽管从技术上讲,这应该不能检测对用户空间进程完全透明的这种内核机制,但是实际上存在一些特定于体系结构的方式(例如,旁通道)利用以确定是否发生“写时复制”。
在支持Restricted Transactional Memory的x86处理器上,您可以利用以下事实:当发生诸如页面错误之类的异常时,内存事务将中止。给定一个有效的地址,此信息可用于检测页面是否驻留在内存中(类似于使用minicore(2)
),甚至可以检测写入时复制。
这是一个可行的例子。注意:通过查看/proc/cpuinfo
的{{1}}标志来检查处理器是否支持RTM,并使用GCC 无优化和rtm
标志进行编译。>
-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之后)会导致事务失败,因为触发了页面错误异常并导致事务中止。事务中止之前,更改将由硬件还原。写入页面后,再次尝试执行相同的操作将导致事务正确终止,并且函数返回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
。
当然,不应真正认真地使用此方法,而应将其视为有趣而有趣的练习。由于RTM事务因任何类型的异常以及上下文切换而中止,因此可能会产生否定错误(例如,如果进程在事务中间被内核抢占了该进程)。保持事务代码非常短(在上述情况下,仅是分支和分配1
)至关重要。还可以进行多次测试以避免误报。