.NET Coreアプリケーションの構造と実行モデル

この記事では、.NET Coreアプリケーションをダウンロードして実行するために必要な.NET Core 2.0プラットフォームのコンポーネントと、2つの可能な展開タイプのアーティファクトについて説明します。

テキストはボリュームがあり、次の目的で設計されています。


この記事では、SDK(dotnet CLI)を使用してアプリケーションを作成するプロセスについては触れませんが、このライブラリはマネージアセンブリであるため、この情報はSDKの動作を理解するのに役立ちます。 .NET Coreで実行されます。

実行プロセスの例はWindowsで説明されていますが、他のOSでも同じ原理で動作します(実行可能ファイルとネイティブライブラリのさまざまな拡張を考慮に入れて)。

0.ペイフォープレイ




すべての.NET開発者は、クレードルから知っています。.NETアプリケーションを実行するには、.NET Frameworkをターゲットコンピューター、つまりCLR + BCLにインストールする必要があります。

BCLはGACにあり、そこからアプリケーションが必要な依存関係をダウンロードします。

.NET Coreアーキテクチャは一般的には同じように見えます:.NET Core = Core CLR + Core FX(BCLの新しい名前)、これらのコンポーネントの解決方法、およびランタイム(CLR)のロード方法が異なります。 .NET Coreの.NET FrameworkのマネージアセンブリMyApp.exeのヘッダーの代わりに、MyApp.exe自体がネイティブのコアCLRブートプログラムです。

.NET Coreでは、コンパイル段階で定義するすべてのプログラムコンポーネントはアプリケーションの依存関係(Core CLR、JITを含む)であり、.NET Coreインフラストラクチャはパッケージとして扱います。 このようなパッケージはassetと呼ばれ、NuGetパッケージまたは通常のファイルのいずれかです。

NuGetを介して出荷されるコンポーネントの例:


アプリケーションを起動するときのアンパック形式のこれらの依存関係は、特定のディレクトリ(.NET Coreフレームワークフォルダー-Core FX、アプリケーションフォルダー、または任意のNuGetキャッシュ)のいずれかになければなりません。

このモデルのおかげで、.NET Coreアプリケーションは恐ろしく多数の小さなモジュールで構成されていますが、これは不要な依存関係の量を減らすために行われます。

このアプローチは、ペイフォープレイと呼ばれます。 つまり、アプリケーションは必要な機能のみをロードしますが、そのような各機能は個別のアセンブリに含まれています。

1. FDD対SCD


.NET Coreアプリケーションに2種類の展開があります


ポータブル(FDD)アプリケーションは、従来の.NET Frameworkアプリケーションに似ています。 この場合、.NET Coreフレームワークの特定のバージョン(共有フレームワーク、.NET Coreランタイム、再配布という用語も使用されます)をターゲットコンピューターに配置する必要があり、起動時に、ホストプロセスはフレームワークフォルダーからCore CLR、Core FXを読み込みます。

スタンドアロン(SCD)アプリケーションでは、実行用のすべてのコンポーネント(CoreCLR、CoreFX)、およびサードパーティのライブラリ、つまり絶対的にすべての依存関係が、アプリケーション自体(ほとんどの場合1つのフォルダー)と共に配信されます。

スタンドアロンアプリケーションは特定のOSおよびアーキテクチャ(Windows 7 x64またはOSX 10.12 x64など)に関連付けられていることを理解することが重要です。 このような識別子は、 ランタイム識別子(RID)と呼ばれます。 各OS /アーキテクチャには独自のバージョンのCore CLRライブラリ(およびその他のネイティブコンポーネント)があるため、スタンドアロンアプリケーションの場合、コンパイル段階で、RuntimeIdentifierプロパティでターゲットシステム(RID)のパラメーターを指定する必要があります。

このようなアプリケーションは、.NET Coreがインストールされているかどうかに関係なく、特定のOS /アーキテクチャを持つコンピューターで動作します。

2. .NET Coreランタイム(共有フレームワーク)


ポータブルアプリケーションを実行するには、少なくとも1つの.NET Core Runtime (共有フレームワーク)をターゲットマシンにインストールする必要があります。

.NET Core RuntimeはC:\ Program Files \ dotnetフォルダーにインストールされます



フレームワークのファイルは、 C:\ Program Files \ dotnet \ sharedフォルダーに保存されます。

.NET Core Runtimeの主要コンポーネント:


.NET Core Runtimeのインストール時のファイル構造

フレームワークのいくつかのバージョンをインストールできます。



ポータブルアプリケーションを実行するには、dotnet.exeホストプロセスを開始し、マネージアセンブリへのパスを引数として渡す必要があります。

「C:\ Program Files \ dotnet」がPATH環境変数の値に追加されるため、ポータブルアプリケーションをコマンドラインから起動できるようになりました。

