Использование execl для запуска демона - PullRequest
2 голосов
/ 29 апреля 2009

Я пишу программу на C на Linux, которая включает в себя модуль, который позволяет выполнять команду оболочки на удаленной машине. Самый простой способ на самом деле выполнить команду, конечно, было бы просто используйте функцию system (), или используйте popen, а затем возьмите выход. Однако я решил использовать более низкоуровневый подход из-за других требований к дизайну, которые не имеют отношения к текущему проблема.

Итак, я установил трубу и вилку, а затем вызвал execl. Это все работает отлично, за исключением одного досадного исключения. Не работает правильно, если выполняемая команда оболочки является демоном. В этом дело, это просто висит. Я не могу понять, почему. Мое понимание что, когда демон запускается, он обычно разветвляется, а затем родитель выходит. Поскольку мое приложение имеет открытый канал к родителю, вызов read () должен потерпеть неудачу, когда родитель выходит. Но вместо этого приложение просто зависает.

Вот некоторый код, который воспроизводит проблему:


int main(int argc, char** argv)
{
        // Create a pipe and fork
        //
        int fd[2];
        int p = pipe(fd);
        pid_t pid = fork();</p>

<pre><code>    if (pid > 0)
    {
            // Read from the pipe and output the result
            //
            close(fd[1]);
            char buf[1024] = { 0 };
            read(fd[0], buf, sizeof(buf));
            printf("%s\n", buf);

            // Wait for child to terminate
            int status;
            wait(&status);
    }
    else if (pid == 0)
    {
            // Redirect stdout and stderr to the pipe and execute the shell
            // command
            //
            dup2(fd[1], STDOUT_FILENO);
            dup2(fd[1], STDERR_FILENO);
            close(fd[0]);
            execl("/bin/sh", "sh", "-c", argv[1], 0);
    }

}

Код работает нормально, если вы используете его с обычной командой оболочки. Но если вы пытаетесь запустить демон, он просто зависает, а не возвращается к подскажите как следует.

Ответы [ 4 ]

2 голосов
/ 01 мая 2009

Наиболее вероятным решением является добавление close(fd[1]); над execl ().

Причина, по которой ваша программа зависает, заключается в том, что функция read () ожидает, пока демон записывает что-то в свой stdout / stderr. Если демон (включая дочерний процесс вашей программы, а также разветвленные дочерние процессы дочернего процесса, которые хранят свои stdout / stderr) ничего не записывает и существует хотя бы один процесс, поддерживающий открытый конец канала для записи, читайте ( ) никогда не вернется. Но что это за процесс, который удерживает доступный для записи конец трубы? Скорее всего, это дочерний элемент вашей программы, длительный процесс демона. Хотя он мог вызывать close(0); и close(1); при демонизации себя, скорее всего, он не вызывал close(fd[1]);, поэтому доступный для записи конец канала все еще открыт.

0 голосов
/ 29 апреля 2009

Поскольку дочерний процесс является демоном, он не завершится в ближайшее время.

Ты уверен? Конечно, я бы согласился с тем, что демон не завершит работу в ближайшее время, но когда демон запускается, он разветвляется, так что ребенок может отсоединиться от терминала, и тогда родитель выходит. Поскольку системный вызов wait () ожидает родительского процесса-демона, он должен завершиться.

Несмотря на это, та же проблема возникает без вызова wait ().

Кроме того, почему read () не получает EOF? Read () читает из открытого канала, который связан с процессом родительского демона. Поэтому, когда родительский процесс-демон завершается, read () должен немедленно вернуться с EOF.

0 голосов
/ 29 апреля 2009

Я думаю, вы должны получить сигнал SIGPIPE при ожидании завершения чтения, так как другой конец канала закрыт. Вы сделали что-нибудь необычное с сигналом? Я предлагаю вам запустить код с помощью команды strace.

0 голосов
/ 29 апреля 2009

Ваша проблема, вероятно, здесь: -

// Ожидание завершения дочернего процесса статус int; ждать (и статус);

Поскольку дочерний процесс является демоном, он не завершится в ближайшее время.

Также ваш "read ()" может зависнуть. Вам придется решить, как долго вы будете ждать, прежде чем откажетесь от любой попытки отобразить вывод.

...