私がやろうとしていること
私の Cocoa アプリでは、一連のコマンドライン プログラムを実行する必要があります。これらのほとんどは非対話型であるため、いくつかのコマンド ライン引数を指定して起動すると、目的の処理を実行し、何かを出力して終了します。プログラムの 1 つは対話型であるため、テキストとプロンプトを stdout に出力し、stdin での入力を期待します。これは、quit コマンドを送信するまで続きます。
機能するもの
大量のデータを stdout にダンプしてから終了するだけの非対話型プログラムは、比較的単純です。
NSPipe
stdout/stdin/stderr の を作成しますNSTask
それらのパイプで起動します
次に、どちらか
NSFileHandle
パイプのもう一方の端がストリームの最後まですべてのデータを読み取り、タスクの終了時に一度に処理するように取得します
また
- 出力パイプのもう一方の端
-fileDescriptor
から を取得します。NSFileHandle
- ノンブロッキング モードを使用するようにファイル記述子を設定する
- を使用して、これらのファイル記述子のそれぞれで GCD ディスパッチ ソースを作成します。
dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, ...
- ディスパッチソースを再開し、それがスローするデータを使用して処理します
read()
- タスクが終了し、パイプ ファイル記述子が EOF を報告するまで続行します (
read()
0 バイトの読み取りが報告されます)。
うまくいかないこと
どちらのアプローチも、インタラクティブ ツールでは完全に機能しません。明らかに、プログラムが終了するまで待ちきれません。プログラムはコマンド プロンプトで待機していて、指示しない限り終了しないからです。一方、NSPipe
データをバッファリングするため、CLI プログラムがたまたまパイプを明示的にフラッシュしない限り、バッファサイズのチャンクで受信しますが、私の場合はそうではありません。最初のコマンド プロンプトはバッファー サイズよりもはるかに小さいため、何も受信せず、そのまま待機します。ノーゴーNSPipe
もそうです。
いくつかの調査の後、NSPipe の代わりに疑似端末(pty)を使用する必要があると判断しました。残念ながら、私はそれを機能させるのに問題がありました。
私が試したこと
stdout パイプの代わりに、次のように pty を作成します。
struct termios termp;
bzero(&termp, sizeof(termp));
int res = openpty(&masterFD, &slaveFD, NULL, &termp, NULL);
これにより、2 つのファイル記述子が得られます。これは、標準出力のみ、または標準出力と標準入力の両方でにslaveFD
渡さNSFileHandle
れます。NSTask
次に、マスター側から通常の非同期読み取りを実行しようとします。
ターミナル ウィンドウで制御しているプログラムを実行すると、最初に 2 行のテキストが出力されます。1 行目は改行を含めて 18 バイト、もう 1 行は 22 バイトで、コマンド プロンプトに改行はありません。これらの 40 バイトの後、入力を待ちます。
stdout に pty を使用すると、制御対象のプログラムから 18 バイトの出力 (改行で終わる正確に 1 行) を受け取り、それ以上は受け取りません。最初の 18 バイトの後にすべてがそこに留まり、それ以上のイベントはありません。GCD イベント ソースのハンドラーは呼び出されません。
stdin にも pty を使用すると、通常は 19 バイトの出力 (前述の行と次の行の 1 文字) を受け取り、制御されたプログラムはすぐに終了します。データの読み取りを試行する前に少し待った場合 (またはスケジューリング ノイズによって少し一時停止した場合)、プログラムが再び即座に停止する前に、実際には 40 バイト全体を取得します。
追加の行き止まり
ある時点で、非同期読み取りコードに欠陥があるのではないかと考えていたので、NSFileHandle
s とその-readInBackgroundAndNotify
メソッドを使用してすべてをやり直しました。これは、GCD を使用する場合と同じように動作しました。NSFileHandle
(私は当初、 API よりもGCD を選択しましたNSFileHandle
。
質問
無駄な試みを 1 日以上続けた後、この時点にたどり着いたので、何らかの助けが必要でした。私がやろうとしていることには根本的な問題がありますか? 標準入力を pty に接続するとプログラムが終了するのはなぜですか? 私は pty のマスターエンドを閉じていないので、EOF を受け取るべきではありません。stdin は別として、なぜ 1 行分の出力しか得られないのですか? pty のファイル記述子で I/O を実行する方法に問題はありますか? マスターとスレーブ エンドを正しく使用していますか? マスターは制御プロセスで、スレーブは NSTask で?
私が試していないこと
これまでのところ、パイプと pty でノンブロッキング (非同期) I/O しか実行していません。私が考えることができる唯一のことは、pty が単にそれをサポートしていないということです。(そうであれば、なぜfcntl(fd, F_SETFL, O_NONBLOCK);
成功するのでしょうか?) 代わりに、バックグラウンド スレッドでブロッキング I/O を実行して、メイン スレッドにメッセージを送信することができます。マルチスレッドに対処する必要がないようにしたかったのですが、これらすべての API が壊れているように見えることを考えると、非同期 I/O の別の順列を試すよりも時間がかかることはありません。それでも、私が間違っていることを正確に知りたいです。