> dotnet path/to/App.dll

アプリケーションフォルダー([AppName] .dllがある場所)には、ファイル[AppName] .runtimeconfig.jsonが含まれている必要があります。 ポータブルアプリケーションの実行に使用するフレームワークの名前とバージョンを示します。 例:

MyApp.runtimeconfig.json
 { "runtimeOptions": { "framework": { "name": "Microsoft.NETCore.App", "version": "2.0.0" } } } 

このファイルは、ポータブルアプリケーションに必要です。

上記の構成では、ランタイムコンポーネントはC:\ Program Files \ dotnet \ shared \ Microsoft.NETCore.App \ 2.0.0フォルダーから読み込まれます。

3.ポータブル(FDD).NETコアアプリケーション構造


Portable .NET Coreアプリケーションは、次の必須ファイルで構成されています。


ドキュメンテーション

.NET Coreプラットフォームの異なるバージョン用の同じポータブルアプリケーションのアーティファクト:



ファイル数の減少は、多くのライブラリがCore FX 1.0に存在しなかったため、通常の依存関係としてアプリケーションの一部として使用されたためです。 Core FX 2.0では、これらのアセンブリが追加されたため、アプリケーションには付属しなくなりましたが、フレームワークフォルダーから取得されます。

4.スタンドアロン(SCD).NETコアアプリケーション構造


ポータブル(FDD)アプリケーションと同じですが、すべてのランタイムコンポーネント(CoreCLR、CoreFX)と、独自のdotnet.exe マルチプレクサーが追加され、[AppName] .exeに名前が変更されました。 バージョン2.0までの.NET Coreの場合、スタンドアロンアプリケーションを起動するためのマルチプレクサーは、C:\ Program Files \ dotnet.exe(同じファイル、名前のみ変更)と同じです。 .NET Core 2.0の場合、Microsoft.NETCore.DotNetAppHost NuGetパッケージのマルチプレクサーが使用されます。 パッケージにはapphost.exeファイルが1つ含まれており、コンパイル時にアセンブリ名(MyApp.dll)に「縫い付け」られ、ファイル自体の名前がMyApp.exeに変更されます。 スタンドアロンアプリケーションが起動すると、実行可能ファイル(MyApp.exe)と実行可能なアセンブリの名前(MyApp.dll)の「バインド」がチェックされます。

異なるバージョンの.NET Coreプラットフォーム用の同じスタンドアロンアプリケーションのコンテンツ:



ポータブルアプリケーションとは逆の画像が観察されます-Core FXが増えるほど、アプリケーションに付属するファイルが増えます。

展開タイプの考慮事項


5.ランタイム構成ファイル


ファイル[AppName] .runtimeconfig.jsonおよび[AppName] .deps.jsonはランタイム構成ファイルと呼ばれます (* .deps.jsonは依存関係マニフェストファイルと呼ばれます)。 これらはコンパイルプロセス中に作成され、dotnet.exeを実行してアプリケーションを実行するために必要なすべての情報が含まれています。

[AppName] .runtimeconfig.jsonでは、 .NET Coreランタイム名前とバージョンが設定され(フレームワークの検索時にパッチバージョン( SemVer )が考慮されるかどうかも示されます)、Core CLRの操作パラメーター(ガベージコレクターの操作モード)も設定されます。 このファイルは、ポータブルに必要であり、スタンドアロンアプリケーションには必要ありません。

dotnet.exe([AppName] .exe)は、 [AppName] .deps.jsonファイルを使用して、起動時にすべてのアプリケーションの依存関係の絶対パスを決定します。

構造[AppName] .deps.json


6. Portable .NET Coreアプリケーションを起動するプロセス


ターゲットコンピューターには、起動するアプリケーションの構成と一致する.NET Core Runtimeがインストールされている必要があります。

6.1。 アプリケーションの起動
コマンドラインからマルチプレクサー(muxer)を使用して実行(すべてのOSで同じ)。

> dotnet path\to\MyApp.dll

dotnet.exe-名前がcorehost.exeに変更されました 。このプログラムは.NET Coreアプリケーションのホストプロセスであり、そこから開始プロセスが開始されます。

6.2。 [corehost] Framework Resolver(hostfxr.dll)の検索とダウンロード
この時点で、dotnet.exeは[own directory] / host / fxr / folderに移動します 。 ポータブルアプリケーションの場合、このライブラリは共有フォルダーC:\ Program Files \ dotnet \ host \ fxr \ [FXR version] \ hostfxr.dllにあります。 複数のバージョンがある場合、dotnet.exeは常に後者を使用します。

hostfxr.dll (Framework Resolver)を読み込ん後、起動プロセスこのライブラリのスコープに入ります。

