#c #linux #fork #pipeline
#c #linux #форк #конвейер
Вопрос:
Я пытаюсь воспроизвести эту команду bash: ps aux | grep bash
на язык C. Я кое-что написал, но, похоже, это не работает. В результате буквально ничего не происходит, приглашение снова печатается в bash. Это код:
int main()
{
int pid;
int pip[2];
char *argexec1[] = {"/bin/grep", "bash", NULL};
char *argexec2[] = {"/bin/ps", "aux", NULL};
if (pipe(pip) == -1){
perror("pipe error n");
exit(1);
}
if (pid = fork() == -1){
perror("fork error");
exit(1);
}
if (pid == 0)
{
dup2(pip[0], 0);
close(pip[1]);
close(pip[0]);
execvp("grep", argexec1);
perror("exec1 failedn");
exit(1);
}
else
{
dup2(pip[1], 1);
close(pip[0]);
close(pip[1]);
execvp("ps", argexec2);
perror("exec2 failedn");
exit(1);
}
Комментарии:
1. Пожалуйста, придерживайтесь тегов, связанных с элементами, в которых опыт поможет людям ответить на вопрос. Быть экспертом по bash не облегчает ответ на этот вопрос; как и быть экспертом по grep. Необходим именно опыт работы с C.
2. Прочитайте Advanced Linux Programming , proc(5) , pipe(7) , системные вызовы (2) , errno(3) , Используйте также strace(1) , GCC и GDB
3. И изучите для вдохновения исходный код GNU bash или sash . Скомпилируйте со всеми предупреждениями и отладочной информацией:
gcc -Wall -Wextra -g
4. если вам не нужно много информации
ps aux
, просто избегайтеps aux | grep bash
и используйтеpgrep -l bash
вместо этого, что быстрее (потому что создается только 1 процесс) и более корректно (потому что список процессов может быть уже изменен доgrep
вызова
Ответ №1:
В дочернем процессе, если execvp() завершается с ошибкой, вызывайте не exit(), а _exit(), иначе унаследованные контексты ввода-вывода могут вызвать повторяющиеся сбросы ввода-вывода и несколько других нежелательных вещей.
. «==» имеет более высокий приоритет, чем «=» . Это приводит к сбою вашего утверждения здесь:
if (pid = fork() == -1)
Добавить круглые скобки:
if ((pid = fork()) == -1)
Вот ваша программа с исправлениями:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main(void)
{
int pid;
int pip[2];
char *argexec1[] = {"/bin/grep", "bash", NULL};
char *argexec2[] = {"/bin/ps", "aux", NULL};
if (pipe(pip) == -1){
perror("pipe error n");
exit(1);
}
if ((pid = fork()) == -1){
perror("fork error");
exit(1);
}
if (pid == 0) {
// Child process
dup2(pip[0], 0);
close(pip[1]);
close(pip[0]);
execvp("grep", argexec1);
perror("exec1 failedn");
_exit(1);
} else {
// Father process
dup2(pip[1], 1);
close(pip[0]);
close(pip[1]);
execvp("ps", argexec2);
perror("exec2 failedn");
exit(1);
}
return 0;
}
Но на самом деле, если вы хотите сделать это как оболочку, вы должны дождаться окончания команд перед выходом из программы и вернуть код выхода последнего процесса в канале. Итак, отец должен запустить два процесса и дождаться их завершения:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
int main(void)
{
int status1, status2;
int pid1, pid2;
int pip[2];
char *argexec1[] = {"/bin/grep", "bash", NULL};
char *argexec2[] = {"/bin/ps", "aux", NULL};
if (pipe(pip) == -1){
perror("pipe error n");
exit(1);
}
if ((pid1 = fork()) == -1){
perror("fork error");
exit(1);
}
if (pid1 == 0) {
// Child process#1
dup2(pip[0], 0);
close(pip[1]);
close(pip[0]);
execvp("grep", argexec1);
perror("exec1 failedn");
_exit(2);
} else {
// Father process
if ((pid2 = fork()) == -1){
perror("fork error");
exit(1);
}
if (pid2 == 0) {
// Child process#2
dup2(pip[1], 1);
close(pip[1]);
close(pip[0]);
execvp("ps", argexec2);
perror("exec2 failedn");
_exit(2);
} else {
// Father process
close(pip[0]);
close(pip[1]);
// Wait for the end of the programs
if (-1 == waitpid(pid1, amp;status1, 0)) {
perror("wait1 error");
exit(1);
}
if (-1 == waitpid(pid2, amp;status2, 0)) {
perror("wait2 error");
exit(1);
}
// Return the exit code of the last program in the pipe
if (WIFEXITED(status1)) {
return WEXITSTATUS(status1);
} else {
// The process may have received a signal, return the whole status...
return status1;
}
}
}
return 0;
}
Комментарии:
1. Спасибо, теперь это работает. Еще одна вещь, когда я выполняю программу, она работает нормально, за исключением того, что в конце она выдает мне эту ошибку: «Сигнал 17 (CHLD), перехваченный ps (НЕИЗВЕСТНО) /bin/ ps: ps/display.c: 66: пожалуйста, сообщите об этой ошибке»
2. Да, это ожидается, потому что родительский процесс разветвляет дочерний процесс и выполняет «ps». Но предполагается, что процесс должен дождаться окончания своих дочерних элементов перед завершением. программа «ps» не знает, что у нее есть дочерние элементы, которых нужно ждать. Обычно вы должны использовать fork() 2 дочерних элемента: один для «ps», а другой для «grep». Отец будет ждать их завершения перед выходом.
3. Итак, единственный способ исправить это — дважды разветвлять? Или это невозможно решить?
4. Более того, основная программа должна возвращать код завершения последнего процесса в канале. Здесь вы должны вернуть код выхода grep.
5. Вы должны дважды выполнить функцию fork() .