C 写入'forkpty'子进程:'less'寻呼机
我试图使用forkpty来执行less pager程序,然后从父进程中编写一些文本,这样子进程就可以将其作为输入 我一直在研究如何实现这一点,但我无法使用forkpty,而我使用pipe和fork 发生的情况是,我没有看到任何输出,然后程序正常退出 编辑:我注意到,如果我在forkpty之后阅读master,我会看到缺少的filename less-help for help from less,但是为什么$echo test | less可以正常工作呢?将exec_argv更改为pass-to-less仍然存在相同的原始问题—没有输出 我错过了什么明显的东西吗 使用管道和叉的工作代码C 写入'forkpty'子进程:'less'寻呼机,c,ipc,execvp,pty,C,Ipc,Execvp,Pty,我试图使用forkpty来执行less pager程序,然后从父进程中编写一些文本,这样子进程就可以将其作为输入 我一直在研究如何实现这一点,但我无法使用forkpty,而我使用pipe和fork 发生的情况是,我没有看到任何输出,然后程序正常退出 编辑:我注意到,如果我在forkpty之后阅读master,我会看到缺少的filename less-help for help from less,但是为什么$echo test | less可以正常工作呢?将exec_argv更改为pass-to
次要缺点:在儿童后叉中,应使用_exit
您在这里看不到输出的原因是子级的stdin、stdout和stderr都连接到新的PTY。您需要从主机读取数据并对数据进行处理。以下是我提供的链接中涉及的文件: 注意:链接网页上还有许多其他文件,您可能会发现此项目需要这些文件: 示例主要功能:
#include "apue.h"
#include <termios.h>
#ifdef LINUX
#define OPTSTR "+d:einv"
#else
#define OPTSTR "d:einv"
#endif
static void set_noecho(int); /* at the end of this file */
void do_driver(char *); /* in the file driver.c */
void loop(int, int); /* in the file loop.c */
int
main(int argc, char *argv[])
{
int fdm, c, ignoreeof, interactive, noecho, verbose;
pid_t pid;
char *driver;
char slave_name[20];
struct termios orig_termios;
struct winsize size;
interactive = isatty(STDIN_FILENO);
ignoreeof = 0;
noecho = 0;
verbose = 0;
driver = NULL;
opterr = 0; /* don't want getopt() writing to stderr */
while ((c = getopt(argc, argv, OPTSTR)) != EOF) {
switch (c) {
case 'd': /* driver for stdin/stdout */
driver = optarg;
break;
case 'e': /* noecho for slave pty's line discipline */
noecho = 1;
break;
case 'i': /* ignore EOF on standard input */
ignoreeof = 1;
break;
case 'n': /* not interactive */
interactive = 0;
break;
case 'v': /* verbose */
verbose = 1;
break;
case '?':
err_quit("unrecognized option: -%c", optopt);
}
}
if (optind >= argc)
err_quit("usage: pty [ -d driver -einv ] program [ arg ... ]");
if (interactive) { /* fetch current termios and window size */
if (tcgetattr(STDIN_FILENO, &orig_termios) < 0)
err_sys("tcgetattr error on stdin");
if (ioctl(STDIN_FILENO, TIOCGWINSZ, (char *) &size) < 0)
err_sys("TIOCGWINSZ error");
pid = pty_fork(&fdm, slave_name, sizeof(slave_name),
&orig_termios, &size);
} else {
pid = pty_fork(&fdm, slave_name, sizeof(slave_name),
NULL, NULL);
}
if (pid < 0) {
err_sys("fork error");
} else if (pid == 0) { /* child */
if (noecho)
set_noecho(STDIN_FILENO); /* stdin is slave pty */
if (execvp(argv[optind], &argv[optind]) < 0)
err_sys("can't execute: %s", argv[optind]);
}
if (verbose) {
fprintf(stderr, "slave name = %s\n", slave_name);
if (driver != NULL)
fprintf(stderr, "driver = %s\n", driver);
}
if (interactive && driver == NULL) {
if (tty_raw(STDIN_FILENO) < 0) /* user's tty to raw mode */
err_sys("tty_raw error");
if (atexit(tty_atexit) < 0) /* reset user's tty on exit */
err_sys("atexit error");
}
if (driver)
do_driver(driver); /* changes our stdin/stdout */
loop(fdm, ignoreeof); /* copies stdin -> ptym, ptym -> stdout */
exit(0);
}
static void
set_noecho(int fd) /* turn off echo (for slave pty) */
{
struct termios stermios;
if (tcgetattr(fd, &stermios) < 0)
err_sys("tcgetattr error");
stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
/*
* Also turn off NL to CR/NL mapping on output.
*/
stermios.c_oflag &= ~(ONLCR);
if (tcsetattr(fd, TCSANOW, &stermios) < 0)
err_sys("tcsetattr error");
}
#include "apue.h"
#define BUFFSIZE 512
static void sig_term(int);
static volatile sig_atomic_t sigcaught; /* set by signal handler */
void
loop(int ptym, int ignoreeof)
{
pid_t child;
int nread;
char buf[BUFFSIZE];
if ((child = fork()) < 0) {
err_sys("fork error");
} else if (child == 0) { /* child copies stdin to ptym */
for ( ; ; ) {
if ((nread = read(STDIN_FILENO, buf, BUFFSIZE)) < 0)
err_sys("read error from stdin");
else if (nread == 0)
break; /* EOF on stdin means we're done */
if (writen(ptym, buf, nread) != nread)
err_sys("writen error to master pty");
}
/*
* We always terminate when we encounter an EOF on stdin,
* but we notify the parent only if ignoreeof is 0.
*/
if (ignoreeof == 0)
kill(getppid(), SIGTERM); /* notify parent */
exit(0); /* and terminate; child can't return */
}
/*
* Parent copies ptym to stdout.
*/
if (signal_intr(SIGTERM, sig_term) == SIG_ERR)
err_sys("signal_intr error for SIGTERM");
for ( ; ; ) {
if ((nread = read(ptym, buf, BUFFSIZE)) <= 0)
break; /* signal caught, error, or EOF */
if (writen(STDOUT_FILENO, buf, nread) != nread)
err_sys("writen error to stdout");
}
/*
* There are three ways to get here: sig_term() below caught the
* SIGTERM from the child, we read an EOF on the pty master (which
* means we have to signal the child to stop), or an error.
*/
if (sigcaught == 0) /* tell child if it didn't send us the signal */
kill(child, SIGTERM);
/*
* Parent returns to caller.
*/
}
/*
* The child sends us SIGTERM when it gets EOF on the pty slave or
* when read() fails. We probably interrupted the read() of ptym.
*/
static void
sig_term(int signo)
{
sigcaught = 1; /* just set flag and return */
}
忘了提到我能够使用forkpty和pipe+fork来读取子进程,比如ls。除其他问题外,这一行:include应该是:include@user3629249以及其他问题-我很高兴你能很容易地发现它们,我不能,这就是我在这里发布的问题。如果你能指出它们,那就太好了。另外,如果stdin上有内容,less不需要文件名,echo test | less,这就是我在这里要实现的。编辑:您删除了关于less需要文件名的注释,但我将我的注释留在这里。此llink:是一本可用的书,其中包含有关如何使用forkpty函数的代码和说明。当我意识到我错了时,我删除了关于less的注释。我不喜欢使用书籍引用的参考书解释了关于linux1终端层的所有需要了解的内容,为什么我应该使用exit?它已经通过返回退出失败退出主系统,对吗?2嗯,我在forkpty之后的父进程中添加了从master读取的代码,我确实看到缺少filename less-help for help。我认为这段代码类似于echo test | less,其中不需要文件名。即使在通过传递-参数强制less阅读stdin之后,这个孩子仍然存在,我什么也看不到。有什么想法吗?@Márcio 1,因为这相当于退出,你也不应该这样做。少2张支票为Attystdin。如果它不是tty,它会将stdin视为数据,并尝试打开一个真正的tty进行控制。如果是tty,它想从其他地方获取数据。1啊,我明白了,谢谢,我不知道,很高兴知道。2哦,所以我的代码可以控制更少,但不能传递输入数据,这就是为什么更少抱怨没有文件名,因为它没有得到任何输入。对吗?@Márcio对,你需要一种不同的机制来减少数据量。您可以创建一个文件,将其打开以写入父文件,然后将其作为文件名传递给less。或者,您可以像以前一样创建一个管道,并将文件名/dev/fd/[number]设置为less。这并不能保证在所有Unix上都能工作,但至少在Linux上可以。
#include "apue.h"
#include <termios.h>
#ifdef LINUX
#define OPTSTR "+d:einv"
#else
#define OPTSTR "d:einv"
#endif
static void set_noecho(int); /* at the end of this file */
void do_driver(char *); /* in the file driver.c */
void loop(int, int); /* in the file loop.c */
int
main(int argc, char *argv[])
{
int fdm, c, ignoreeof, interactive, noecho, verbose;
pid_t pid;
char *driver;
char slave_name[20];
struct termios orig_termios;
struct winsize size;
interactive = isatty(STDIN_FILENO);
ignoreeof = 0;
noecho = 0;
verbose = 0;
driver = NULL;
opterr = 0; /* don't want getopt() writing to stderr */
while ((c = getopt(argc, argv, OPTSTR)) != EOF) {
switch (c) {
case 'd': /* driver for stdin/stdout */
driver = optarg;
break;
case 'e': /* noecho for slave pty's line discipline */
noecho = 1;
break;
case 'i': /* ignore EOF on standard input */
ignoreeof = 1;
break;
case 'n': /* not interactive */
interactive = 0;
break;
case 'v': /* verbose */
verbose = 1;
break;
case '?':
err_quit("unrecognized option: -%c", optopt);
}
}
if (optind >= argc)
err_quit("usage: pty [ -d driver -einv ] program [ arg ... ]");
if (interactive) { /* fetch current termios and window size */
if (tcgetattr(STDIN_FILENO, &orig_termios) < 0)
err_sys("tcgetattr error on stdin");
if (ioctl(STDIN_FILENO, TIOCGWINSZ, (char *) &size) < 0)
err_sys("TIOCGWINSZ error");
pid = pty_fork(&fdm, slave_name, sizeof(slave_name),
&orig_termios, &size);
} else {
pid = pty_fork(&fdm, slave_name, sizeof(slave_name),
NULL, NULL);
}
if (pid < 0) {
err_sys("fork error");
} else if (pid == 0) { /* child */
if (noecho)
set_noecho(STDIN_FILENO); /* stdin is slave pty */
if (execvp(argv[optind], &argv[optind]) < 0)
err_sys("can't execute: %s", argv[optind]);
}
if (verbose) {
fprintf(stderr, "slave name = %s\n", slave_name);
if (driver != NULL)
fprintf(stderr, "driver = %s\n", driver);
}
if (interactive && driver == NULL) {
if (tty_raw(STDIN_FILENO) < 0) /* user's tty to raw mode */
err_sys("tty_raw error");
if (atexit(tty_atexit) < 0) /* reset user's tty on exit */
err_sys("atexit error");
}
if (driver)
do_driver(driver); /* changes our stdin/stdout */
loop(fdm, ignoreeof); /* copies stdin -> ptym, ptym -> stdout */
exit(0);
}
static void
set_noecho(int fd) /* turn off echo (for slave pty) */
{
struct termios stermios;
if (tcgetattr(fd, &stermios) < 0)
err_sys("tcgetattr error");
stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
/*
* Also turn off NL to CR/NL mapping on output.
*/
stermios.c_oflag &= ~(ONLCR);
if (tcsetattr(fd, TCSANOW, &stermios) < 0)
err_sys("tcsetattr error");
}
#include "apue.h"
#define BUFFSIZE 512
static void sig_term(int);
static volatile sig_atomic_t sigcaught; /* set by signal handler */
void
loop(int ptym, int ignoreeof)
{
pid_t child;
int nread;
char buf[BUFFSIZE];
if ((child = fork()) < 0) {
err_sys("fork error");
} else if (child == 0) { /* child copies stdin to ptym */
for ( ; ; ) {
if ((nread = read(STDIN_FILENO, buf, BUFFSIZE)) < 0)
err_sys("read error from stdin");
else if (nread == 0)
break; /* EOF on stdin means we're done */
if (writen(ptym, buf, nread) != nread)
err_sys("writen error to master pty");
}
/*
* We always terminate when we encounter an EOF on stdin,
* but we notify the parent only if ignoreeof is 0.
*/
if (ignoreeof == 0)
kill(getppid(), SIGTERM); /* notify parent */
exit(0); /* and terminate; child can't return */
}
/*
* Parent copies ptym to stdout.
*/
if (signal_intr(SIGTERM, sig_term) == SIG_ERR)
err_sys("signal_intr error for SIGTERM");
for ( ; ; ) {
if ((nread = read(ptym, buf, BUFFSIZE)) <= 0)
break; /* signal caught, error, or EOF */
if (writen(STDOUT_FILENO, buf, nread) != nread)
err_sys("writen error to stdout");
}
/*
* There are three ways to get here: sig_term() below caught the
* SIGTERM from the child, we read an EOF on the pty master (which
* means we have to signal the child to stop), or an error.
*/
if (sigcaught == 0) /* tell child if it didn't send us the signal */
kill(child, SIGTERM);
/*
* Parent returns to caller.
*/
}
/*
* The child sends us SIGTERM when it gets EOF on the pty slave or
* when read() fails. We probably interrupted the read() of ptym.
*/
static void
sig_term(int signo)
{
sigcaught = 1; /* just set flag and return */
}
#include "apue.h"
void
do_driver(char *driver)
{
pid_t child;
int pipe[2];
/*
* Create a full-duplex pipe to communicate with the driver.
*/
if (fd_pipe(pipe) < 0)
err_sys("can't create stream pipe");
if ((child = fork()) < 0) {
err_sys("fork error");
} else if (child == 0) { /* child */
close(pipe[1]);
/* stdin for driver */
if (dup2(pipe[0], STDIN_FILENO) != STDIN_FILENO)
err_sys("dup2 error to stdin");
/* stdout for driver */
if (dup2(pipe[0], STDOUT_FILENO) != STDOUT_FILENO)
err_sys("dup2 error to stdout");
if (pipe[0] != STDIN_FILENO && pipe[0] != STDOUT_FILENO)
close(pipe[0]);
/* leave stderr for driver alone */
execlp(driver, driver, (char *)0);
err_sys("execlp error for: %s", driver);
}
close(pipe[0]); /* parent */
if (dup2(pipe[1], STDIN_FILENO) != STDIN_FILENO)
err_sys("dup2 error to stdin");
if (dup2(pipe[1], STDOUT_FILENO) != STDOUT_FILENO)
err_sys("dup2 error to stdout");
if (pipe[1] != STDIN_FILENO && pipe[1] != STDOUT_FILENO)
close(pipe[1]);
/*
* Parent returns, but with stdin and stdout connected
* to the driver.
*/
}
ifeq "$(PLATFORM)" "solaris"
EXTRALIBS=-lsocket -lnsl
endif
PROGS = pty
all: $(PROGS)
pty: main.o loop.o driver.o $(LIBAPUE)
$(CC) $(CFLAGS) -o pty main.o loop.o driver.o $(LDFLAGS) $(LDLIBS)
clean:
rm -f $(PROGS) $(TEMPFILES) *.o
include $(ROOT)/Make.libapue.inc