6.3。 [hostfxr]ランタイムの決定(スタンドアロン、マルチプレクサ、スプリット/ FX)
hostfxrの最初のタスクは、ホストプロセスが動作するモード決定することです。したがって、アプリケーションのタイプ(ポータブル(FDD)またはスタンドアロン(SCD))を決定します。 ポータブル(FDD)モードでは、実行中のアプリケーションかSDKコマンドかを判別します。

実行のタイプ(プログラムまたはSDKコマンド)の決定は次のとおりです。

-引数の中に値が.dllまたは.exeで終わる引数がある場合-起動プロセスは、指定されたファイルの実行モードで続行します。 そのような引数がない場合、制御はSDKに転送されます。 これを行うには、dotnet.dll(ポータブルアプリケーションとして)が[own directory] ​​\ sdk \ [version]フォルダー(存在する場合)から起動され、プロセスの現在のホストの引数がこのアセンブリに渡されます。

また、ポータブル(FDD)アプリケーションの場合、hostfxrは、コンポーネントが実行のためにロードされるフレームワーク(.NET Core Runtime)を定義します。

検証アルゴリズムは非常に単純です-[AppName] .exeマルチプレクサー(この場合はdotnet.exe)が起動されたフォルダーにcoreclr.dllまたは[AppName] .dllがない場合、ポータブルアプリケーション。 これらの2つのファイルのいずれかが存在する場合、次に検証が行われます-ポータブル(分割/ FX)アプリケーションまたはスタンドアロン。 [AppName] .dllが存在する場合はスタンドアロン、そうでない場合はポータブル(分割/ FX)。

スプリット/ FXモードはxunitの起動に使用され、アプリケーションが独自のhostfxr.dllでポータブルとして起動することを意味します。 このモードは.NET Core 2.0では使用されません。

ポータブルアプリケーションは、いわゆるExecモードで起動することもできます
これを行うには、開始コマンド最初の引数として exec C:\> dotnet exec ...を含める必要があります。

このモードで起動すると、構成ファイルへのパスを明示的に指定できます。
--depsfile <PTH>
--runtimeconfig <PTH>
これは、アプリケーションフォルダー内のファイルの代わりに使用されます。

6.4。 [hostfxr] .NET Coreランタイムの定義
Hostfxrは、まずdepsおよびruntimeconfig構成ファイルを検出してロードします。 引数で何もオーバーライドされていない場合、これらのファイルはアプリケーションフォルダーから取得されます。

現在の段階で、hostfxrは( 構成ファイルに従って )アプリケーションがポータブルかスタンドアロンかを判別します。

構成ファイルを読み込んでモードを決定した後、hostfxr はフレームワークフォルダー (.NET Core Runtime)を定義します

これを行うには、hostfxrはまず共有フォルダーにインストールされているバージョンを判断し、[AppName] .runtimeconfig.jsonの値を考慮して 、このリストからリリースバージョンを選択します

バージョンを選択するとき、 「候補Fxなしでロールフォワード」パラメータが考慮されます。これは、指定されたバージョンとマシンで使用可能なバージョンのコンプライアンスの重大度を示します。

6.5。 [hostfxr] hostpolicy.dllの検索とダウンロード
現在の段階では、すべてがランタイムコンポーネントのパスを決定する準備ができています。 ホストライブラリと呼ばれるhostpolicy.dllライブラリは、このタスクに関係しています。

hostpolicy.dllの検索プロセスは、さまざまな場所の順次チェックで構成されています。 ただし、最初に、hostpolicyのバージョンはフレームワークのdepsファイルから決定されます(例:C:\ Program Files \ dotnet \ shared \ Microsoft.NETCore.App \ 2.0.0 \ Microsoft.NETCore.App.deps )。 このファイルでは、 Microsoft.NETCore.DotNetHostPolicyという名前のパッケージが見つかり、そのバージョンが取得されます。

次に、.NET Core Servicingフォルダー( Windowsの場合 -フォルダーC:\ Program Files [(x86)] \ coreservicing \ pkgs )。 そのようなファイルが見つかった場合、将来使用するためにダウンロードされます。

前の手順でファイルが見つからなかった場合、hostpolicy.dllはフレームワークフォルダーにあります。

hostpolicy.dllが定義されると、hostfxrはこのライブラリをロードし、 制御を転送します

6.6。 [hostpolicy]依存関係リストの定義
hostpolicy.dllライブラリ 、すべてのアプリケーション依存関係の絶対パスを決定する役割を果たします。

まず、hostpolicy Dependencies Resolverと呼ばれるコンポーネントを作成します。このコンポーネントは、フレームワークファイルとアプリケーションファイルの2つのdepsファイルダウンロードします。

