getrlimit(2) は、manページで次のように定義されています。
RLIMIT_ASプロセスの仮想メモリ(アドレス空間)の最大サイズ(バイト単位)。この制限は、この制限を超えるとエラーENOMEMで失敗するbrk(2)、mmap(2)、およびmremap(2)の呼び出しに影響します。また、自動スタック拡張は失敗します(そして代替スタックがない場合、プロセスを強制終了するSIGSEGVを生成しますsigaltstack(2)を介して利用可能になりました)。値が長いため、32ビット長のマシンでは、この制限は最大2 GiBであるか、このリソースは無制限です。
ここで「自動スタック拡張」とはどういう意味ですか? Linux/UNIX環境のスタックは必要に応じて拡張されますか?はいの場合、正確なメカニズムは何ですか?
はい、スタックは動的に成長します。スタックはメモリの一番上にあり、ヒープに向かって下に向かって成長します。
--------------
| Stack |
--------------
| Free memory|
--------------
| Heap |
--------------
.
.
新しい関数が呼び出されると、ヒープは(mallocを実行するたびに)上向きに成長し、スタックは下向きに成長します。ヒープは、プログラムのBSSセクションのすぐ上にあります。つまり、プログラムのサイズと、ヒープ内のメモリをすべて分類する方法も、そのプロセスの最大スタックサイズに影響します。通常、スタックサイズは無制限です(ヒープ領域とスタック領域が一致するか、スタックオーバーフローとSIGSEGVが発生する上書きされるまで:-)
これはユーザープロセス専用です。カーネルスタックは常に固定されています(通常は8KB)。
正確なメカニズムは、Linuxのここに記載されています。 匿名マッピングでページ違反を処理する あなた 「成長した割り当て」であるかどうかを確認してください) スタック。 VMエリアレコードがそうするべきであると言っている場合、スタックを拡張するために開始アドレスを調整します。
ページフォルトが発生すると、アドレスに応じて、スタック拡張によってサービスが提供されます(フォルトが打ち消されます)。仮想メモリのこの「フォールトで下向きに成長する」動作は、MAP_GROWSDOWN
フラグがmmap
syscallに渡されている任意のユーザープログラムによって要求できます。
ユーザープログラムでもこのメカニズムをいじることができます。
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
int main() {
long page_size = sysconf(_SC_PAGE_SIZE);
void *mem = mmap(NULL, page_size, PROT_READ|PROT_WRITE, MAP_GROWSDOWN|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
if (MAP_FAILED == mem) {
perror("failed to create growsdown mapping");
return EXIT_FAILURE;
}
volatile char *tos = (char *) mem + page_size;
int i;
for (i = 1; i < 10 * page_size; ++i)
tos[-i] = 42;
fprintf(stderr, "inspect mappping for originally page-sized %p in /proc... press any key to continue...\n", mem);
(void) getchar();
if (munmap(mem, page_size))
perror("failed munmap");
return EXIT_SUCCESS;
}
プロンプトが表示されたら、プログラムのpidを(ps
を介して)見つけ、/proc/$THAT_PID/maps
を調べて、元の領域がどのように成長したかを確認します。