GNU/Linux >> Linux の 問題 >  >> Linux

Linux でスタック割り当てはどのように機能しますか?

スタックメモリの制限が割り当てられていないようです(とにかく、無制限のスタックではできませんでした)。 https://www.kernel.org/doc/Documentation/vm/overcommit-accounting によると:

<ブロック引用>

C 言語スタックの成長は、暗黙的な mremap を行います。絶対的な保証が必要で、エッジの近くで実行する場合は、必要になると思われる最大サイズのスタックを mmap する必要があります。典型的なスタックの使用法では、これは大した問題ではありませんが、本当に気にする場合はまれなケースです

ただし、スタックの mmap はコンパイラの目標になります (そのためのオプションがある場合)。

編集:x84_64 Debian マシンでいくつかのテストを行った後、システム コールなしでスタックが大きくなることがわかりました (strace によると)。 )。したがって、これはカーネルが自動的にそれを拡張することを意味します (これが上記の「暗黙的」の意味です)、つまり、明示的な mmap なしで /mremap

これを確認する詳細な情報を見つけるのは非常に困難でした。 Mel Gorman による Understanding The Linux Virtual Memory Manager をお勧めします。答えはセクション 4.6.1 ページ フォールトの処理 にあると思います。 、「リージョンは無効ですが、スタックのような拡張可能なリージョンの横にあります」という例外と、対応するアクション「リージョンを拡張してページを割り当てる」があります。 D.5.2スタックの拡張も参照してください .

Linux メモリ管理に関するその他の参考文献 (ただし、スタックについてはほとんど何もありません):

  • メモリに関するよくある質問
  • Ulrich Drepper によるメモリについてすべてのプログラマが知っておくべきこと

編集 2:この実装には欠点があります。まれに、スタックが制限よりも大きくなる場合でも、スタック ヒープの衝突が検出されないことがあります。その理由は、スタック内の変数への書き込みが、割り当てられたヒープ メモリで終了する可能性があるためです。この場合、ページ フォールトは発生せず、カーネルはスタックを拡張する必要があることを認識できません。 gcc-help リストで開始した GNU/Linux での Silent stack-heap collision のディスカッションで私の例を参照してください。これを避けるために、コンパイラは関数呼び出し時にコードを追加する必要があります。これは -fstack-check で実行できます GCC 用 (詳細については、Ian Lance Taylor の返信と GCC のマニュアル ページを参照してください)。


Linux カーネル 4.2

  • mm/mmap.c#acct_stack_growth は、segfault するかどうかを決定します。 rlim[RLIMIT_STACK] を使用しています これは POSIX gerlimit(RLIMIT_STACK) に対応します
  • arch/x86/mm/fault.c#do_page_fault は、最終的に acct_stack_growth を呼び出すチェーンを開始する割り込みハンドラです。
  • arch/x86/entry/entry_64.S は、ページ フォールト ハンドラを設定します。その部分を理解するには、ページングについて少し知っておく必要があります:x86 ページングは​​どのように機能しますか? | |スタック オーバーフロー

最小限のテスト プログラム

次に、最小限の NASM 64 ビット プログラムでテストします。

global _start
_start:
    sub rsp, 0x7FF000
    mov [rsp], rax
    mov rax, 60
    mov rdi, 0
    syscall

ASLR をオフにして、環境変数を削除してください。これらはスタックに置かれてスペースを占有します:

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
env -i ./main.out

制限は私の ulimit -s をわずかに下回っています (私にとっては8MiB)。これは、環境に加えて最初にスタックに置かれた追加の System V 指定データが原因のようです。スタック オーバーフロー

本気ならTODOでスタックトップから書き始めてダウンする最小限のinitrdイメージを作ってQEMU+GDBで実行する。 dprintf を入れる スタックアドレスを出力するループ、および acct_stack_growth のブレークポイント .輝かしいものになるでしょう。

関連:

  • https://softwareengineering.stackexchange.com/questions/207386/how-are-the-size-of-the-stack-and-heap-limited-by-the-os
  • Linux プロセスのスタック メモリはどこから割り当てられますか? | |スタック オーバーフロー
  • Linux スタックとは? | |スタック オーバーフロー
  • Python の最大再帰深度とは?それを増やす方法は?スタック オーバーフローについて

デフォルトでは、最大スタック サイズはプロセスごとに 8MB に設定されています。
ulimit を使用して変更できます :

デフォルトを kB で表示:

$ ulimit -s
8192

無制限に設定:

ulimit -s unlimited

現在のシェルとサブシェル、およびそれらの子プロセスに影響を与えます。
(ulimit はシェル組み込みコマンドです)

使用中の実際のスタックアドレス範囲を表示するには:
cat /proc/$PID/maps | grep -F '[stack]'


Linux
  1. Linuxでメモリキャッシュをクリアする方法

  2. Awkの「!a [$ 0] ++」はどのように機能しますか?

  3. Linux –負荷平均は最新のCPUでどのように機能しますか?

  1. NGINXとは何ですか?それはどのように機能しますか?

  2. Linuxのメモリ割り当てはノンブロッキングですか?

  3. Linux での ZFS は動作しますか?

  1. スティッキービットはどのように機能しますか?

  2. Ssh – Tcp-keepaliveはSshでどのように機能しますか?

  3. 「ls」コマンドは Linux/Unix でどのように機能しますか?