Сообщение Re: Нужна консультация fork, pipe, execvp от 13.03.2017 21:02
Изменено 13.03.2017 21:06 Ssd13
Re: Нужна консультация fork, pipe, execvp
У тебя в массиве 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 | uniq -c" ):
1. создаются дескрипторы для pipe в мастере, затем три ребенка (в которых тоже есть эти дескрипторы).
2. первый ребенок меняет и закрывает дескриторы Pipes[0], но оставляет открытыми Pipes[1], затем exec, который пишет и успешно заканчивает работу
3. второй ребенок меняет Pipes[1], но сохраняет Pipes[0] и читает что надо, но так как есть еще незакрытые копии файловых дескрипторов для нулевого pipe (в мастере и третьем ребенке), то его записывающий конец не закрыт и sort продолжает пытаться читать.
Вот мой пропатченный вариант:
Исправленный код | |
| |
В виде патча | |
[с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] | |
На самом деле, я бы поправил алгоритм, потому что хранить массив файловых дескрипторов, который передается в ребенка, не очень надежное решение.
Re: Нужна консультация fork, pipe, execvp
У тебя в массиве Pipes остаются файловые дескрипторы в открытом состоянии (причем как в детях, кроме тех fd, которые заменяются на stdin/out, так и в мастере, прием в нем все fd остаются открытыми), а их нужно закрыть.
Т.е. происходит следующее (для "who | sort | uniq -c" ):
1. создаются дескрипторы для pipe в мастере, затем три ребенка (в которых тоже есть эти дескрипторы).
2. первый ребенок меняет и закрывает дескриторы Pipes[0], но оставляет открытыми Pipes[1], затем exec, который пишет и успешно заканчивает работу
3. второй ребенок меняет Pipes[1], но сохраняет Pipes[0] и читает что надо, но так как есть еще незакрытые копии файловых дескрипторов для нулевого pipe (в мастере и третьем ребенке), то его записывающий конец не закрыт и sort продолжает пытаться читать.
Вот мой пропатченный вариант:
На самом деле, я бы поправил алгоритм, потому что хранить массив файловых дескрипторов, который передается в ребенка, не очень надежное решение. Например, можно создавать pipe непосредственно перед форком и передавать наследнику только два нужных файловых дескриптора от его пайпов. Наследник для них сделает dup2 и close, а родитель сразу после форка сделает close ненужным концам пайпов. Таким образом не будет создаваться мусора из ненужных файловых дескрипторов, которые еще и нужно закрывать, но в разных местах по разному, чтобы не закрыть чего нужного.
Т.е. происходит следующее (для "who | sort | uniq -c" ):
1. создаются дескрипторы для pipe в мастере, затем три ребенка (в которых тоже есть эти дескрипторы).
2. первый ребенок меняет и закрывает дескриторы Pipes[0], но оставляет открытыми Pipes[1], затем exec, который пишет и успешно заканчивает работу
3. второй ребенок меняет Pipes[1], но сохраняет Pipes[0] и читает что надо, но так как есть еще незакрытые копии файловых дескрипторов для нулевого pipe (в мастере и третьем ребенке), то его записывающий конец не закрыт и sort продолжает пытаться читать.
Вот мой пропатченный вариант:
Исправленный код | |
| |
В виде патча | |
[с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 ненужным концам пайпов. Таким образом не будет создаваться мусора из ненужных файловых дескрипторов, которые еще и нужно закрывать, но в разных местах по разному, чтобы не закрыть чего нужного.