为什么不使用wait函数时父进程仍然处于活动状态?

问题描述 投票:0回答:2

我正在使用管道来显示文件内容。当我注释 wait 命令(在父进程的最后一部分)时,我期望在

ps
命令的输出中看到父进程(过了一会儿,因为读取已完成,我仍在浏览子进程生成的输出) process),但我在输出中看到父进程和子进程。

以下代码显示了argv1内容。构建程序后(例如

xpager
),应将其执行为
xpager filename

我的平台是

Ubuntu 22.04.4 LTS

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h>

#define BUFSIZE 8192
#define PPAGER  "/usr/bin/less"

int main(int argc, char *argv[]) {
    pid_t   pid;
    int eerrno, n;
    int in, pfd[2];
    char    buf[BUFSIZE];
    char    *pager, *argv0;


    if (argc != 2) {
        printf("usage: %s <pathname>\n", argv[0]);
        exit(1);
    }

    if ( (in = open(argv[1], O_RDONLY)) < 0) {
        eerrno = errno;
        printf("Open error. %s(%d)\n", strerror(errno), errno);
        exit(eerrno);
    }

    if ( pipe(pfd) < 0) {
        eerrno = errno;
        printf("pipe error. %s(%d)\n", strerror(errno), errno);
        exit(eerrno);
    }


    pid = fork();
    switch (pid) {
        case -1:
            eerrno = errno;
            printf("fork error. %s(%d)\n", strerror(errno), errno);
            exit(eerrno);

        case 0:
            close(pfd[1]);
            close(in);
            if (STDIN_FILENO != pfd[0]) {
                if (dup2(pfd[0], STDIN_FILENO) < 0) {
                    eerrno = errno;
                    printf("dup2 error. %s(%d)\n", strerror(errno), errno);
                    exit(eerrno);
                }
                close(pfd[0]);
            }
            if ((pager=getenv("PPAGER")) == NULL)
                pager = PPAGER;
            if ((argv0=strrchr(pager, '/')) != NULL)
                argv0++;
            else
                argv0=pager; 
            execlp(pager, argv0, argv[1], (char *)0);
        default:
            close(pfd[0]);
            while((n=read(in, buf, BUFSIZE)) > 0) {
                if (write(pfd[1], buf, n) < 0) {
                    eerrno = errno;
                    printf("write errno. %s(%d)\n", strerror(errno), errno);
                    kill (pid, 9);
                    exit(eerrno);
                }
            }
            if (n < 0) {
                eerrno = errno;
                printf("read error. %s(%d)\n", strerror(errno), errno);
                kill(pid, 9);
                exit(eerrno);
            }
            close(pfd[1]);
        //  wait(NULL);
    }

    exit(0);
}

为了方便阅读,总结代码如下:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/wait.h>

#define BUFSIZE 8192
#define PPAGER  "/usr/bin/less"

int main(int argc, char *argv[]) {
        pid_t   pid;
        int     n,in, pfd[2];
        char    buf[BUFSIZE];
        char    *pager, *argv0;

        in = open(argv[1], O_RDONLY);
        pipe(pfd);

        pid = fork();
        switch (pid) {
                case 0:
                        close(pfd[1]);
                        dup2(pfd[0], STDIN_FILENO);
                        pager = PPAGER;
                        argv0="less";
                        execlp(pager, argv0, argv[1], (char *)0);
                default:
                        close(pfd[0]);
                        while ((n=read(in, buf, BUFSIZE)) > 0)
                                write(pfd[1], buf, n);
                        close(pfd[1]);
                //      wait(NULL);
        }
        return(0);
   }
c linux gcc fork wait
2个回答
0
投票

我认为问题在于您向

exec
调用传递了错误的参数。您正确设置了管道,将子管道的读取端重定向到
stdin
,但随后将
argv[1]
传递给子管道(这是
less
二进制文件)。

less
将要读取的文件作为其第一个参数,因此当您传递
argv[1]
时,进程只会直接从该文件读取,而不是从您设置的管道中读取。这会导致
less
表现得正常,并且父进程在尝试写入管道时被阻止,因为没有人从中读取。

尝试将

"/dev/stdin"
而不是
argv[1]
传递给
exec
,看看是否可以解决您的问题。
/dev/stdin
是Linux上的伪文件,指的是输入流。既然是这种情况,您还需要将
-f
传递给
less
以强制它显示此文件。

请注意,您很可能仍然会看到父进程正在运行,因为

less
仅根据需要从文件中读取,如其手册页中所述。

编辑:您还可以将管道的文件描述符直接传递给进程,而不是将管道重定向到

stdin
。所有打开的文件描述符都可以在
/dev/fd/
找到。在这里您将分别找到
0
1
2
的文件
stdin
stdout
stderr
。任何当前打开的文件描述符(例如文件、管道等)也可以在这里找到。

管道的文件描述符存储在您定义的整数变量中(

pfd
)。因此,您可以执行以下操作,而不是重定向到
stdin

char pipe_fd[32];
sprintf(pipe_fd, "/dev/fd/%d", pfd[0]);
execlp(pager, argv0, "-f", pipe_fd, (char *)0);

效果是一样的。

在这两种情况下,当

less
到达 EOF 时,父进程将终止,并且
less
也将被
init
进程(系统上所有进程的父进程)关闭。


0
投票

在子进程中调用寻呼机时去掉文件名参数。当没有给定文件名时,它将从标准输入中读取,该输入已被重定向到管道:

所以改变

execlp(pager, argv0, argv[1], (char *)0);

execlp(pager, argv0, (char *)0);
© www.soinside.com 2019 - 2024. All rights reserved.