Почему exec выводит вывод после выхода из программы?

С файлом z2, содержащим:

user1:x:1001:1001::/home/user1home:/bin/bash
user2:x:1002:1002::/home/user2home:/bin/bash
user3:x:1003:1003::/home/user3home:/bin/bash
user4:x:1004:1004::/home/user4home:/bin/bash first
user1:x:1001:1001::/home/user1home:/bin/bash
user2:x:1002:1002::/home/user2home:/bin/bash
user3:x:1003:1003::/home/user3home:/bin/bash
user4:x:1004:1004::/home/user4home:/bin/bash second

Утилита cgrepвыдаст:

$ cgrep -e '1002:1002' +w '/home/user4home' z2
========================================
user2:x:1002:1002::/home/user2home:/bin/bash
user3:x:1003:1003::/home/user3home:/bin/bash
user4:x:1004:1004::/home/user4home:/bin/bash first
========================================
user2:x:1002:1002::/home/user2home:/bin/bash
user3:x:1003:1003::/home/user3home:/bin/bash
user4:x:1004:1004::/home/user4home:/bin/bash second

Дополнительная информация оcgrep(контексте, оконном grep):

cgrep   shows context of matching patterns found in files (man)
Path    : ~/executable/cgrep
Version : 8.15
Type    : ELF 64-bit LSB executable, x86-64, version 1 (SYS...)
Home    : http://sourceforge.net/projects/cgrep/ (doc)

С наилучшими пожеланиями... ура, дрл

6
25.04.2021, 23:42
2 ответа

У вас есть:

    waitpid(cmd_pid,NULL,WNOHANG);

Включая опцию WNOHANG, вы говорите waitpid()не ждать завершения процесса, если он еще не завершен.

Я предполагаю, что вы добавили это, потому что ваша программа зависнет, если вы ее не включите. Это связано с тем, что первоначальный родитель по-прежнему имеет открытый дескриптор файла для записи -конца канала, поэтому процесс чтения по-прежнему заблокирован в ожидании ввода данных в этом канале.

Вот исправленная версия вашей программы, которая закрывает дескрипторы файлов каналов и не использует WNOHANG.

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    if (argc <= 1) {
        printf("ERROR: No arguments passed\n");
        printf("USAGE:./pipe <command 1> | <command 2>\n");
        return 1;
    }

    char *cmd1[50];
    char *cmd2[50];
    int cmd1_arg = 0;
    int cmd2_arg = 0;
    int pipe_num = 0;

    for (int cla = 1; cla < argc; cla++) {
        if (!strcmp(argv[cla], "|")) {
            pipe_num++;
        } else if (pipe_num == 0) {
            cmd1[cmd1_arg++] = argv[cla];
        } else if (pipe_num == 1) {
            cmd2[cmd2_arg++] = argv[cla];
        }
    }

    cmd1[cmd1_arg] = NULL;
    cmd2[cmd2_arg] = NULL;

    if (pipe_num != 1) {
        printf("ERROR: Insufficient arguments passed\n");
        printf("USAGE:./pipe <command 1> | <command 2>\n");
        return 1;
    }

    int pipe_fd[2];

    if (pipe(pipe_fd) < 0) {
        perror("pipe");
        return 1;
    }

    const pid_t pid = fork();

    if (pid < 0) {
        perror("fork");
        return 1;
    } else if (pid != 0) {
        const pid_t cmd_pid = fork();

        if (cmd_pid < 0) {
            perror("fork");
            return 1;
        } else if (cmd_pid != 0) {
            printf("PARENT PID %d\n", getpid());

            close(pipe_fd[0]);
            close(pipe_fd[1]);

            if (waitpid(pid, NULL, 0) < 0) {
                perror("waitpid");
            }

            if (waitpid(cmd_pid, NULL, 0) < 0) {
                perror("waitpid");
            }
        } else {
            printf("c2 PID %d\n", getpid());

            if (dup2(pipe_fd[0], STDIN_FILENO) < 0) {
                perror("dup2");
                return 1;
            }
            close(pipe_fd[0]);
            close(pipe_fd[1]);

            if (execvp(cmd2[0], cmd2) < 0) {
                perror("CMD2 FAIL");
                return 1;
            }
        }
    } else {
        printf("c1 PID %d\n", getpid());

        if (dup2(pipe_fd[1], STDOUT_FILENO) < 0) {
            perror("dup2");
            return 1;
        }
        close(pipe_fd[0]);
        close(pipe_fd[1]);

        if (execvp(cmd1[0], cmd1) < 0) {
            perror("CMD1 FAIL");
            return 1;
        }
    }

    return 0;
}

Запуск этой программы дает мне:

./a.out echo 1 2 3 '|' wc
PARENT PID 20412
c1 PID 20413
c2 PID 20414
      1       3       6
$
16
28.04.2021, 22:51

Ваш исходный процесс, родительский для дочерних процессов, запускает это:

waitpid(pid,NULL,0);                     
waitpid(cmd_pid,NULL,WNOHANG);            
printf("PARENT PID %d\n",getpid());  

То есть он ожидает выхода первого потомка, делает системный вызов, чтобы проверить, вышел ли второй, но из-за WNOHANGна самом деле не ждет этого. В любом случае проверка спорна, так как программа не использует возвращаемое значение из waitpid(). Затем родитель печатает свой PID и продолжает работу по функции через два других оператораif-и выходит через return 0.

В этот момент второй дочерний процесс может быть все еще запущен, а может и нет. Без явной синхронизации нет никаких гарантий того, в каком порядке происходят события. Кроме того, оболочка не знает о дочерних процессах, запускаемых вашей программой, и не имеет механизма их ожидания. Основной процесс должен wait*()для них.

Прежде чем продолжить работу с кодом, избавьтесь от беспорядка вложенных операторов if -и создайте более четкую структуру. Обычный способ упростить подобный код состоит в том, чтобы дочерние процессы немедленно вызывали функцию и _exit()там или сразу после возврата функции. Прямо сейчас, если ваши вызовы exec*()терпят неудачу, дочерние процессы также продолжают выполнение того же кода вместо выхода.

6
28.04.2021, 22:51

Теги

Похожие вопросы