ランタイムシステム
ランタイムシステム (英語: runtime systemまたはrun-time system) とは、主に実行モデルの一部を実装するものである。これは、ランタイムシステムが動作中となる期間である、プログラムのランタイムライフサイクルフェーズとは異なる。大多数の言語には何らかの形のランタイムシステムがあり、その言語の観点で規定された作用を実行する順序に対する制御はランタイムシステムによって実装される。年月とともに「ランタイムシステム」という用語の意味は拡大し、実行時に動的に決定されるほぼすべての振る舞いを指すようになった。
概要
すべてのプログラミング言語において実行モデルが規定されており、多くの言語でそのモデルの少なくとも一部がランタイムシステムに実装されている。議論の余地があるものの、プログラムの直接の作用でない振る舞いはすべてランタイムシステムの振る舞いである、という風にランタイムシステムを定義する方法もある。この定義では、関数呼び出しの前にパラメーターをスタックに置くこと、ディスクI/Oの振る舞い、そして並列実行関係の振る舞い等がランタイムシステムの一部として含まれる。
この定義によると、コンパイル言語、インタープリター言語、そして組み込みドメイン固有言語を含め、基本的にすべての言語にランタイムシステムが存在する。Pthread等のスタンドアローンの実行モデルを呼び出すAPIにすらランタイムシステム、つまり実行モデルの振る舞いの実装が存在する。
ランタイムシステムに関する学術論文は、並列ランタイムシステムの実装の詳細に焦点を置くものがほとんどである。並列ランタイムシステムで注目すべき例は、よく用いられる並列プログラミングモデルであるCilkのランタイムシステムである。また、並列ランタイムシステムの作成を単純化するためにproto-runtime toolkitが作成された。
実行モデルの振る舞いに加えて、ランタイムシステムは型検査、デバッグ、またはコード生成と最適化等のサポートサービスを実行する場合もある。
ランタイムシステムは実行中のプログラムがランタイム環境と対話するための入口である。ランタイム環境には、プログラム実行中にアクセスできるステート値だけでなく、ディスクドライブや人 (キーボードを介して) 等、プログラム実行中に対話が可能なアクティブエンティティも含まれている。たとえば、環境変数は多くの基本ソフトに存在する機能であり、ランタイム環境の一部であるが、実行中のプログラムはランタイムシステムを通じて環境変数にアクセスすることができる。同様にDVDドライブ等のハードウェア機器は、プログラムがランタイムシステムを介して対話することのできるアクティブエンティティである。
RTE (ランタイム環境) の特異な使用方法は、OS (基本ソフト) 内部でRTE以外の実行を許可しない、つまり起動から電源遮断までOS全体がRTE内部で実行されるプログラムに専念することである。これ以外のコードを実行しようとする、またはアプリケーション内で不具合が発生するとRTEが破損し、このためOSが破損してすべてのプロセスが停止し、再起動が必要になる。読み込み専用メモリから起動すると、極めてセキュアでシンプルなミッション専用システムが作成される。たとえば、これによって簡単にパッチ不要で変更不可能なIoT機器が作成できる。この場合、IoT機器は他の目的 (ボットネット等) には使用できないが、再起動を強制する脆弱性の悪用を防ぐパッチを当てることもできない。
例
基本的なランタイムシステムの簡単な例を挙げると、C言語のランタイムシステムはコンパイラーが実行可能イメージに挿入する、ある特定の命令セットである。これらの命令には、プロセッサーのスタックの管理、局所変数用領域の作成、そして関数呼び出しパラメーターのスタックの頂点へのコピー等がある。どの言語的振る舞いをランタイムシステム内部のものと見なすか、どの振る舞いを「コンパイル」されたものと見なすかを判断する明確な基準はないことが多い。この場合、Cのスタックの振る舞いがランタイムシステムの一部であるとする理由は、Cのある予約語の一部とは逆に、Cが体系的であり、プログラムの実行中、スタックのステートがずっとメンテナンスされることである。この体系的な振る舞いによって、結果を計算するコードに直接翻訳される特定の予約語のセマンティクスの実装とは反対に、Cの実行モデルが実装される。
もう1つの例はランタイムシステムの性質を浮き彫りにするものだが、ランタイムシステムと対話するためのAPIを用いる場合である。そのようなAPIの呼び出しは通常のソフトウェアライブラリの呼び出しと同じに見えるが、呼び出し中のある時点で実行モデルが変化する。ランタイムシステムは、ライブラリ記述時に意図されていた言語の実行モデルとは異なる実行モデルを実装する。通常のライブラリのコードを読むには、ライブラリが書かれた言語に関する知識でライブラリの振る舞いを理解することができる。しかし、ランタイムシステムを呼び出すAPIのコードを読むには、API呼び出しが書かれた言語に関する知識だけではAPI呼び出しの振る舞いを理解することはできない。ある時点である仕組みを介して、実行モデルは、その呼び出しが書かれた言語の実行モデルであることを止め、ランタイムシステムによって実装された実行モデルへと切り替わる。たとえば、トラップ命令は実行モデルを切り替える方法の1つである。この違いは、POSIXスレッド等、APIで呼び出される実行モデルと通常のソフトウェアライブラリとを区別するものである。POSIXスレッドの呼び出しとソフトウェアライブラリの呼び出しはどちらもAPIを介するが、POSIXスレッドの振る舞いは呼び出した言語の観点から理解することができない。正確に言うと、POSIXスレッドの呼び出しは、POSIXスレッドランタイムシステムによって実装される外部の実行モデル (この実行システムはOSカーネルであることが多い) を持ち込むものである。
高度な機能
コンパイル型またはインタープリター型言語の一部では、アプリケーションコードがランタイムシステムと直接対話することを可能にするインターフェースが提供される。たとえば、Java言語のThreadクラスは他のスレッドの開始や停止等を行う (単一のスレッドで実行される) コードを実現する。通常、タスクスケジューリングやリソース管理といった、ある言語の振る舞いの核となる側面はこの方法でアクセスすることはできない。
ランタイムシステムによって実装される場合がある高レベルの振る舞いには、画面上でのテキストの描画やインターネット接続の実行等がある。こうした振る舞いはOSによっても提供される場合が多く、その場合、ランタイムシステムは、ランタイムシステム呼び出しをOS呼び出しへと翻訳する抽象化レイヤーとして実装される。これは、OSカーネル自体がランタイムシステムと見なせることと、OSの振る舞いを呼び出すOS呼び出しがランタイムシステムとの対話と見なされ得ることをも意味する。
極端な例では、ランタイムシステムはPコードマシンまたは仮想マシンといった、プロセッサーの命令セットさえも隠蔽するサービスを提供し得る。これはAWK等のインタープリター言語の多くと、Java等のマシンから独立した中間言語コード (バイトコード等) にコンパイルすることを想定した言語の一部によって採用された手法である。この手法により、言語の実装作業と様々なマシンへの移植が大幅に単純化され、しかもリフレクション等の洗練された言語機能の効率が向上する。さらに、同一のプログラムを明示的に再コンパイルをせずに任意のマシンで実行するという、ワールドワイドウェブの普及以来非常に重要になっている機能が可能になる。実行を高速化するため、ランタイムシステムの中には機械語への実行時コンパイルを実装しているものもある。
もう1つの極端な例では、物理CPU自体を特定のアセンブリ言語のランタイムシステムの実装と見なすことができる。この考え方では、実行モデルは物理CPUシステムとメモリシステムによって実装される。たとえて言うと、高級言語のシステムはそれ自体が別の言語で実装される。こうしてCPU自体 (実際にはプログラムカウンターの増加や命令のスケジューリングを決定するCPU内部のデジタル回路構造) が最下層のランタイムシステムの役目を負った、ランタイムシステムの階層構造が生み出される。
ランタイムシステムの現代的側面は、Pthreadのミューテックス構造やOpenMPの並列セクション構造等の並列実行の振る舞いである。そのような並列実行の振る舞いを持つランタイムシステムは、proto-runtime手法に従ってモジュール化される場合がある。
歴史
初期のランタイムシステムで特筆すべきは、BASICとLispのインタープリターである。これらの環境にはガベージコレクターも含まれていた。Forthは中間言語コードにコンパイルすることを想定した言語の初期の例である。Forthのランタイムシステムは、Forthのコードを解釈する仮想マシンであった。もう1つの有名な例は、理論上のものであるが、ドナルド・クヌースのMIXコンピューターである。
動的メモリ確保に対応したCや後続の言語では、プログラムのメモリプールを管理するライブラリもランタイムシステムに含まれていた。
オブジェクト指向プログラミング言語では、ランタイムシステムは動的型検査とメソッド参照の解決も果たすことが多かった。