LinuxでメモリをmallocするCプログラムを作成し、ループで実行しましたが、TOPはメモリ消費量を表示しませんでした。
それから私はそのメモリで何かをしました、そしてTOPはメモリ消費を示しました。
私がmallocするとき、私は本当に「メモリを取得」しますか、それとも「怠惰な」メモリ管理がありますか?それは私がそれを使用する場合にのみメモリを提供しますか?
(TOPは、使用時にメモリ消費量しかわからないというオプションもあるので、よくわかりません。)
ありがとう
LinuxでメモリをmallocするCプログラムを作成し、ループで実行しましたが、TOPはメモリ消費量を表示しませんでした。
それから私はそのメモリで何かをしました、そしてTOPはメモリ消費を示しました。
私がmallocするとき、私は本当に「メモリを取得」しますか、それとも「怠惰な」メモリ管理がありますか?それは私がそれを使用する場合にのみメモリを提供しますか?
(TOPは、使用時にメモリ消費量しかわからないというオプションもあるので、よくわかりません。)
ありがとう
Linuxでは、mallocはsbrk()またはmmap()を使用してメモリを要求します。どちらの場合も、アドレススペースはすぐに拡張されますが、Linuxは、問題のページに最初に書き込むまで、物理メモリの実際のページを割り当てません。RESで実際の物理メモリ使用量を確認しながら、VIRT列でアドレス空間の拡張を確認できます。
これは少し本題から外れますが (それからあなたの質問に結び付けます)、何が起きているかは、Linux でプロセスを fork したときに何が起こるかに似ています。フォークする場合、コピー オン ライトと呼ばれるメカニズムがあり、メモリが書き込まれるときに新しいプロセスのメモリ スペースのみをコピーします。このようにして、フォークされたプロセス exec がすぐに新しいプログラムを実行する場合、元のプログラムのメモリをコピーするオーバーヘッドを節約できます。
質問に戻りますが、考え方は似ています。他の人が指摘しているように、メモリを要求すると仮想メモリ空間がすぐに取得されますが、実際のページはそれらに書き込むときにのみ割り当てられます。
これの目的は何ですか?基本的に、メモリの割り当てを Big O(n) 操作ではなく、多かれ少なかれ一定時間の操作 Big O(1) にします (Linux スケジューラが 1 つの大きなチャンクで実行するのではなく、それを分散させる方法と同様です)。
私が何を意味するかを実証するために、次の実験を行いました。
rbarnes@rbarnes-desktop:~/test_code$ time ./bigmalloc
real 0m0.005s
user 0m0.000s
sys 0m0.004s
rbarnes@rbarnes-desktop:~/test_code$ time ./deadbeef
real 0m0.558s
user 0m0.000s
sys 0m0.492s
rbarnes@rbarnes-desktop:~/test_code$ time ./justwrites
real 0m0.006s
user 0m0.000s
sys 0m0.008s
bigmalloc プログラムは 2000 万の int を割り当てますが、何もしません。デッドビーフは各ページに 1 つの int を書き込み、その結果 19531 回の書き込みが発生し、justwrites は 19531 個の int を割り当ててそれらをゼロにします。ご覧のとおり、deadbeef の実行には bigmalloc の約 100 倍、justwrites の約 50 倍の時間がかかります。
#include <stdlib.h>
int main(int argc, char **argv) {
int *big = malloc(sizeof(int)*20000000); // allocate 80 million bytes
return 0;
}
.
#include <stdlib.h>
int main(int argc, char **argv) {
int *big = malloc(sizeof(int)*20000000); // allocate 80 million bytes
// immediately write to each page to simulate all at once allocation
// assuming 4k page size on 32bit machine
for ( int* end = big + 20000000; big < end; big+=1024 ) *big = 0xDEADBEEF ;
return 0;
}
.
#include <stdlib.h>
int main(int argc, char **argv) {
int *big = calloc(sizeof(int),19531); // number of writes
return 0;
}
はい、あなたがそれに触れない限り、メモリはあなたのメモリスペースにマッピングされません。mallocingメモリはページングテーブルのみをセットアップするため、割り当てられたメモリでページフォールトが発生したときに、メモリをにマップする必要があることがわかります。
コンパイラの最適化を使用していますか?割り当てられたリソースを使用していないため、オプティマイザーが割り当てを削除した可能性がありますか?
この機能はオーバーコミットと呼ばれます。カーネルは、データ セグメントのサイズを増やすことでメモリを「約束」しますが、物理メモリを割り当てません。その新しいスペースのアドレスに触れると、プロセスはカーネルにページフォールトし、物理ページをそれにマップしようとします。
はい、VirtualAllocフラグに注意してください。
MEM_RESERVE
MEM_COMMIT
.
LinuxやPOSIX/BSD/SVR#システムの場合、vfork() は古くから存在し、同様の機能を提供します。
vfork() 関数は、子プロセスがコードとデータを呼び出しプロセス (親プロセス) と共有できるという点でのみ fork() と異なります。これにより、 vfork() が誤用された場合、親プロセスの整合性が損なわれる危険性があり、複製アクティビティが大幅に高速化されます。
vfork() を exec ファミリからの関数または _exit() への即時呼び出しの前置きとして以外の目的で使用することはお勧めしません。
vfork() 関数を使用すると、古いプロセスのアドレス空間を完全にコピーすることなく、新しいプロセスを作成できます。fork() によって親から子にコピーされたデータ領域は、フォークされたプロセスが単に exec を呼び出すだけの場合は使用されません。これは、ページ化された環境では特に非効率的であり、vfork() が特に役立ちます。親のデータ領域のサイズに応じて、vfork() は fork() よりも大幅にパフォーマンスを向上させることができます。