2016-10-24 2 views
1

Существует несколько программ на основе проклятий, которые запускаются, создавая цепочку процессов среди них, используя fork/exec/wait.Терминал изменяет размер в цепных программах ncurses (fork/exec/wait)

Когда размер xterm изменен и существует только первая работающая программа работает отлично. Но, когда выполняется вторая (или третья) программа:

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?

EDIT:

Когда я заменить fork/exec/wait на system(), все работает отлично. Но я не могу его сохранить (это был просто тест), это большая система устаревания, сильно зависящая от системных вызовов fork/exec/wait.

Я также попытался запустить execlp("/bin/sh", "-c", cmd), и проблема в том же.

EDIT 2: Полный пример кода с нуля:

// 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); 
     } 
    } 
} 

Инструкции:

  • типа "R", чтобы породить прикован новый экземпляр себе.
  • Тип "." (точка) для выхода и возврата к родительскому процессу.
  • Во время первого (основного) процесса измените размер терминала. Все работает нормально.
  • ПРОБЛЕМА: Откройте много экземпляров (10 или более), измените размер терминала. Посмотрите, что он обновляет/перерисовывает экран несколько раз, вероятно, один для каждого запускаемого экземпляра. Попытайтесь вернуться к родителям, кажется, что с этого момента все они пытаются прочитать stdin и перерисовать его экран в одно и то же время, происходит настоящий беспорядок. Если вам повезет, вы сможете нажать CTRL+C либо на тип pkill testprogram.
  • Попробуйте использовать функцию system(). Проблема не возникает. Для целей тестирования на первой строке есть #define, чтобы легко переключаться с fork/exec/wait на system().
+1

Недостаточно кода для того, чтобы предположить, что, возможно, ваша программа в какой-то момент перестанет ждать. –

+0

Вы пробовали замену 'resetty(); refresh(); '- >>' refresh(); resetty(); 'в последней строке? – joop

+0

@joop: Нет. Не работает. Я добавил полный образец, вы можете попробовать сами. – Luciano

ответ

0

Блокировка SIGWINCH устраняет проблему, как предлагается комментарием @ KarstenKoop.

Проверка сигналов, прослушиваемых процессами в соответствии с unix.stackexchange.com/q/85364/114939, мы видим, что никакие процессы не блокируют сигнал SIGWINCH, даже процессы, очищенные системой().

Но, имея решение, которое блокирует SIGWINCH до того fork/exec/wait мы можем подтвердить, что это действительно помечено как заблокированы в/Proc/{PID}/состоянии. Как и SigBlk: 0000000008010000, где 0x8 блокируется SIGWINCH.

Хотя проблема была решена, я не понимаю, что делает system() для запуска процессов без блокировки SIGWINCH и даже при этом все работает нормально.