見出し画像

コンピューターの進化の歴史から読み解く。プログラムとは翻訳だらけ (3): .NET を動かしてみる

.NET 6 の起動から翻訳の過程を探る

さて前回は、Java と .NET のお話まで来ました。
その際に、CPU が理解できる機械語に翻訳されるまでの幾つもの階層がある事をお話してきました。機械語も電気信号に翻訳されるんですけどね。

そろそろ実際にモノを動かしてその流れを探っていきましょうか。
軽めに行きます。.NET や Java がどう動いているのかは、別途お話します。こちらの参考情報の先にあるPowerPointのスライドではある程度説明しています。

実際に動かしてみよう! - HelloWorld

ここでは、執筆時点 (2022年2月8日) で最新の .NET 6 を使って Hello World のコードを動かしてみます。
動かすための環境で追加費用は不要です。作業時間も 5分くらいですかね。Visual Studio Code をここでは使いますが、.NETであれば Visual Studio の方がよりデラックスな環境でコードが書けます。

Visual Studio Code を使用して .NET コンソール アプリケーションを作成する - .NET | Microsoft Docs

さて、上記のチュートリアルを一部抜粋してここにも記しましょう。
Visual Studio Code の作成するコードのテンプレート。これを以下の様に変更します。何のことはない、Console.ReadLine(); でユーザーの入力待ちにしているだけです。そのままですと、あっという間に実行されて処理が終わってしまうので。

Console.WriteLine("Hello, World!");
Console.ReadLine();
Console.WriteLine("completed!");

これを実行して、2行目で止まっている様がこちらになります。

"Hello, World!"
とターミナルに出力された後に、入力待ちになっていますね。

ここでそのプログラムがどうなっているかを見ていきます。
.NET の場合は、テキストファイルである Program.cs をコンパイルすると bin フォルダーにバイナリーのファイルが出来ます。つまりメモ帳などのテキストエディターで開いても、意味が分からない状態のファイルが出来ます。

ここにあるファイルですが。
NET6 というのはフォルダー名なんですね。紛らわしい😅私がコンソール アプリケーションに名前を付けていないので、フォルダー名が使われます。フォルダー名大事ですね。
ここで大事なのが exedll のファイルです。これが実行時に使われます。Windows においてプログラムとして実行されるのは exe ファイルです。executable の省略形です (確か)。
図にすると以下の様になります。

さて、いろいろと言葉が出てきますね。
この図でいうとアセンブリ が exe や dll です。あれ? exe や dll はWindows の場合、プログラムの実行の際に使われるとお話をしました。.NETの場合はツールでコンパイルしても通常はアセンブリが出来るだけで、ネイティブコード、つまり、機械語にするためには別の仕組みが必要なんです。そうです。Java では Java 仮想マシン。.NET では Common Language Runtime と呼ばれる機構です。

共通言語ランタイム (CLR) の概要 - .NET | Microsoft Docs

JITコンパイラーの話を前回しましたね。この図にもあります。アセンブリと実行環境が疎結合になっているので、アセンブリが異なる OSの上でも動くわけですよね。

OS がプログラムを起動している様をのぞいてみよう - Process Explorer

Windows には、OSがどの様にアプリケーションを管理しているのかを可視化してくれる有名なツールがあります。
Process Explorer です。

プロセス エクスプローラー - Windows Sysinternals | Microsoft Docs

これは Windows に標準で搭載されていません。下記のサイトからダウンロードして起動させます。Process Explorer 以外のツールも沢山あります。今後のWindowsでのエンジニアリングライフを助けてくれるものばかりです。Windowsをお使いの方は是非ダウンロードしてください。

Sysinternals Suite - Windows Sysinternals | Microsoft Docs

zip 解凍すると Process Explorer があります。殆どの方が x64 の 64bit OSをお使いだと思います。procexp64.exe を起動させます。

見た目は多少違うかもしれません。私は頻繁にこのツールを使っていますので、私の使いやすいようにフォントなどを変えています。

ありました。NET6.exe が。

私がターミナルで実行したコマンドは、以下なんです。NET6.exe の実行は指定していませんね。

dotnet run

dotnet コマンドは、引数が幾つもあります。

dotnet コマンド - .NET CLI | Microsoft Docs

dotnet run は、そのうちの一つですね。

dotnet run コマンド - .NET CLI | Microsoft Docs

ドキュメントの引数を見ながら、実際に自分で引数の指定をしてみると、その挙動をよく理解できます。

さて、Process Explorer に戻って NET6.exe をダブルクリックしてプロパティを見ます。


いやー凄いですね!
このアプリケーション一つを動かすだけで、実に様々な情報が見えますね! OS って本当に多くのアプリケーションを管理しているんですね。
ちなみに、今の私の Windows 11 のタスクマネージャーを起動すると、CPUの欄に以下の情報が見えます。なんと 296 のプロセス、つまり、アプリケーションが動いています! 結構な数ですよね😊

Process Explorer に戻りましょう。
注目すべきはこのスレッドがどの様に管理されているのか? です。これを見ると NET6.exe の後に +0x13d50 という数字が付与されています。これはメモリーの中のアドレス、つまり位置情報を指します。2行目に coreclr.dll の文字が見えます。
これが .NET アプリケーションの Common Language Runtime の実体です! これを見ると MetaDataGetDispenser というCLRの関数が呼ばれていることがわかりますね。

この .NET のアプリケーションの起動についてはドキュメントに記載があります。興味がある方は読んでください。

マネージド実行プロセス | Microsoft Docs

ここで [Stack] というボタンを押してみてください。

来ましたね! OS の深淵が見えます。
ntoskrnll.exe が呼ばれてますね。OSにはカーネルというOSそのものがあります。その exe が呼ばれています。


この Process Explorer は本当によく出来ています。
NET6.exe は dotnet.exe に呼ばれています。コンソールアプリケーションとして開発しましたので、cmd.exe から呼ばれて動くわけです。

それらから、OS のカーネルへの呼び出しが行われています。NET6.exe の Stack には、NtReadFile という関数も見えますし。今、ユーザーの入力を待っていますから。KeWaitForSingleObject という関数名も見えますね!
この関数をネットで検索してみてください。Windows の API として定義されていることがわかります。例えば KeWaitForSingleObject の場合は、こちらになります。

KeWaitForSingleObject function (wdm.h) - Windows drivers | Microsoft Docs

因みに今の状態で cmd.exe の Stack を見ると、更に積みあがっているのがわかりますねぇ。正に Stack! メモリ上のアドレスが微妙に違うところもあります。これは .NET のセキュリティ機構やアプリケーションの管理などに絡むお話です。

見えてくる事 - 全てがOSの管理下に置かれている

私たちがどんなに優れたコードを書いても、それは原則として OSの管理下に置かれます。OSの管理から逃れたい場合は、ハードウェアのドライバーとしてのコードを書くか、あるいはOS そのものの様なコードを直接 CPU に対して書くかしないといけません。そしてそれは Java や .NET で記述はでき前ん (殆どの場合)。OSの下に行くには アセンブラや機械語で書く必要があります。C言語はUNIXというOSのソフトウェアの記述のために生まれてきたというお話もしました。実は C言語というのは、どんなOSの上でも動く事が多いですし、OSそのものの記述も出来るんですね。しかも高速に動く。高級言語と分類されるプログラム言語の中で一番ポータビリティ、つまりOSやハードウェアを超えての可搬性が高いとも言えます。

これらは歴史を紐解くと見えてくると思います。


いいなと思ったら応援しよう!