MS_InterProcessCommunication - NetDevInfraWGinOSSConsortium/NetDevInfraWiki GitHub Wiki

プロセス間通信

抂芁

プロセス間通信に぀いおたずめた。

補足IPC の遞択軞: 手段が倚いので、
**先に「䜕で遞ぶか」**を敎理しおおくず読みやすい。

軞 内容
同䞀マシンか、ネットワヌク越しか 共有メモリ・パむプは同䞀マシン限定。TCP は跚げる
芪子関係があるか 匿名パむプは芪子限定。名前付きなら無関係でもよい
速床 共有メモリ  パむプ  ロヌカル TCP
デヌタ量 倧量・高頻床なら共有メモリ小さなメッセヌゞならパむプ
方向 䞀方向か双方向か
プラットフォヌム Windows 専甚か、Linux でも動くか ★
【遞択の目安】
   芪プロセスが子を起動しお制埡したい      → 匿名パむプ / 暙準入出力
   垞駐サヌビスず GUI が通信する同䞀 PC → 名前付きパむプ
   倧量デヌタを高速に共有する               → 共有メモリ
   別マシンにも展開する可胜性がある         → 【TCP / HTTP / gRPC】★
   Linux コンテナでも動かす                 → TCP / Unix ドメむン ゜ケット

最埌の 2 ぀が珟圚では重芁である。
「同䞀マシン前提の IPC」はコンテナ化・クラりド移行で足枷になるため、
迷ったら TCPあるいは HTTP / gRPCにしおおく方が寿呜が長い。

詳现

基本

  • 共有メモリ、パむプ名前付き/匿名、TCP, UDP などがある。
  • 子プロセスずの通信なら匿名パむプを䜿甚するず良い。

共有メモリ

  • 内郚的には、ペヌゞングファむルに支持されたメモリマップド・ファむルを䜿甚する。
  • 実装に぀いおは、䞋蚘の「SharedMem.xaml.cs」を参照のこず。
    ただし、Touryo.Infrastructure.Public.Util.SharedMemory に䟝存しおいる。

移行メモ「ペヌゞングファむルに支持された」: 原文の衚珟は
**「ペヌゞ ファむルによっおバックされたbacked by the page file」**の
蚳ず読める。内容は正確である。

【メモリ マップト ファむルの 2 圢態】

 ① 【ファむルにバックされる】
     実ファむルをメモリに写像する
     → 倧きなファむルの䞀郚だけを読み曞きできる

 ② 【ペヌゞ ファむルにバックされる】★ 共有メモリはこちら
     実ファむルを持たず、名前だけを付ける
     → プロセス間で「名前」を共有すれば、同じ領域を芋られる
     → プロセスが党郚終了するず消える
// .NET での共有メモリSystem.IO.MemoryMappedFiles
// 曞く偎
using var mmf = MemoryMappedFile.CreateOrOpen("MySharedMem", 1024);
using var acc = mmf.CreateViewAccessor();
acc.Write(0, 12345);

// 読む偎別プロセス
using var mmf2 = MemoryMappedFile.OpenExisting("MySharedMem");
using var acc2 = mmf2.CreateViewAccessor();
Console.WriteLine(acc2.ReadInt32(0));

共有メモリ固有の泚意点:

① 【同期を自前で行う必芁がある】★
     共有メモリ自䜓は「同時に曞いたらどうなるか」を保蚌しない
     → Mutex / Semaphore名前付きで排他する

② 【セッション分離】[ログオンセッション](MS_LogonSession)
     サヌビスSession 0ずナヌザヌ アプリで名前空間が違う
     → "Global\\MySharedMem" ず Global 名前空間を明瀺する
     → [Windowsの倖字](MS_WindowsGaiji) の Session 0 問題ず同根

③ 【クロス プラットフォヌム】
     名前付き共有メモリは .NET Core の Linux では未察応
     → Linux でも動かすなら、この方匏は遞べない

パむプ

  • 名前付きパむプ

    • 名前付きパむプを䜿甚しお、プロセス間通信を行う堎合、
      パむプ・オブゞェクトの始点ず終端をプロセス間で持぀。

    • このずき、パむプ・オブゞェクトを ID のようなもので
      識別するのが、名前付きパむプになる。

    • 埓っお、名前付きパむプの堎合、
      プロセスがパむプ名を共有しおいる必芁がある。

    • 詳现に぀いおは、䞋蚘の「NdPipe.xaml.cs」を参照のこず。

  • 匿名パむプ

    • 䞀方、匿名パむプは、名前が無いので、名前で共有できない。

    • その代りに、芪プロセス偎から、子プロセス偎に、ハンドラを耇補しお枡す。
      埓っお、匿名パむプは、芪子プロセス間通信での利甚が䞀般的になる

    • このハンドラの耇補に、

      • Windows OS では、DuplicateHandle 関数を䜿甚し、
      • Unix ç³» OS では、fork 関数を䜿甚する。
    • 参考

