OSのメモリ割り当てとイベントループ、コールスタック、タスクキュー


プロセスを実行するには、オペレーティングシステムがプログラム内の情報をメモリにロードする必要があります.

OSのメモリ割り当て


オペレーティングシステムのプログラム実行時の動作



プロセス実行時には、上述したように、RAMはコード、データ、hip、スタック領域を割り当て、CPUはメモリ内の各領域間を切り替えてプロセスのタスクを実行する.
単純なエンコーダでない場合、開発者は、メモリのどの領域にどのデータを格納するか、自分が使用する言語のゴミ収集操作、メモリプールがスレッド別かパブリックプールかを含むコードから実装されたIOを熟知する必要があります.
本稿では,オペレーティングシステムの観点から,プログラム実行時プロセスがメモリをどのように割り当てるかについて述べた.

メモリ割当て



RAMは大きく4つの分野に分かれている.

1.コード領域


その名の通り、実行するプログラムコードを格納する場所です.
プログラムの開始から終了までメモリでメンテナンスします.

2.データ領域


グローバル変数と静的変数を格納する場所です.
プログラムの開始から終了までメモリでメンテナンスします.

3.お尻領域


動的に割り当てられ、解除された空間.
hipはQueue構造でFIFO方式を採用している.
メモリの低いアドレスから割り当てを開始します.
スタック領域とメモリプールを共有します.

4.スタック領域


パラメータと領域変数が格納されている場所.
関数呼び出しが完了すると消えます.
メモリの高アドレスから割り当てを開始します.
スタックはその名の通りスタック構造であり,LIFO方式を採用している.
(実行時に関数内で他の関数を呼び出すことを想像すると分かりやすいです.)
臀部領域とメモリプールを共有します.

Hipとスタックを共有するメモリプール


HIPは低アドレスからメモリを割り当て,スタックは高アドレスからメモリを割り当て,メモリプールを共有する.
スペースが不足すると、オーバーフローが発生します.
臀部が臀部を侵すと、臀部があふれ、臀部が臀部を侵すと、臀部があふれ出るという.

三環とイベント環


プロセスとスレッド



オペレーティングシステムが生成する単位をプロセスと呼びます.
このプロセスでは、共有メモリに基づいて複数のタスクを生成することもできます.このときのタスク単位はThreadと呼ばれます.
したがって、各スレッドには割り当てられた個人メモリがあり、スレッドが属するプロセスが持つメモリにアクセスできます.

通常、1つのプログラムには1つのスレッドがありますが、プログラム環境によっては2つ以上のスレッドを同時に実行できます.
デフォルトでは、Pythonは単一スレッドで実行されます.
Javaはマルチスレッドで実行されます.ただし、Javaではスレッドを生成するために多くのリソースが必要になるため、JVMはアプリケーションドライバで定義したスレッド数に基づいてオペレーティングシステム上にスレッドプールを事前に作成し、開発者のニーズに応じてこれらのスレッドを使用します.
Java Springの基本方式は,スレッド内でIOが発生した場合にその操作が完了するのを待つことであり,この状態をThread Blocking状態と呼ぶ.
ブロックされた状態の書き込み数が書き込みプールの許容数を超えている場合、使用可能な書き込み数がなく、サーバに障害が発生します.

イベントループ


デフォルトでは、イベントループはJavascriptのリファレンスを参照して学習され、言語ごとに動作が異なる場合があります.
イベントの処理方法は2種類あります.
  • イベントリストプログラム+イベントハンドルスレッド(Javaのspring+tomcat?)
  • Event queue + Event loop
  • ここで、イベントループは、コールスタック、Task queue、およびバックグラウンドループを介して、CPUにタスクを割り当てる.
    Call stackは、現在CPUが動作している実行フローと見なすことができ、Task queueは次のタスクのリストである.
    Call Stackが空の場合、イベントループはTaskキューに割り当てられたタスクをCallStackに割り当てます.
    サンプルコードによる理解
    def C():
    	pass
    def B():
    	print("B is executing")
    def A():
    	B()
        print("A is executing")
        
    if __name__="__main__":
    	A()    #---- 1번
    	B()    #---- 2번
       	C()    #---- 3번
    コールスタックとテクニカルキューのステータスをプログラムの実行フロー番号で説明します.

    1番


    これは実行時の最初の実行フローであるため、イベントループの呼び出しスタックとテクノロジーキューは空です.イベントループは、A()を技術キューに送信し、その後、技術キューのA()をcallスタックに送信し、CPUは、callスタックが空であるため、callスタック内の動作を実行する.
    同時に、技術キューが再び空になるため、イベントループはB()を技術キューにアップロードする.
    callスタックで実行されたA関数は内部でB関数が再呼び出されるため、callスタックA()にB()が積み上げられ、Bが実行された後にAが実行され、callスタックは空になります.
    (これもメモリがスタック構造を使用している理由です.)

    2番


    A()関数はcallスタックで完了するので、イベントループは技術キューのB()をcallスタックに移動し、CPUはB()を実行し、イベントループはC()を空の技術キューに割り当てる.

    3番


    B()が呼スタック内ですべて実行されると、イベントループは、技術キューに次のタスクが割り当てられていないため、CPUがすべてのC()を完了すると、プロセスは終了する技術キューのC()を呼スタックに移行する.

    イベントループのタイプ


  • Single Thread Event Loop (Python, node.js)
  • イベントハンドルスレッド
  • イベントループは簡単です.予測可能動作
  • を実行する.
  • イベント発生順処理
  • .
  • タスク実行時間を延長可能

  • Multi Thread Event Loop (Vert.x)
    複数の
  • イベントハンドルスレッド
  • イベント発生順序と操作実行順序が一致しない
  • .
  • マルチコアCPU
  • を効率的に使用
  • 運転時間が比較的短い
  • Thread数が多すぎると問題になります
  • OOMエラー
  • 過剰GC
  • を引き起こす
  • Context変換コスト
  • coreは一度に1つのスレッド
  • しか実行できません.
  • Threadを実行する状態Run、Waiting、Ready、Sleep、Blockedを選択し、スレッドが持つスタック情報をカーネルのレジスタにコピーすることで、コスト
  • が発生する.
  • スレッド競合
  • 複数のスレッド間でリソースを共有する場合、スレッド間の競合によりCPUリソース使用率
  • が生じる.
    参考URL:https://all-young.tistory.com/17
    https://dgkim5360.tistory.com/entry/understanding-the-global-interpreter-lock-of-cpython
    https://shortstories.gitbooks.io/studybook/content/c774_bca4_d2b8_baa8_b378.html
    https://m.blog.naver.com/4roring/221155283089
    とても良いブログ記事です.