C++ 使用 COW 创建了多少页面错误?
C++ How Many Page-Faults are Created with COW?
我在网上看到这个问题,不知道我的回答是否正确:
#include <unistd.h>
#include <sys/wait.h>
#define N 100*1024*1024
int a[N];
int main() {
if (fork() > 0) {
wait(nullptr);
} else {
for (unsigned int i = 0; i < N; ++i) {
a[i] = 1;
}
}
return 0;
}
问题是:
Considering COW technique is enabled in x86-64 OS (With a page size of 4KB = 4096B) how many page faults the child process creates during its whole runtime. (Not looking for very exact answer)
- 1024*1
- 1024*10
- 1024*100
- 1024*1024
- 1024*1024*10
- 1024*1024*100
我认为正确的是 6,因为每次子进程尝试写入内存中的写保护位置时,我们都会尝试复制 N
个元素。我说得对吗?
我尝试 运行 strace
我的程序,似乎 none 是正确的,因为输出不包含任何数千行代表每个页面错误:
strace ./a.out
execve("./a.out", ["./a.out"], 0x7ffd84f9ace0 /* 50 vars */) = 0
brk(NULL) = 0x55c515202000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=96020, ...}) = 0
mmap(NULL, 96020, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f93cbf68000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "7ELF[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]>[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030928, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f93cbf66000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f93cb966000
mprotect(0x7f93cbb4d000, 2097152, PROT_NONE) = 0
mmap(0x7f93cbd4d000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f93cbd4d000
mmap(0x7f93cbd53000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f93cbd53000
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7f93cbf674c0) = 0
mprotect(0x7f93cbd4d000, 16384, PROT_READ) = 0
mprotect(0x55c4fb12b000, 4096, PROT_READ) = 0
mprotect(0x7f93cbf80000, 4096, PROT_READ) = 0
munmap(0x7f93cbf68000, 96020) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f93cbf67790) = 3341
wait4(-1, NULL, 0, NULL) = 3341
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3341, si_uid=1000, si_status=0, si_utime=19, si_stime=35} ---
exit_group(0) = ?
+++ exited with 0 +++
页面错误不会显示在 strace 中;它们甚至不是“明确”发生的事情。
每次访问分配的物理页面尚未创建或映射到地址 space.
的内存时,都会发生页面错误
实际上,您程序的页面错误数是 sizeof(int)*N/pagesize。解释:
数组 a
在全局 .data
段中创建,但默认已初始化。 .data
在进程启动时被映射 MAP_ANONYMOUS 一次,并且由于填充初始化,它的清零将发生在第一个页面错误上。由于父进程从不访问数组a
,因此父进程不会发生a
占用的地址space的单页错误。
唯一会导致 a
页面错误的进程是子进程。但是,页面错误只会在第一次访问以前的非驻留页面时发生。由于 a
是按顺序遍历的,因此 a
上的页面错误只会发生在 0 == ((uintptr_t)&a[i]) % pagesize
访问的页面上(假设 uintptr_t 强制转换指针与地址完全对应,在大多数平台上,它们确实如此).
当然,在内存压力高的系统中,系统可能会崩溃,从而在迭代器到达下一页之前换出页面,从而产生额外的页面错误。
我在网上看到这个问题,不知道我的回答是否正确:
#include <unistd.h>
#include <sys/wait.h>
#define N 100*1024*1024
int a[N];
int main() {
if (fork() > 0) {
wait(nullptr);
} else {
for (unsigned int i = 0; i < N; ++i) {
a[i] = 1;
}
}
return 0;
}
问题是:
Considering COW technique is enabled in x86-64 OS (With a page size of 4KB = 4096B) how many page faults the child process creates during its whole runtime. (Not looking for very exact answer)
- 1024*1
- 1024*10
- 1024*100
- 1024*1024
- 1024*1024*10
- 1024*1024*100
我认为正确的是 6,因为每次子进程尝试写入内存中的写保护位置时,我们都会尝试复制 N
个元素。我说得对吗?
我尝试 运行 strace
我的程序,似乎 none 是正确的,因为输出不包含任何数千行代表每个页面错误:
strace ./a.out
execve("./a.out", ["./a.out"], 0x7ffd84f9ace0 /* 50 vars */) = 0
brk(NULL) = 0x55c515202000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=96020, ...}) = 0
mmap(NULL, 96020, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f93cbf68000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "7ELF[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]>[=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=][=11=]"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030928, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f93cbf66000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f93cb966000
mprotect(0x7f93cbb4d000, 2097152, PROT_NONE) = 0
mmap(0x7f93cbd4d000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f93cbd4d000
mmap(0x7f93cbd53000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f93cbd53000
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7f93cbf674c0) = 0
mprotect(0x7f93cbd4d000, 16384, PROT_READ) = 0
mprotect(0x55c4fb12b000, 4096, PROT_READ) = 0
mprotect(0x7f93cbf80000, 4096, PROT_READ) = 0
munmap(0x7f93cbf68000, 96020) = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f93cbf67790) = 3341
wait4(-1, NULL, 0, NULL) = 3341
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=3341, si_uid=1000, si_status=0, si_utime=19, si_stime=35} ---
exit_group(0) = ?
+++ exited with 0 +++
页面错误不会显示在 strace 中;它们甚至不是“明确”发生的事情。
每次访问分配的物理页面尚未创建或映射到地址 space.
的内存时,都会发生页面错误实际上,您程序的页面错误数是 sizeof(int)*N/pagesize。解释:
数组 a
在全局 .data
段中创建,但默认已初始化。 .data
在进程启动时被映射 MAP_ANONYMOUS 一次,并且由于填充初始化,它的清零将发生在第一个页面错误上。由于父进程从不访问数组a
,因此父进程不会发生a
占用的地址space的单页错误。
唯一会导致 a
页面错误的进程是子进程。但是,页面错误只会在第一次访问以前的非驻留页面时发生。由于 a
是按顺序遍历的,因此 a
上的页面错误只会发生在 0 == ((uintptr_t)&a[i]) % pagesize
访问的页面上(假设 uintptr_t 强制转换指针与地址完全对应,在大多数平台上,它们确实如此).
当然,在内存压力高的系统中,系统可能会崩溃,从而在迭代器到达下一页之前换出页面,从而产生额外的页面错误。