最初に、リストがフレームワークのdepsファイルからロードされ、CoreCLRやCoreFXライブラリなどの依存関係が定義されます。 次に、アプリケーションのdepsファイルからのリストは、アプリケーションのアセンブリとその依存関係を示します。

各依存ファイルについて、Dependency Resolver 指定されたruntimeTargetのすべての依存関係をリストします

各パッケージについて、runtimeTargets(RID固有の依存関係)のすべてのセクションからのファイルのリストが最初にコンパイルされ、次にネイティブおよびランタイムのセクションからのすべてのファイルのリストがコンパイルされます。 条件付き形式のすべての依存関係の相対パスのこのような組み合わせリスト
パッケージID-RID-資産タイプ(ランタイム、ネイティブ)-ファイルパスはターゲット資産と呼ばれます。

依存関係ファイルのこれら2つのリスト(RIDおよび非RID)がコンパイルされた後、 ターゲットライブラリの調整と呼ばれるプロセスが実行されます。 これは、ライブラリセクションの各パッケージについて、通常のファイルでオーバーライドされる必要があるRID固有のファイルがあるかどうかがチェックされるという事実から成ります。

6.7。 [hostpolicy] TPA、コアCLR、およびCLR Jitパスの定義
次に、依存関係リゾルバーは、マネージアセンブリファイルの絶対パス(アプリケーションの依存関係)を一覧表示します。 このリストはTPA(Trusted Platform Assemblies)と呼ばれ、AppDomainを構成するためにコアCLRに渡されます。 依存関係ファイルの残りの場所(coreclr、corejitを除く)のディレクトリの絶対パスのリストもコンパイルされます。

マネージアセンブリの絶対パスは、プローブパスでファイルを検索することで決定されます。 デフォルトでは、フレームワークフォルダーとアプリケーションフォルダーの2つがあり、それらはdepsファイルの場所に基づいています。 パスを追加することもできます。

1)引数--additionalprobingpathを渡す
--additionalprobingpath %UserProfile%\\.nuget\\packages

2)ファイル[AppName] .runtimeconfig.jsonで指定します(優先度は引数の優先度よりも低い)、たとえば

 { "runtimeOptions": { "additionalProbingPaths": [ "C:\\Users\\username\\.nuget\\packages" ] } } 

ファイルの存在は、フレームワークとアプリケーションフォルダーで確認されます(対応するdepsファイルで指定されている場合)。これらのディレクトリはNuGetパッケージのキャッシュと見なされるため、パスに関する他のディレクトリの相対パスは考慮されません。

検索順序:


アプリケーションdepsファイルが欠落している場合、アプリケーションフォルダーの拡張子が.ni.dll、.dll、.ni.exe、.exeのすべてのファイルがTPAに入ります。

TPAリストをコンパイルした後、CoreCLRおよびCLRJitパスが決定されます。

アプリケーションdepsファイルがない場合、dotnet.exeは最初に[app directory] ​​\ lib \でこれらのライブラリを見つけようとします。 通常の実行では、パスはフレームワークフォルダーから取得されます(相対パスを破棄し、ファイル名のみを取得します)。

次のCoreCLR設定が設定されます。


次に、制御はcoreclr.dllに渡されます。

7. Startalone(SCD).NET Coreアプリケーションの起動プロセス


スタンドアロンアプリケーションを起動するプロセスは、初期段階と、デフォルトでアプリケーションフォルダに配置する必要があるコンポーネントの場所のみがポータブルと異なります。

7.1。 アプリケーションの起動
ネイティブマルチプレクサーMyApp.exeを実行して実行されます。 .NET Core <2.0では、このマルチプレクサは名前が変更された汎用dotnet.exeマルチプレクサです。 .NET Core 2.0以降では、別個のapphost.exeマルチプレクサー(dotnet.exeのわずかに変更されたバージョン)が使用されます。

このファイル(apphost.exe)は、Microsoft.NETCore.DotNetAppHostパッケージのNuGetを介して配信されます。
ファイルにはテキストプレースホルダーが含まれています(その値は、foobar文字列のSHA-256ハッシュです)。
dotnet build SDK コマンドが実行されると、プレースホルダー値が起動されているアセンブリの名前(MyApp.dllなど)に変更され、apphost.exeの名前がMyApp.exeに変更されます。 したがって、実行可能ファイルはアセンブリにリンクされます。 .NET Core> = 2.0アプリケーションを実行すると、この「バインディング」が最初にチェックされます。

7.2。 起動プロセス
ポータブルアプリケーションの場合と同じですが、depsファイルが1つしかなく、すべての依存関係がアプリケーションフォルダーまたは指定された--additionalprobepathsで検索される点が異なります。

8.要約する


Source: https://habr.com/ru/post/J327686/


All Articles