首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在链式ncurses程序中调整终端的大小(叉/exec/等待)

在链式ncurses程序中调整终端的大小(叉/exec/等待)
EN

Stack Overflow用户
提问于 2016-10-24 13:05:11
回答 1查看 342关注 0票数 1

有一些基于诅咒的程序被启动,在其中创建一个过程链,使用fork/exec/wait

xterm调整大小,并且只有第一个程序运行时,所有程序都可以正常工作。但是,当第二个(或第三个)程序运行时:

代码语言:javascript
复制
savetty{};
endwin();

// forking
if (fork() != 0) {
    // at parent
    wait(&status);
} else {
    // child
    if (execlp(cmd, cmd, (char*) NULL) < 0) {
        exit(1);
    }
}

// child exited: parent refresh its screen
resetty();  refresh();

所有程序似乎都试图同时刷新它们的屏幕。从这一点开始,屏幕变得非常混乱。

exec下一个程序之前,在每个程序中需要做什么来“冻结”诅咒直到wait返回?

编辑:

当我将fork/exec/wait替换为system()时,一切正常。但是我不能保留它(这只是一个测试),它是一个很大的遗留系统,强烈依赖于fork/exec/wait系统调用。

我还试着运行execlp("/bin/sh", "-c", cmd),问题是一样的。

编辑2:从头开始的完整示例代码:

代码语言:javascript
复制
// Uncomment to switch from fork/exec/wait to system() function
//#define USE_SYSTEM

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#include <curses.h>
#include <term.h>

#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

void mybox(int y, int x, int height, int width);
void msg(char* format, ...);
void draw();

int myexec(char* cmd);
int spawn_new_instance(char* cmd);

char* argid = NULL;

int main(int argc, char* argv[]) {
   initscr();     
   cbreak();      
   keypad(stdscr, TRUE);
   noecho();  

   draw();
   if (argc > 1) {
       argid=argv[1];
       msg("That is a child process");
   }

   int pid;
   int key;
   do {
       key = getch();

       switch(key) {
           case KEY_RESIZE:
               clear(); draw();
               msg("KEY_RESIZE");
               break;
           case 'r':
           case 'R':
               pid = spawn_new_instance(argv[0]);
               if (argid) {
                   #ifdef USE_SYSTEM
                       msg("Came back from system()");
                   #else
                       msg("Came back from pid %d", pid);
                   #endif
               } else {
                   msg("Came back from pid %d - THAT IS THE ROOT PROCESS", pid);
               }
               break;

           default:
               msg("Unsupported key '%d'. Type '.' (dot) to exit", key);
       }
   } while (key != '.');

   endwin();
}


void fullbox(void) {
    mybox(0, 0, LINES, COLS);
}


void mybox(int y, int x, int height, int width) {
    int x2 = x + width - 1;
    int y2 = y + height - 1;

    for (int ct = x; ct < x2; ct++) {
        mvaddch(y, ct, ACS_HLINE);
        mvaddch(y2, ct, ACS_HLINE);
    }

    for (int ct = y; ct < y2; ct++) {
        mvaddch(ct, x, ACS_VLINE);
        mvaddch(ct, x2, ACS_VLINE);
    }

    mvaddch(y, x, ACS_ULCORNER);
    mvaddch(y, x2, ACS_URCORNER);
    mvaddch(y2, x, ACS_LLCORNER);
    mvaddch(y2, x2, ACS_LRCORNER);
    refresh();
}


void msg(char* format, ...) {
    for (int ct = 2; ct < COLS - 2; ct++) {
        mvaddch(LINES-3, ct, ACS_CKBOARD);
    }

    char buffer[512];
    va_list argptr;
    va_start(argptr, format);
    vsprintf(buffer, format, argptr);

    int msglen = strlen(buffer) + 2;
    int msgx = (COLS - msglen)/2;

    mvprintw(LINES-3, msgx, " %s ", buffer);
}


void draw() {
    mybox(0, 0, LINES, COLS);

    char sbuf[128];
    sprintf(sbuf, "PID: %d, LINES: %d, COLS: %d", getpid(), LINES, COLS);
    int msglen = strlen(sbuf);
    int msgy = (LINES - 1) / 2;
    int msgx = (COLS - msglen)/2;
    mvprintw(msgy, msgx, "%s", sbuf);

    mybox(msgy-2, msgx-2, 1 + 4, msglen + 4);
    mybox((LINES - LINES/3)/2, (COLS - COLS/3)/2, LINES/3, COLS/3);
    mybox(LINES-4, 1, 3, COLS-2);

    msg("Resize the terminal emulator, or type R to chain new process instance");

    refresh();
}


int spawn_new_instance(char* cmd) {
    savetty();
    endwin();

    int pid;
#ifdef USE_SYSTEM
    char buf[512];
    sprintf(buf, "%s child", cmd);
    system(buf); 

    // we haven't pid using system()
    pid=0;
#else
    pid = myexec(cmd);
#endif

    resetty();
    refresh();

    return pid;
}

int myexec(char* cmd) {
    sigset_t blockSigchld;
    sigset_t previousBlock;
    sigaddset(&blockSigchld, SIGCHLD);
    sigprocmask(SIG_BLOCK, &blockSigchld, &previousBlock);

    int ret = 0, status = 0;
    int retries = 4;

    int pid;
    while ((pid = fork()) == -1) {
        if (errno == EAGAIN) {
            if (--retries >= 0) {
                sleep(1);
                continue;
            } else {
                msg("Cannot open the process now.");
                return -1;
            }
        } else if (errno == ENOMEM) {
            msg("Not enough memory.");
            return -1;
        } else {
            msg("Errno = %u", errno);
            return -1;
        }
    }

    if (pid != 0) { /*  Parent  */
        ret = waitpid(pid, &status, 0);
        sigprocmask(SIG_SETMASK, &previousBlock, (sigset_t *) 0);

        if (ret == -1) {
            return -1;
        }

       return pid;
    } else { /* Child */
        sigprocmask(SIG_SETMASK, &previousBlock, (sigset_t *) 0);

        if (execlp(cmd, cmd, "child", (char*) NULL) < 0) {
            exit(1);
        }
    }
}

Instructions:

  • 键入"R“以生成一个自身的链式新实例。
  • “类型”。(点)退出并返回到父进程。
  • 在第一个(主)进程中,调整终端的大小。一切都很好。
  • 问题:打开许多实例(10个或更多),调整终端的大小。查看它多次刷新/重新绘制屏幕,可能是针对运行的每个实例。试着回到父母身边,似乎从这一点开始,他们都在试着读德斯丁和重绘它的屏幕的同时,真正的混乱发生了。如果你运气好的话,你可以按下CTRL+C或者pkill testprogram
  • 使用system()函数尝试上面的操作。这个问题并没有发生。出于测试的目的,第一行是#define,可以轻松地从fork/exec/wait切换到system()。
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-11-04 14:12:44

阻止SIGWINCH解决了这个问题,正如@KarstenKoop的评论所建议的那样。

根据unix.stackexchange.com/q/85364/114939检查进程正在监听的信号,我们可以看到没有任何进程阻止信号SIGWINCH,甚至system()发出的进程也是如此。

但是,有了这个在/proc/{pid}/status.之前阻止SIGWINCH的解决方案,我们可以确认它在fork/exec/wait中确实被标记为阻塞就像SigBlk: 0000000008010000一样,0x8是被阻塞的SIGWINCH。

虽然它解决了这个问题,但我不明白system()如何在不阻塞SIGWINCH的情况下启动进程,甚至一切都很好。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40219248

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档