为了达到我想要的效果,我必须解决一个问题:默认情况下,所有输出函数都使用 STDOUT,并且对于大多数输出函数,我无法改变这种行为(想想我没有源代码在 lib 中嵌入 printf ,或 ncurses 本身)。
因此,如果您修改 STDOUT,一切都会受到影响。
但我希望我的程序保留对 STDOUT 的控制。因此,您必须以某种方式将获得真正 STDOUT 的内容和获得假STDOUT 的内容分开。分离听起来很像叉子......所以叉子就是这样。
本质上,您在一个子进程中分叉了所有程序的大部分,您为此修改了 STDOUT 管道它。父进程保持对 STDOUT 的控制,并且能够读取管道并对其中的数据执行任何操作。
#include <ncurses.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <iostream>
#include <sstream>
#define MAX_LEN 99
WINDOW *create_newwin(int height, int width, int starty, int startx, int vert, int horz);
char buffer[MAX_LEN+1] = {0};
int out_pipe[2];
int saved_stdout;
volatile sig_atomic_t s_interrupted = 0;
volatile sig_atomic_t alarm_expired = 0;
static void s_signal_handler (int signal_value)
{
if (signal_value == SIGTERM || signal_value == SIGSTOP || signal_value == SIGINT)
s_interrupted = 1;
if (signal_value == SIGALRM)
//signal(SIGALRM,alarm_wakeup);
alarm_expired = 1;
}
static void s_catch_signals (void)
{
struct sigaction action;
action.sa_handler = s_signal_handler;
action.sa_flags = 0;
sigemptyset (&action.sa_mask);
sigaction (SIGINT, &action, NULL);
sigaction (SIGTERM, &action, NULL);
sigaction (SIGALRM, &action, NULL);
}
int main (int arg, char **argv) {
const char mesg[]="Testing stdout redirection";
WINDOW *my_win, *my_win2;
int cycle = 0, pid, p[2], row, col;
if(pipe(p) == -1)
{
perror("pipe call error");
return(1);
}
switch (pid=fork())
{
case -1: perror("error: fork call");
return(2);
case 0: /* if child then write down pipe */
if(dup2(p[1], 1) == -1 ) /* stdout == write end of the pipe */
{
perror( "dup2 failed" );
return(1);
}
s_catch_signals ();
setvbuf(stdout, NULL, _IOLBF, 1000);
while(!s_interrupted)
{
printf("Test %d\n",cycle);
cycle++;
usleep(10000);
}
printf("\n\tChild quitting cleanly\n\n");
break;
default:
s_catch_signals ();
std::string mystr;
if(dup2(p[0], 0 ) == -1 ) /* stdin == read end of the pipe */
{
perror( "dup2 failed" );
return(1);
}
initscr(); /* start the curses mode */
getmaxyx(stdscr,row,col); /* get the number of rows and columns */
mvprintw(row/2,(col-strlen(mesg))/2,"%s",mesg);
/* print the message at the center of the screen */
refresh();
my_win2 = create_newwin(10,col,row-10, 0, 0, 0);
wrefresh(my_win2);
my_win = create_newwin(8,col-2,row-9, 1, ' ', ' ');
wrefresh(my_win);
wprintw(my_win,"This screen has %d rows and %d columns\n",row,col);
wprintw(my_win,"Try resizing your window(if possible) and then run this program again\n");
scrollok(my_win,TRUE);
int myy,myx;
std::stringstream ss;
while(!s_interrupted)
{
while( std::getline(std::cin, mystr) )
{
ss.clear();
ss << mystr << std::endl;
mystr = ss.str();
waddstr(my_win,mystr.c_str());
wrefresh(my_win);
}
usleep(100000);
}
endwin();
break;
}
endwin();
printf("\n\tProgram quitting cleanly\n\n");
return (EXIT_SUCCESS);
}
WINDOW *create_newwin(int height, int width, int starty, int startx, int vert, int horz)
{ WINDOW *local_win;
local_win = newwin(height, width, starty, startx);
box(local_win, vert , horz); /* 0, 0 gives default characters
* for the vertical and horizontal
* lines */
wrefresh(local_win); /* Show that box */
return local_win;
}
该程序准确地显示了这种行为:主程序产生一个子程序,它将自己的 STDOUT 重定向到管道。然后,该管道由父级读取,父级将其输出到受控窗口内。屏幕的其余部分可以由父母自由操作,但这是必要的。
switch (pid=fork())
{
case 0: /* if child then write down pipe */
if(dup2(p[1], 1) == -1 ) /* stdout == write end of the pipe */
{
perror( "dup2 failed" );
return(1);
}
当调用 fork 时,如果我们是孩子 (pid==0) 则取我们之前创建的管道,并将我们自己的 STDOUT 切换到该管道的写入端。
setvbuf(stdout, NULL, _IOLBF, 1000);
将缓冲模式改为行,这样我们的父级可以更轻松地抓取它。
while(!s_interrupted)
{
printf("Test %d\n",cycle);
cycle++;
usleep(10000);
}
生成一些简单的文本来检查它是否有效。
switch (pid=fork())
{
default:
if(dup2(p[0], 0 ) == -1 ) /* stdin == read end of the pipe */
{
perror( "dup2 failed" );
return(1);
}
如果我们是父级,请将我们的 STDIN 设置为我们之前创建的管道的读取端。
initscr(); /* start the curses mode */
getmaxyx(stdscr,row,col); /* get the number of rows and columns */
mvprintw(row/2,(col-strlen(mesg))/2,"%s",mesg);
/* print the message at the center of the screen */
refresh();
my_win2 = create_newwin(10,col,row-10, 0, 0, 0);
wrefresh(my_win2);
my_win = create_newwin(8,col-2,row-9, 1, ' ', ' ');
wrefresh(my_win);
wprintw(my_win,"This screen has %d rows and %d columns\n",row,col);
wprintw(my_win,"Try resizing your window(if possible) and then run this program again\n");
scrollok(my_win,TRUE);
初始化 ncurses 并创建一些窗口。我们使用 my_win2 绘制边框,使用无边框的 my_win 实际包含数据。此外,我们将 my_win 设置为自动滚动,以便我们可以继续在其上打印,而不必担心空间不足。
while( std::getline(std::cin, mystr) )
{
ss.clear();
ss << mystr << std::endl;
mystr = ss.str();
waddstr(my_win,mystr.c_str());
wrefresh(my_win);
}
usleep(100000);
在一个轻松节奏的循环中,我们检查我们的 STDIN,如果我们有新的行,我们只需将它们添加到我们的滚动窗口中。因此,当我们的进程被安排在活动状态时,我们会花时间处理大量输出,而不是重复轮询。
这被证明是对所提出问题的有效解决方案。