为什么这段代码会导致页面错误?
Why does this code cause page faults?
我分配了 512mb 的内存,然后我修改了每 4096 个字节(这应该会导致每次修改都出现轻微的页面错误,这就是我实际得到的结果)。但随后我重复了相同的循环,它再次导致每个请求出现轻微的页面错误。我的问题是,为什么?
输出如下所示:
但是,如果我从我的程序中删除调用 ps,则执行第二个循环所需的时间会少得多,例如 0.04 秒。为什么?
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <sstream>
#include <unistd.h>
using namespace std;
const int sz = 512 * 1024 * 1024;
int main()
{
char * data = (char *)malloc(sz);
if (data == 0) cout << "alloc fail";
stringstream cmd;
cmd << "ps -o min_flt,maj_flt " << getpid();
system(cmd.str().c_str());
cout << "start\n";
clock_t start = clock();
for (int i=0; i<sz; i += 4096)
data[i] = 1;
clock_t end = clock();
double time1 = double(end-start) / CLOCKS_PER_SEC;
system(cmd.str().c_str());
start = clock();
for (int i = 0; i < sz; i += 4096)
data[i] = 1;
end = clock();
double time2 = double(end-start) / CLOCKS_PER_SEC;
system(cmd.str().c_str());
cout << time1 << " " << time2 << endl;
}
库函数system
大致做了以下事情:
- Fork,使用内存映像的副本创建新进程。
- 在子进程中,
exec
/bin/sh
向其传递命令行选项 -c
和指示的命令,用新加载的 /bin/sh.
- 在父进程(原进程)中,等待子进程完成,return它的状态码。
当进程执行fork
时,系统希望避免复制整个内存映像以复制它。所以它只是复制页表,并将所有页面标记为 "copy-on-write" 这需要将它们设置为只读以便可以检测到写入。
随后的exec
会将页面标记为未共享,但它可能不会取消页面的只读设置,因此后续对页面的写入仍会触发轻微的页面错误,尽管由于页面不再共享,处理程序将不执行任何操作。
实际上不能保证 exec
会在第二个写循环开始之前发生。您的机器很可能有一个以上的内核,因此两个进程很可能同时处于活动状态。由于 exec
可能需要一段时间才能设置,因此很可能某些写循环甚至会在 exec
之前发生,也就是说在没有任何线索表明复制之前-write 是不必要的。
我分配了 512mb 的内存,然后我修改了每 4096 个字节(这应该会导致每次修改都出现轻微的页面错误,这就是我实际得到的结果)。但随后我重复了相同的循环,它再次导致每个请求出现轻微的页面错误。我的问题是,为什么?
输出如下所示:
但是,如果我从我的程序中删除调用 ps,则执行第二个循环所需的时间会少得多,例如 0.04 秒。为什么?
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <sstream>
#include <unistd.h>
using namespace std;
const int sz = 512 * 1024 * 1024;
int main()
{
char * data = (char *)malloc(sz);
if (data == 0) cout << "alloc fail";
stringstream cmd;
cmd << "ps -o min_flt,maj_flt " << getpid();
system(cmd.str().c_str());
cout << "start\n";
clock_t start = clock();
for (int i=0; i<sz; i += 4096)
data[i] = 1;
clock_t end = clock();
double time1 = double(end-start) / CLOCKS_PER_SEC;
system(cmd.str().c_str());
start = clock();
for (int i = 0; i < sz; i += 4096)
data[i] = 1;
end = clock();
double time2 = double(end-start) / CLOCKS_PER_SEC;
system(cmd.str().c_str());
cout << time1 << " " << time2 << endl;
}
库函数system
大致做了以下事情:
- Fork,使用内存映像的副本创建新进程。
- 在子进程中,
exec
/bin/sh
向其传递命令行选项-c
和指示的命令,用新加载的 /bin/sh. - 在父进程(原进程)中,等待子进程完成,return它的状态码。
当进程执行fork
时,系统希望避免复制整个内存映像以复制它。所以它只是复制页表,并将所有页面标记为 "copy-on-write" 这需要将它们设置为只读以便可以检测到写入。
随后的exec
会将页面标记为未共享,但它可能不会取消页面的只读设置,因此后续对页面的写入仍会触发轻微的页面错误,尽管由于页面不再共享,处理程序将不执行任何操作。
实际上不能保证 exec
会在第二个写循环开始之前发生。您的机器很可能有一个以上的内核,因此两个进程很可能同时处于活动状态。由于 exec
可能需要一段时间才能设置,因此很可能某些写循环甚至会在 exec
之前发生,也就是说在没有任何线索表明复制之前-write 是不必要的。