web-dev-qa-db-ja.com

「自動スタック拡張」とは何ですか?

getrlimit(2) は、manページで次のように定義されています。

RLIMIT_ASプロセスの仮想メモリ(アドレス空間)の最大サイズ(バイト単位)。この制限は、この制限を超えるとエラーENOMEMで失敗するbrk(2)、mmap(2)、およびmremap(2)の呼び出しに影響します。また、自動スタック拡張は失敗します(そして代替スタックがない場合、プロセスを強制終了するSIGSEGVを生成しますsigaltstack(2)を介して利用可能になりました)。値が長いため、32ビット長のマシンでは、この制限は最大2 GiBであるか、このリソースは無制限です。

ここで「自動スタック拡張」とはどういう意味ですか? Linux/UNIX環境のスタックは必要に応じて拡張されますか?はいの場合、正確なメカニズムは何ですか?

13
loudandclear

はい、スタックは動的に成長します。スタックはメモリの一番上にあり、ヒープに向かって下に向かって成長します。

--------------
| Stack      |
--------------
| Free memory|
--------------
| Heap       |
--------------
     .
     .

新しい関数が呼び出されると、ヒープは(mallocを実行するたびに)上向きに成長し、スタックは下向きに成長します。ヒープは、プログラムのBSSセクションのすぐ上にあります。つまり、プログラムのサイズと、ヒープ内のメモリをすべて分類する方法も、そのプロセスの最大スタックサイズに影響します。通常、スタックサイズは無制限です(ヒープ領域とスタック領域が一致するか、スタックオーバーフローとSIGSEGVが発生する上書きされるまで:-)

これはユーザープロセス専用です。カーネルスタックは常に固定されています(通常は8KB)。

1
Santosh

正確なメカニズムは、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を調べて、元の領域がどのように成長したかを確認します。

8
cdleary