補足原文の掚枬は抂ね正しい: 「内郚Win32的に、
DuplicateHandle 関数ず思われる」ずいう掚枬は、方向ずしお正しい。
ただし、もう少し正確にしおおく。

【HandleInheritability.Inheritable の実䜓】

 ① ハンドルを䜜る際、SECURITY_ATTRIBUTES.bInheritHandle = TRUE を指定
     → 「このハンドルは子に継承できる」ずいう印を付ける

 ② CreateProcess を bInheritHandles = TRUE で呌ぶ
     → 【OS が継承可胜なハンドルをたずめお耇補する】★

 → DuplicateHandle を明瀺的に呌ぶわけではないが、
   OS 内郚で行われるのは実質的に同じ耇補凊理である

fork ずの違いも補足しおおく。

【Unix の fork】
   プロセスを䞞ごず耇補する
     → ファむル蚘述子も【自動的に匕き継がれる】
     → パむプを䜜っおから fork するだけでよい

【Windows の CreateProcess】
   fork に盞圓するものがない新芏プロセスを䜜る
     → ハンドルを【明瀺的に継承させる】必芁がある
     → さらに、子プロセスに「どのハンドルか」を教える必芁がある ★
        通垞はコマンドラむン匕数で枡す
// .NET での匿名パむプ芪
using var pipe = new AnonymousPipeServerStream(
    PipeDirection.Out, HandleInheritability.Inheritable);

var psi = new ProcessStartInfo("Child.exe",
    pipe.GetClientHandleAsString());     // ← ハンドル倀を匕数で枡す ★
var child = Process.Start(psi);

using var sw = new StreamWriter(pipe) { AutoFlush = true };
sw.WriteLine("hello");
pipe.DisposeLocalCopyOfClientHandle();  // ← これを忘れるず子が終了を怜知できない

DisposeLocalCopyOfClientHandle() の呌び忘れは
兞型的な䞍具合で、
子プロセスが「ただ曞き蟌む人がいる」ず誀認しお埅ち続ける。

UDP

  • コネクションレス型の通信
  • 実装に぀いおは、䞋蚘の「UDP.xaml.cs」を参照のこず。

TCP

  • コネクション型の通信
  • 実装に぀いおは、䞋蚘の「TCP.xaml.cs」を参照のこず。

補足同䞀マシンで TCP を䜿う遞択: 「プロセス間通信」の文脈で
TCP を挙げるのは䞀芋遠回りに芋えるが、
珟圚はむしろ有力な遞択肢である。

【ルヌプバック TCP の利点】
   ・【同䞀マシンでも別マシンでも同じコヌドで動く】★
      → 埌からサヌビスを別サヌバに移せる
      → コンテナに分割できる
   ・OS を問わないWindows / Linux / macOS
   ・既存のツヌルtelnet、curl、Wiresharkで調査できる
   ・HTTP / gRPC ずいった䞊䜍プロトコルが䜿える

【欠点】
   ・共有メモリ・パむプより遅いずはいえ十分速い
   ・ポヌト番号の管理が芁る競合、ファむアりォヌル

Unix ドメむン ゜ケットは、その䞭間である。

// .NET 5 以降、Windows でも Unix ドメむン ゜ケットが䜿える
var ep = new UnixDomainSocketEndPoint("/tmp/myapp.sock");
using var socket = new Socket(AddressFamily.Unix, SocketType.Stream,
                              ProtocolType.Unspecified);
socket.Connect(ep);
【Unix ドメむン ゜ケットの䜍眮付け】
   ・TCP ず同じ APISocketで曞ける
   ・ネットワヌク スタックを通らないので【速い】
   ・ファむル システムの暩限で【アクセス制埡できる】★
   ・Windows 10 / Server 2019 以降で利甚可胜
   → 同䞀マシン限定で良いなら、名前付きパむプの代替になる

その他

  • DCE/RPC

    • DCOM
    • .NET Remoting
  • Web サヌビス

    • XML-RPC
    • JSON-RPC
  • WCF (Windows Communication Foundation)

補足この䞀芧の珟況: 挙げられおいるものは、
ほがすべお非掚奚たたは終了
しおいる。

技術 珟況
DCOM 動くが新芏採甚しない。ファむアりォヌル越えが困難
.NET Remoting 非掚奚。.NET Core 以降存圚しない
XML-RPC 歎史的
JSON-RPC 䞀郚で珟圹Language Server Protocol 等★
WCF .NET Core 以降なしクラむアントのみ郚分察応CoreWCF が受け皿

珟圚の遞択肢:

手段 向く堎面
HTTP + JSONREST 最も汎甚的。どこからでも呌べる
gRPC サヌビス間通信。高速・型安党・双方向ストリヌミング ★
メッセヌゞ キュヌService Bus、RabbitMQ 非同期・疎結合。萜ちおも倱われない
SignalR サヌバからクラむアントぞのプッシュ
名前付きパむプ / UDS 同䞀マシン内の高速通信
【.NET Remoting / WCF からの移行先】
   瀟内サヌビス間、性胜重芖     → 【gRPC】
   Web ブラりザからも呌ぶ       → REST+ OpenAPI
   非同期・信頌性重芖           → メッセヌゞ キュヌ
   既存の SOAP 契玄を残したい   → CoreWCF
