У тебя в массиве Pipes остаются файловые дескрипторы в открытом состоянии (причем как в детях, кроме тех fd, которые заменяются на stdin/out, так и в мастере, прием в нем все fd остаются открытыми), а их нужно закрыть. Иначе программа, читающая из канала не знает когда ей закончить, т.к. в каком-то процессе есть еще открытый файловый дескриптор от пишущего конца канала, хотя процесс, который писал уже завершился.
Т.е. происходит следующее (для "who | sort | uniq -c" ):
1. создаются дескрипторы для pipe в мастере, затем три ребенка (в которых тоже есть эти дескрипторы).
2. первый ребенок меняет и закрывает дескриторы Pipes[0], но оставляет открытыми Pipes[1], затем exec, который пишет и успешно заканчивает работу
3. второй ребенок меняет Pipes[1], но сохраняет Pipes[0] и читает что надо, но так как есть еще незакрытые копии файловых дескрипторов для нулевого pipe (в мастере и третьем ребенке), то его записывающий конец не закрыт и sort продолжает пытаться читать. Нужно закрыть все пишущие концы канала в мастере и других детях, чтобы при выходе "who" и закрытии им своего пишущего конца sort на читающем конце считал все, что есть и завершил чтение, т.к. данных больше нет, пишущий конец закрыт.
Вот мой пропатченный вариант:
| Исправленный код |
| #include <iostream>
#include <fstream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <vector>
#include <array>
//#include <algorithm>
//#include <functional>
//#include <cctype>
//#include <locale>
#include <string.h>
#include <sys/wait.h>
using namespace std;
void parse(char *s, char delim, std::vector<char *> &CmdList)
{
unsigned int l=strlen(s);
unsigned int i=0;
CmdList.push_back(s);
//while (s[strlen(s)-1]==' ') s[strlen(s)-1]=0;
for (i=0;i<l;i++)
{
if (s[i]==delim)
{
s[i]=0;
i++;
while (s[i]==' ') {s[i]=0;i++;}
if (strlen(&s[i])) CmdList.push_back(&s[i]);
while (s[strlen(&s[i])-1]==' ') s[strlen(&s[i])-1]=0;
}
}
}
int main() {
std::filebuf fb;
fb.open ("./Pipe.log",std::ios::out);
std::ostream os(&fb);
std::vector<char *> CmdList;
char cmdLine[1024];
int iChildPID=0, subCount=0;
//cin>>cmdLine;
strcpy(cmdLine,"who | sort | uniq -c | sort -nk1");
//strcpy(cmdLine,"who | sort -nk1");
parse(cmdLine,'|',CmdList);
std::vector<int *> Pipes;
for (unsigned int i=0;i<CmdList.size()-1;i++)
{
int *Pipe=new int[2];
Pipes.push_back(Pipe);
if ( pipe(Pipes[i]) < 0 )
{
cout << "Pipe error" << endl;
return errno;
}
os << "Pipe" << i <<": " << Pipes[i][0]<<", "<<Pipes[i][1]<<endl;
}
int fd = 0;
while (!CmdList.empty())
{
std::vector<char *> ParamList;
parse(CmdList.front(),' ',ParamList);
ParamList.push_back(NULL);
if (!(iChildPID=fork()))
{
if (CmdList.size()>1)
{
dup2(Pipes[subCount][1],1);//O_CLOEXEC
close(Pipes[subCount][0]);
Pipes[subCount][1]=1;
os<<getpid()<<" Pipe(>)"<<subCount<<": "<< Pipes[subCount][0]<<", "<<Pipes[subCount][1]<<endl;
}
else
{
fd = open("./result.out", O_CREAT | O_RDWR );
dup2(fd,1);
os<<getpid()<<" File(>)\n";
}
if (subCount)
{
dup2(Pipes[subCount-1][0],0);
Pipes[subCount-1][0]=0;
close(Pipes[subCount-1][1]);
os<<getpid()<<" Pipe(<)"<<subCount-1<<": "<< Pipes[subCount-1][0]<<", "<<Pipes[subCount-1][1]<<endl;
}
for( int it = 0; it < Pipes.size(); ++it )
{
if( it != subCount )
{
close( close(Pipes[it][0]) );
close( close(Pipes[it][1]) );
}
}
if (-1==execvp(CmdList.front(),ParamList.data()))
{
std::cerr << "error in execvp: " << strerror(errno) << std::endl;
return errno;
}
}
subCount++;
cout <<" PID:"<<iChildPID<<" Count:"<<subCount<< endl;
CmdList.erase(CmdList.begin());
}
for( int it = 0; it < Pipes.size(); ++it )
{
close( close(Pipes[it][0]) );
close( close(Pipes[it][1]) );
}
int status=0;
cout <<"Waiting for "<<subCount<< endl;
int pid ;
for (int i=1;i<subCount;i++)
{
cout <<"Waiting for "<<subCount-i<< endl;
pid=wait(&status);
cout <<"PID: "<<pid<<" terminated \n";
}
close(fd);
cout <<"Master is finished!"<< endl; // prints !!!Hello World!!!
fb.close();
return 0;
}
|
| |
| В виде патча |
| [сcode]
--- old.cpp 2017-03-13 23:59:27.729222560 +0300
+++ main.cpp 2017-03-13 23:59:11.217348891 +0300
@@ -15,13 +15,14 @@
using namespace std;
-void parce(char *s, char delim, std::vector<char *> &CmdList)
+void parse(char *s, char delim, std::vector<char *> &CmdList)
{
unsigned int l=strlen(s);
unsigned int i=0;
CmdList.push_back(s);
//while (s[strlen(s)-1]==' ') s[strlen(s)-1]=0;
for (i=0;i<l;i++)
+ {
if (s[i]==delim)
{
s[i]=0;
@@ -29,11 +30,12 @@
while (s[i]==' ') {s[i]=0;i++;}
if (strlen(&s[i])) CmdList.push_back(&s[i]);
while (s[strlen(&s[i])-1]==' ') s[strlen(&s[i])-1]=0;
+ }
}
}
int main() {
std::filebuf fb;
— fb.open ("/home/saddam/Pipe.log",std::ios::out);
+ fb.open ("./Pipe.log",std::ios::out);
std::ostream os(&fb);
std::vector<char *> CmdList;
char cmdLine[1024];
@@ -41,13 +43,13 @@
//cin>>cmdLine;
strcpy(cmdLine,"who | sort | uniq -c | sort -nk1");
//strcpy(cmdLine,"who | sort -nk1");
— parce(cmdLine,'|',CmdList);
+ parse(cmdLine,'|',CmdList);
std::vector<int *> Pipes;
for (unsigned int i=0;i<CmdList.size()-1;i++)
{
int *Pipe=new int[2];
Pipes.push_back(Pipe);
— if (0<pipe(Pipes[i]))
+ if ( pipe(Pipes[i]) < 0 )
{
cout << "Pipe error" << endl;
return errno;
@@ -58,7 +60,7 @@
while (!CmdList.empty())
{
std::vector<char *> ParamList;
— parce(CmdList.front(),' ',ParamList);
+ parse(CmdList.front(),' ',ParamList);
ParamList.push_back(NULL);
if (!(iChildPID=fork()))
{
@@ -71,7 +73,7 @@
}
else
{
— fd = open("/home/saddam/result.out", O_CREAT | O_RDWR );
+ fd = open("./result.out", O_CREAT | O_RDWR );
dup2(fd,1);
os<<getpid()<<" File(>)\n";
}
@@ -82,13 +84,29 @@
close(Pipes[subCount-1][1]);
os<<getpid()<<" Pipe(<)"<<subCount-1<<": "<< Pipes[subCount-1][0]<<", "<<Pipes[subCount-1][1]<<endl;
}
+ for( int it = 0; it < Pipes.size(); ++it )
+ {
+ if( it != subCount )
+ {
+ close( close(Pipes[it][0]) );
+ close( close(Pipes[it][1]) );
+ }
+ }
if (-1==execvp(CmdList.front(),ParamList.data()))
+ {
+ std::cerr << "error in execvp: " << strerror(errno) << std::endl;
return errno;
+ }
}
subCount++;
cout <<" PID:"<<iChildPID<<" Count:"<<subCount<< endl;
CmdList.erase(CmdList.begin());
}
+ for( int it = 0; it < Pipes.size(); ++it )
+ {
+ close( close(Pipes[it][0]) );
+ close( close(Pipes[it][1]) );
+ }
int status=0;
cout <<"Waiting for "<<subCount<< endl;
int pid ;
[/сcode] |
| |
На самом деле, я бы поправил алгоритм, потому что хранить массив файловых дескрипторов, который передается в ребенка, не очень надежное решение. Например, можно создавать pipe непосредственно перед форком и передавать наследнику только два нужных файловых дескриптора от его пайпов. Наследник для них сделает dup2 и close, а родитель сразу после форка сделает close ненужным концам пайпов. Таким образом не будет создаваться мусора из ненужных файловых дескрипторов, которые еще и нужно закрывать, но в разных местах по разному, чтобы не закрыть чего нужного.