2013-10-13 71 views
1

我遇到了ncurses问题,无法在网络上找到解决方案,所以我编写了下面的小程序来演示问题。ncurses在调整终端大小时中断系统调用

您可以通过编译:

sudo aptitude install ncurses-dev 
g++ -lncurses -o resize resize.cpp 

它显示的整数计数器递增每一秒通过分叉成一个定时器过程,其周期性地经由socketpair发送一个字节到父进程。您可以按CTRL + C退出它。

当您调整终端的大小时,您应该会收到'Interrupted system call'中断的错误消息。所以读取调用在调整大小时被SIGWINCH中断。但我怎么能避免这种情况?或者是系统调用被中断的常见现象?但是,我将如何处理中断的系统调用,以便继续增加计数器,因为文件描述符在中断后似乎已经死机。

如果你使用非阻塞套接字,你会得到'资源暂时不可用'。

我使用稳定的debian wheezy,所以ncurses版本是5.9-10,libstdC++版本是4.7.2-5。

#include <ncurses.h> 
#include <signal.h> 
#include <netdb.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <string.h> 
#include <errno.h> 
#include <string> 
#include <iostream> 

//Define a second. 
timespec span = {1, 0}; 

//Handles both, SIGWINCH and SIGINT 
void handle(int signal) { 
    switch (signal) { 
     case SIGWINCH: 
      //Reinitialize ncurses to get new size 
      endwin(); 
      refresh(); 
      printw("Catched SIGWINCH and handled it.\n"); 
      refresh(); 
     break; 
     case SIGINT: 
      //Catched CTRL+C and quit 
      endwin(); 
      exit(0); 
     break; 
    } 
} 

//This registers above signal handler function 
void set_handler_for(int signal) { 
    struct sigaction action; 
    action.sa_handler = handle; 
    action.sa_flags = 0; 
    if (-1 == sigemptyset(&action.sa_mask) or -1 == sigaction(signal, &action, NULL)) 
     throw "Cannot set signal handler"; 
} 

main() { 
    int fd[2]; 
    //In this try block we fork into the timer process 
    try { 
     set_handler_for(SIGINT); 
     set_handler_for(SIGWINCH); 
     //Creating a socketpair to communicate between timer and parent process 
     if (-1 == socketpair(PF_LOCAL, SOCK_STREAM, 0, fd)) 
      throw "Cannot create socketpair"; 
     pid_t pid; 
     //Doing the fork 
     if (-1 == (pid = fork())) 
      throw "Cannot fork process"; 
     if (!pid) { 
      //We are the timer, so closing the other end of the socketpair 
      close(fd[0]); 
      //We send one byte every second to the parent process 
      while (true) { 
       char byte; 
       ssize_t bytes = write(fd[1], &byte, sizeof byte); 
       if (0 >= bytes) 
        throw "Cannot write"; 
       nanosleep(&span, 0); 
      } 
      //Here the timer process ends 
      exit(0); 
     } 
     //We are the parent process, so closing the other end of the socketpair 
     close(fd[1]); 
    } 
    catch (const char*& what) { 
     std::cerr << what << std::endl; 
     exit(1); 
    } 
    //Parent process - Initializing ncurses 
    initscr(); 
    noecho(); 
    curs_set(0); 
    nodelay(stdscr, TRUE); 
    //In this try block we read (blocking) the byte from the timer process every second 
    try { 
     int tick = 0; 
     while (true) { 
      char byte; 
      ssize_t bytes = read(fd[0], &byte, sizeof byte); 
      if (0 >= bytes) 
       throw "Cannot read"; 
      //Clear screen and print increased counter 
      clear(); 
      mvprintw(0, 0, "Tick: %d - Resize terminal and press CTRL+C to quit.\n", ++tick); 
      //Catch special key KEY_RESIZE and reinitialize ncurses to get new size (actually not necassary) 
      int key; 
      while ((key = getch()) != ERR) { 
       if (key == KEY_RESIZE) { 
        endwin(); 
        refresh(); 
        printw("Got KEY_RESIZE and handled it.\n"); 
       } 
      } 
      //Update the screen 
      refresh(); 
     } 
    } 
    catch (const char*& what) { 
     //We got an error - print it but don't quit in order to have time to read it 
     std::string error(what); 
     if (errno) { 
      error.append(": "); 
      error.append(strerror(errno)); 
     } 
     error = "Catched exception: "+error+"\n"; 
     printw(error.c_str()); 
     refresh(); 
     //Waiting for CTRL+C to quit 
     while (true) 
      nanosleep(&span, 0); 
    } 
} 

谢谢!

问候

回答

0

大部分(如果不是全部)系统调用中断错误代码(错误== EINTR),这是正常的。

我会检查从管道读取EINTR并忽略它,只需再次阅读。

我不会在信号处理程序中调用任何ncurses函数,有些函数是可重入的,但我怀疑printw是。只需做KEY_RESIZE检查。

0

好吧,我通过使用信号处理程序中的可重入函数得到了它的工作。现在socketpair仍然在EINTR或EAGAIN之后工作。

谢谢!

#include <ncurses.h> 
#include <signal.h> 

#include <netdb.h> 
#include <unistd.h> 

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

#include <string> 
#include <iostream> 

// Define a second. 
timespec base = {1, 0}; 

// Holds raised SIGINTs. 
size_t raised_SIGINT = 0; 

// Holds raised SIGWINCHs. 
size_t raised_SIGWINCH = 0; 

