Почему / bin / sh зависает при запуске через execl на этом конкретном скрипте? - PullRequest
4 голосов
/ 19 апреля 2019

Можете ли вы дать мне несколько идей о следующем коде?Код выполняется, но не завершается.Другая строка s="ls -1" работает хорошо.Выполнение фрагмента оболочки через sh(1) также прекрасно работает.

#include <unistd.h>
#include <string.h>

int
main(int argc, char *argv[])
{
    int fd[2];

    char *s = "ls -1 \"/usr/bin\" | while IFS= read -r fp\ndo\ncat <<- EOF\n\t$fp\nEOF\ndone;";
    //char *s = "ls -1";


    pipe(fd);

    switch(fork()) {
        case 0:
            close(fd[1]);
            dup2(fd[0], 0);
            execl("/bin/sh", "sh", NULL);
            close(fd[0]);
        break;
        default:
            close(fd[0]);
            write(fd[1], s, strlen(s) + 1);
            close(fd[1]);
        break;
    }

    return 0;
}

Ответы [ 2 ]

3 голосов
/ 19 апреля 2019

Когда я преобразовывал свои комментарии в ответ, я протестировал предлагаемое изменение, и оно не устранило проблему. Одно исправление, которое должно сработать, - это добавить ; exit в конце строки, хотя это равносильно измене. Однако тестирование, которое также показывает, что оно не заканчивается; это как если бы ls не заканчивался.

Я пошел в другой терминал, чтобы посмотреть, были ли еще процессы для ls или pipe97 (мое имя для вашего кода); они не были.

  • Попробуйте набрать ps для вашего «зависшего» процесса.

Вы должны получить нормальный вывод и ваше приглашение.

Поскольку родитель не ждет выхода ребенка, подсказка теряется где-то в выводе из ls (который довольно длинный и генерируется довольно медленно). Перенаправьте вывод на /dev/null, и вы увидите приглашение.

  • Лучшим решением, вероятно, является добавление цикла wait() в родительский процесс, поэтому он не завершается до тех пор, пока дочерний процесс не сделает.
#include <sys/wait.h>

…
int corpse;
int status;
while ((corpse = wait(&status)) > 0)
    ;

Во время отладки вы можете печатать corpse и status, чтобы видеть, что происходит.


Я заметил, что этот комментарий (без преамбулы) остается в силе:

close(fd[0]); должен быть перед execl() - запомните, если он успешен, execl() никогда не возвращается (он возвращается только при ошибке). Возможно, вы должны иметь отчет об ошибке и exit(1); или аналогичный после execl(); на данный момент вы сообщаете об успехе даже при неудаче.

Комментарий Rule of Thumb сам по себе действителен, но на самом деле он неприменим к этому коду - дескрипторы незакрытых каналов не вызывают проблем, даже если один дескриптор канала должен был быть закрыт не был закрыт.

1 голос
/ 21 апреля 2019

@ WilliamPursell Спасибо, ты прав. Я не фокусировался на этом на этом примере.

@ user3629249 Я знаю, что не проверяю ошибки должным образом, спасибо!

@ mosvy Это был OpenBSD, действительно, он работал на Mac.

Эта версия работает:

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

int
main(int argc, char *argv[])
{
    int fd[2];

    char *s = "ls -1 '/usr/bin' | while IFS= read -r fp\ndo\ncat <<- EOF\n\t$fp\nEOF\ndone;";
    //char *s = "ls -1";


    pipe(fd);

    switch(fork()) {
        case 0:
            close(fd[1]);
            dup2(fd[0], 0);
            execl("/bin/sh", "sh", NULL);
            close(fd[0]);
        break;
        default:
            close(fd[0]);
            write(fd[1], s, strlen(s));
            close(fd[1]);
            wait(NULL);
        break;
    }

    return 0;
}

Это был wait(2), который сделал это. Я помню, что в какой-то момент я сделал wait(2), но, похоже, я испортил ордер close(1), поэтому он блокировался.

В любом случае, проблема исправлена, спасибо!

...