我正在尝试在我的树莓派上为 omxplayer 编写一个远程控制程序

我可以让 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 进行测试并运行程序时,我可以看到 top 输出出现在终端窗口中,并且我可以在窗口中看到 q 命令,但它看起来像是转到父进程而不是子进程。我是否对管道做错了什么,或者是否无法向生成的子进程发送命令?

我尝试更改子 dup2 语句以从管道复制到标准输入

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

但是 top 无法启动并显示失败的 tty get 消息

有帮助吗?

解决方案

要与希望能够操作终端的程序进行交互,您应该使用伪 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,而是 BSD 和 Linux 风格的 UNIX 上可用的接口。

你可以执行 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 建议的那样),除非该调用返回子进程的进程 ID,否则您将无法杀死它(不执行 pkill, ,或者挖掘进程表以找到要杀死的正确子进程)。

我认为您对如何使用有些困惑 dup2. 。手册页使用术语 oldfdnewfd, ,这意味着 oldfd 会变成 newfd. 。为了说明这一点,这里有一个简单的程序,它将 stdout 和 stderr 重定向到日志文件,调用一个函数,然后恢复 stdout 和 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