プログラミング言語: C と C++ の混合コード
コンテキスト: STDIN からユーザー入力を受け取り、STDOUT に応答を出力するプログラム 'A' があります。「A」の入力と出力は、意図したユーザーには適していません。残念ながら、「A」を直接編集または拡張することはできません。
アプローチ: 子プロセスをフォークして「A」を起動し、パイプを使用して入出力をインターセプトおよび変換するプログラム「B」を作成しました (つまり、ユーザー入力を取得し、パイプを介して A の STDIN に送信する前に調整し、逆に「 A' パイプ経由でユーザーに合わせて調整します。)
問題: 「A」の出力を受信しようとすると、プログラムが通信中に「ハング」したように見えます。私の調査の結果、この問題は 3 つの問題のうちの 1 つである可能性があると確信しました。 A' を親に。
- パイプ 2 に関連付けられたストリームの最後に EOF がありません。これを示す兆候は、パイプ 1 が期待どおりに機能し、同様の問題の影響を受けないことです。おそらく、プログラム 'A' が
cin
入力の処理に使用し、'\ EOF ではありません。プログラムを 1 行ずつ読み取るように調整しようとしましたが、「A」の実行の最初の行の後 (子プロセスによって出力された自己宣言行に加えて)、データの受信が停止しました。 - 親がパイプから読み取ろうとしているのと同時に子がパイプに書き込みを行っている場合、ある種の競合が発生します。
fgetc()
パイプ 2 ストリーム バッファ内のデータを「消費」していない可能性があるため、子プロセスはこのデータが「受信」されるまでブロックされます。'A' (パイプ 1 データ) の入力がシェル/OS (STDIN) によって問題なく受信され、バッファリングがその側でより適切に/異なる方法で処理されるのに対し、STDOUT は 'A' の出力を受信し、これをパイプ 2 に配信し、その結果、すべてのバッファリングの責任はパイプ メカニズムに委ねられます。
以下は、プログラム B (「test.cpp」) のコードと、A の代わりにテストに使用しているダミーの模倣プログラム (「test2.cpp」) です。
test.cpp
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
int main()
{
pid_t pid;
int wpipe[2];//write to child pipe
int rpipe[2];//read from child pipe
//initiate pipes here
if(pipe(wpipe))
{
cout << "write pipe creation failed"<<endl;
return 1;
}
if(pipe(rpipe))
{
cout << "read pipe creation failed"<<endl;
return 1;
}
//fork child here
pid_t procID = vfork();//does not copy address space. better than fork?
if(procID==0)
{//child
//close unused pipe ends
close(wpipe[1]);//read from wpipe
close(rpipe[0]);//write to rpipe
//handle stdin & stdout pipes
dup2(wpipe[0],STDIN_FILENO);//reroute stdin to wpipe[0]
dup2(rpipe[1],STDOUT_FILENO);//reroute stdout to rpipe[1]
//close pipes since they are copied??
close(wpipe[0]);
close(rpipe[1]);
//prepare array for execv
char** args;
args = new char*[2];
args[0] = new char[8];
strcpy(args[0],"test2");
args[1] = (char*)0;
cout << "I'm child. Relinquishing procID & memory image to test 2."<<endl;
int test = execv(args[0],args);
if(test==-1)
cout << "Error: execv failed."<<endl;
delete[] args[0];
delete[] args;
exit(0);
}else if(procID>0)
{//parent
int state,c;
//close unused pipe ends
close(wpipe[0]);//write to wpipe
close(rpipe[1]);//read from rpipe
//communicate with child
FILE *wstream;
wstream = fdopen(wpipe[1],"w");
FILE *rstream;
rstream = fdopen(rpipe[0],"r");
//read from child
c = fgetc(rstream);
while(c!=EOF)
{
putchar(c);
c = fgetc(rstream);
}
fprintf(wstream,"test1\n");
c = fgetc(rstream);
while(c!=EOF)
{
putchar(c);
c = fgetc(rstream);
}
fprintf(wstream,"test2\n");
c = fgetc(rstream);
while(c!=EOF)
{
putchar(c);
c = fgetc(rstream);
}
fclose(wstream);
fclose(rstream);
waitpid(procID,&state,0);
cout << "I'm parent."<<endl;
}
else
{//failure to fork
cout << "Fork failed" << endl;
return 1;
}
return 0;
}
test2.cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
string tmp = "";
cout << "started" << endl;
while(tmp=="")
cin >> tmp;
cout << tmp << endl;
tmp="";
while(tmp=="")
cin >> tmp;
cin >> tmp;
cout << tmp << endl;
cout << "exiting" << endl;
return 0;
}
長い質問とモノリシックなコードについては、事前にお詫び申し上げます。