2016-10-24 9 views
1

fork/exec/waitを使用して、いくつかのcursesベースのプログラムが起動され、その中にプロセスチェーンが作成されます。チェーンされたncursesプログラムのターミナルのサイズ変更(fork/exec/wait)

xtermのサイズが変更され、最初のプログラムのみが動作していれば、すべて正常に動作します。しかし、2番目(または3番目)のプログラムが実行されている場合:

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(); 

すべてのプログラムは同時に画面をリフレッシュしようとしています。この時点から画面は真面目なものになります。

waitが返るまで、各プログラムで何をする必要があるのですか?exec次のプログラムは、「凍結」しますか?

EDIT:

私はsystem()fork/exec/waitを交換するとき、すべてが正常に動作します。しかし、私はそれを保つことができません(これは単なるテストでした)、これはシステムコールである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以上)を開き、端末のサイズを変更します。画面を複数回更新/再描画するようにしてください。恐らく、実行中のインスタンスごとに1つずつです。親に戻るようにしてください、この時点から、すべての人が標準を読み込んで同時にその画面を再描画しようとしているようですが、本当の混乱が起こります。運があればCTRL+Cpkill testprogramのいずれかのタイプを押すことができます。
  • 上記のsystem()関数を使用してください。問題は発生しません。テスト目的のために、最初の行は#defineでfork/exec/waitからsystem()に簡単に切り替えることができます。
+1

おそらくプログラムがある時点で待ち合わせを見逃していると推測するには十分なコードがありません。 –

+0

あなたは 'resetty();を交換しようとしましたか?リフレッシュ(); ' - >>' refresh();最後の行でresetty(); '? – joop

+0

@joop:いいえ動作しません。私は完全なサンプルを追加した、あなた自身で試すことができます。 – Luciano

答えて

0

@ KarstenKoopのコメントで示唆されているように、SIGWINCHをブロックすることで問題が解決されました。

unix.stackexchange.com/q/85364/114939に従ってプロセスがリッスンしている信号を確認すると、system()によって取得されたプロセスであってもシグナルSIGWINCHをブロックするプロセスはないことがわかります。

しかし、我々は/ procの/ {PID} /ステータスにブロックされたとして、それが本当にフラグが立てられていますことを確認することができますfork/exec/wait前にどのブロックSIGWINCHこのソリューションを持ちます。 SigBlk:0000000008010000のように、0x8はSIGWINCHがブロックされている場所です。

問題を修正しましたが、system()がSIGWINCHをブロックせずにプロセスを起動することがわかりません。したがって、すべて正常に動作します。

関連する問題