отправка команд дочернему процессу через Pipe/dup2 в C

StackOverflow https://stackoverflow.com//questions/11689421

  •  12-12-2019
  •  | 
  •  

Вопрос

Я пытаюсь написать программу удаленного управления для omxplayer на моем Rasperry Pi.

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

int fd_pipe[2];
pipe (fd_pipe);

while(1) {  // main accept() loop

    printf("server: got connection from %s\n", s);

    /* Attempt to fork and check for errors */
    if( (pid=fork()) == -1){
        fprintf(stderr,"Fork error. Exiting.\n");  /* something went wrong */
        exit(1);
    }

    if (pid==0) 
    { // this is the child process
        dup2(0, fd_pipe[0]);
        close(fd_pipe[1]);


        if(execl("/usr/bin/top","top",NULL) == -1){
            fprintf(stderr,"execl Error!");
            exit(1);
        }

        //try and send command here
        write(fd_pipe[0], "q", 1);

        exit(0);

    } else {

        close(new_fd);  // parent doesn't need this
        dup2(1, fd_pipe[1]);

        //try and send here too
        write(fd_pipe[0], "q", 1);
    }
}

Когда я тестировал с помощью top и запускал программу, я вижу верхний вывод в окне терминала и вижу в окне команду q, но похоже, что она идет к родительскому процессу, а не к дочернему процессу.Я делаю что-то не так с каналами или невозможно отправлять команды порожденному дочернему процессу?

Я попытался изменить дочерний оператор dup2, чтобы скопировать его из канала в стандартный ввод.

        { // this is the child process
        dup2(fd_pipe[0], 0);

Но затем top не запускается с ошибкой получения сообщения tty

Это было полезно?

Решение

Чтобы взаимодействовать с программой, которая ожидает возможности манипулировать терминалом, вам следует использовать псевдотерминал.Это позволит избежать failed tty get ошибка.

/*...*/
#include <pty.h>

void do_child () {
    if (execlp("top", "top", (const char *)0) < 0) {
        perror("exec top");
        exit(EXIT_FAILURE);
    }
    /* NOTREACHED */
}

void do_parent (int fd, pid_t p) {
    sleep(5);
    char r;
    write(fd, "q", 1);
    while (read(fd, &r, 1) > 0) { write(1, &r, 1); }
    waitpid(p, 0, 0);
    close(fd);
}

int main () {
    int fd;
    pid_t p = forkpty(&fd, 0, 0, 0);
    switch (p) {
    case 0:  do_child();
             /* NOTREACHED */
    case -1: perror("forkpty");
             exit(EXIT_FAILURE);
    default: break;
    }
    do_parent(fd, p);
    return 0;
}

Обратите внимание, что forkpty это не POSIX, а интерфейс, доступный в версиях UNIX BSD и Linux.

Вы можете выполнить top в пакетном режиме, но для выхода нельзя использовать команду.Вы должны убить его.

void do_child () {
    if (execlp("top", "top", "-b", (const char *)0) < 0) {
        perror("exec top");
        exit(EXIT_FAILURE);
    }
    /* NOT REACHED */
}

void do_parent (pid_t p) {
    sleep(5);
    if (kill(p, SIGINT) < 0) {
        perror("kill");
        exit(EXIT_FAILURE);
    }
    waitpid(p, 0, 0);
}

int main () {
    pid_t p;
    switch ((p = fork())) {
    case 0:  do_child();
    case -1: perror("fork"); exit(EXIT_FAILURE);
    default: break;
    }
    do_parent(p);
    return 0;
}

Хотя запуск в пакетном режиме позволит вам открыть процесс более простым вызовом (например, popen как предполагает mux), если этот вызов не вернет идентификатор дочернего процесса, вы не сможете его убить (без выполнения pkill, или покопайтесь в таблице процессов, чтобы найти нужный дочерний процесс, который нужно уничтожить).

Я думаю, у вас есть некоторая путаница в том, как использовать dup2.На странице руководства используются термины oldfd и newfd, и это означает, что oldfd станет newfd.Для иллюстрации приведем простую программу, которая перенаправляет стандартный вывод и стандартный вывод stderr в файл журнала, вызывает функцию, а затем восстанавливает стандартный вывод и стандартный вывод stderr.

void do_something () {
    fputs("Error message\n", stderr);
    puts("This is regular output.");
    fputs("Error message\n", stderr);
    puts("This is regular output.");
}

int main () {
    int fd = creat("/tmp/output.log", 0664);
    int outfd = dup(fileno(stdout));
    int errfd = dup(fileno(stderr));
    fflush(stdout);
    fflush(stderr);
    dup2(fd, fileno(stdout));
    dup2(fd, fileno(stderr));
    setlinebuf(stdout);

    do_something();

    fflush(stdout);
    fflush(stderr);
    dup2(outfd, fileno(stdout));
    dup2(errfd, fileno(stderr));
    close(outfd);
    close(errfd);

    fputs("Error to the screen\n", stderr);
    puts("Regular output to screen");
    fputs("Error to the screen\n", stderr);
    puts("Regular output to screen");

    return 0;
}

Как указал Mux, ваша программа записывает не в тот конец канала.А pipe команда возвращает однонаправленную пару файловых дескрипторов, где все, что записано, fd_pipe[1] можно прочитать из fd_pipe[0].

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top