// Handle SIGWINCH 
void handle_SIGWINCH(int) { 
    ++raised_SIGWINCH; 
} 

// Handle SIGINT 
void handle_SIGINT(int) { 
    ++raised_SIGINT; 
} 

// Registers signal handlers. 
void assign(int signal, void (*handler)(int)) { 
    struct sigaction action; 
    action.sa_handler = handler; 
    action.sa_flags = 0; 
    if (-1 == sigemptyset(&action.sa_mask) or -1 == sigaction(signal, &action, NULL)) 
     throw "Cannot set signal handler"; 
} 

// Prints ticks alive and usage information. 
inline void print(size_t ticks) { 
    mvprintw(0, 0, "%ds alive. Resize terminal and press CTRL+C to quit.\n\n", ticks); 
} 

int main() { 
    // Holds the two socketpair file descriptors. 
    int fd[2]; 

    // Fork into the timer process. 
    try { 
     // Register both signals. 
     assign(SIGINT, handle_SIGINT); 
     assign(SIGWINCH, handle_SIGWINCH); 

     // Create a socketpair to communicate between timer and parent process. 
     if (-1 == socketpair(PF_LOCAL, SOCK_STREAM, 0, fd)) 
      throw "Cannot create socketpair"; 

     // Doing the fork. 
     pid_t pid; 
     if (-1 == (pid = fork())) 
      throw "Cannot fork process"; 
     if (!pid) { 
      // We are the timer, so closing the parent end of the socketpair. 
      close(fd[0]); 

      // We send one byte every second to the parent process. 
      while (true) { 
       timespec less = base; 
       int ret; 

       // Continue sleeping after SIGWINCH but only for the time left. 
       while (-1 == (ret = nanosleep(&less, &less)) and errno == EINTR and raised_SIGWINCH); 

       // Maybe quit by user. 
       if (raised_SIGINT) 
        return 0; 

       // If something went wrong, terminate. 
       if (-1 == ret) 
        throw "Cannot sleep"; 

       // Repeated writing if interrupted by SIGWINCH. 
       char byte; 
       ssize_t bytes; 
       do { 
        // Doing the write. 
        bytes = write(fd[1], &byte, sizeof byte); 
       } 
       while (0 >= bytes and (errno == EAGAIN or errno == EINTR) and raised_SIGWINCH); 

       // Maybe quit by user. 
       if (raised_SIGINT) 
        return 0; 

       // If something went wrong, terminate. 
       if (0 >= bytes) 
        throw "Cannot write"; 
      } 

      // Here the timer process ends. 
      return 0; 
     } 

     // We are the parent process, so closing the timer end of the socketpair. 
     close(fd[1]); 
    } 
    catch (const char*& what) { 
     // Print fatal error and terminate timer process causing parent process to terminate, too. 
     std::cerr << what << std::endl; 
     return 1; 
    } 

    // Initializing ncurses for the parent process. 
    initscr(); 

    // Disable typing. 
    noecho(); 

    // Disable cursor. 
    curs_set(0); 

    // Make reading characters non-blocking. 
    nodelay(stdscr, TRUE); 

    // Catch fatal errors. 
    try { 
     // Holds ticks alive. 
     size_t ticks = 0; 

     // Blockingly read the byte from the timer process awaiking us every second. 
     while (true) { 
      // Print ticks alive before incrementing them. 
      print(ticks++); 

      // Holds typed keys. 
      std::string keys; 

      // Read typed keys. 
      for (int key = getch(); key != ERR; key = getch()) 
       if (key != KEY_RESIZE) 
        keys += key; 

      // Format typed keys string. 
      if (keys.size()) 
       printw("You've typed: "); 
      else 
       keys += "\n"; 
      keys += "\n\n"; 

      // Print typed keys string. 
      printw(keys.c_str()); 

      // Doing the prints. 
      refresh(); 

      // Repeated reading if interrupted by SIGWINCH. 
      ssize_t bytes = 0; 
      bool again = false; 
      do {      
       // Doing the read. 
       char byte; 
       bytes = read(fd[0], &byte, sizeof byte); 
       again = (0 >= bytes and (errno == EAGAIN or errno == EINTR) and raised_SIGWINCH); 

       // Print how often we got interrupted by SIGWINCH per time base. 
       if (again) { 
        // Next two calls are the common way to handle a SIGWINCH. 
        endwin(); 
        refresh(); 

        // For simpicity clear everything. 
        clear(); 

        // Re-print ticks. 
        print(ticks); 

        // Print the interruption counter. 
        printw("%dx catched SIGWINCH per time base.\n\n", raised_SIGWINCH); 

        // Doing the prints. 
        refresh(); 
       } 
      } 
      while (again); 

      // Reset SIGWINCH raises per time base. 
      raised_SIGWINCH = 0; 

      // Maybe quit by user. 
      if (raised_SIGINT) { 
       endwin(); 
       return 0; 
      } 

      // If something went wrong, terminate. 
      if (0 >= bytes) 
       throw "Cannot read"; 
     } 
    } 
    catch (const char*& what) { 
     // We got an error, appending errno if set. 
     std::string error(what); 
     if (errno) { 
      error.append(": "); 
      error.append(strerror(errno)); 
     } 
     error = "Catched exception: "+error+"\n"; 

     // Print the fatal error. 
     printw(error.c_str()); 

     //Doing the print. 
     refresh(); 

     // Waiting for CTRL+C to quit. 
     while (true) 
      nanosleep(&base, 0); 

     // Quit by user. 
     endwin(); 
     return 0; 
    } 
} 
相关问题