Переход к программе grep на C

#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() .