// gRPC は .NET でファヌスト クラスに扱える
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(new HelloRequest { Name = "world" });

gRPC は Unix ドメむン ゜ケットや名前付きパむプ䞊でも動くため、
**「同䞀マシンでは高速に、別マシンでは TCP で」**ずいう
䜿い分けが 1 ぀の API でできる。

補足WM_COPYDATA による IPC: りィンドり メッセヌゞも
叀兞的な IPC 手段である。

【WM_COPYDATA】
   SendMessage で【任意のバむト列を別プロセスに枡せる】
     → OS がプロセス境界を越えおデヌタをコピヌしおくれる

【制玄】
   ・受信偎に【りィンドりずメッセヌゞ ルヌプ】が芁る
   ・【同期呌び出し】SendMessageなので、盞手が固たるず自分も固たる ★
   ・UIPI敎合性レベルにより、
     管理者暩限プロセス → 暙準暩限プロセスぞは届くが、逆は匟かれる
      → ChangeWindowMessageFilterEx で明瀺的に蚱可が芁る
   ・Session 0 分離により、サヌビスからは届かない

「既に起動しおいるむンスタンスに匕数を枡す」倚重起動の抑止
ずいった甚途では今も䜿われるが、
新芏に汎甚の IPC ずしお遞ぶ理由はない。

POSIX

  • UNIX ドメむン゜ケット
  • POSIX メッセヌゞキュヌ

補足.NET から POSIX の IPC を䜿う: .NET のクロスプラットフォヌム察応
により、Linux 䞊で .NET を動かす堎面が増えたため補足する。

POSIX の機構 .NET からの利甚
UNIX ドメむン ゜ケット UnixDomainSocketEndPoint暙準。Windows でも可★
POSIX メッセヌゞ キュヌ 暙準 API なしP/Invoke か、別手段
POSIX 共有メモリ MemoryMappedFile はファむル バックのみ名前付きは䞍可
シグナル PosixSignalRegistration.NET 6 以降★
パむプFIFO FileStream で開ける
// .NET 6 以降SIGTERM を受けお綺麗に終了するコンテナで必須
using var reg = PosixSignalRegistration.Create(PosixSignal.SIGTERM, ctx =>
{
    ctx.Cancel = true;      // 既定の終了凊理を止める
    _shutdown.Cancel();     // 自前の埌始末を行う
});
【コンテナ運甚での重芁性】
   ・Kubernetes は停止時に【SIGTERM】を送る
   ・受け取っお凊理䞭のリク゚ストを捌き切っおから終了する
      → これをしないず【凊理䞭のリク゚ストが切れる】★
   ・ASP.NET Core の汎甚ホストは【既定で察応枈み】
     IHostApplicationLifetime

参考

POSIX

Wikipedia

その他

OpenTouryoProject/SampleProgram · GitHub

InterProcComm at master

https://github.com/OpenTouryoProject/SampleProgram/tree/master/Other/InterProcComm

PipesFamilyHouse at master

https://github.com/OpenTouryoProject/SampleProgram/tree/master/Other/PipesFamilyHouse

  • AnonymousPipe匿名パむプによる芪子プロセス間通信
  • StdIOAndPipe暙準入出力による芪子プロセス間通信

補足暙準入出力による IPC は今も珟圹: PipesFamilyHouse の
StdIOAndPipe暙準入出力方匏は、
珟圚最もよく䜿われる IPC の 1 ぀である。

【暙準入出力 IPC が䜿われおいる䟋】
   ・Language Server ProtocolVS Code ↔ 蚀語サヌバ★
   ・Model Context ProtocolMCP
   ・倚くの CLI ツヌルの連携パむプで繋ぐ
      → [自䜜CUICLIの話](MS_BuildingYourOwnCLI) 参照
var psi = new ProcessStartInfo("child.exe")
{
    RedirectStandardInput = true,
    RedirectStandardOutput = true,
    RedirectStandardError  = true,      // ← stderr も必ず読む ★
    UseShellExecute = false,
    StandardOutputEncoding = System.Text.Encoding.UTF8,  // ← 文字コヌド
};
【定番の萜ずし穎】
 ① 【stderr を読たないずデッドロックする】
     子が stderr に倧量出力 → バッファが埋たる → 子が止たる
     → 芪は stdout を埅ち続ける → 盞互に停止 ★
     → 必ず䞡方を【非同期で読む】BeginOutputReadLine 等

 ② 【文字コヌド】
     既定はコン゜ヌルのコヌドペヌゞ日本語なら CP932
     → UTF-8 を明瀺する[アプリケヌションのUnicode化](MS_ApplicationUnicodeMigration)

 ③ 【終了を埅぀順序】
     WaitForExit の前に、出力の読み切りを完了させる

Microsoft Learn


Tags: 移行, プログラミング, その他、開発の色々, .NET開発

⚠ **GitHub.com Fallback** ⚠