我正在使用管道来显示文件内容。当我注释 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);
}
我认为问题在于您向
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
进程(系统上所有进程的父进程)关闭。
在子进程中调用寻呼机时去掉文件名参数。当没有给定文件名时,它将从标准输入中读取,该输入已被重定向到管道:
所以改变
execlp(pager, argv0, argv[1], (char *)0);
到
execlp(pager, argv0, (char *)0);