2017-01-19 21 views
1

Я не понимаю поведение pselect. В основном то, что я делаю следующее:pselect уведомляет обе файловые дескрипторы конца файла, хотя только один конец записи был записан на

  • Зарегистрировать обработчик сигнала SIGCHILD
  • Создание двух труб
  • Создать дочерний процесс с помощью вилки
  • сна у ребенка в течение 5 секунд, затем exit
  • В родительском процессе вызывается pselect, ожидающий на считываемых концах двух труб
  • Когда дочерний процесс завершает запись чего-то в первом трубе изнутри обработчик SIGCHILD.
  • pselect возвращается в родительский процесс с обоих файловых дескрипторов набор

Я ожидаю выход следующего кода будет:

Pipe1 is set! 

Но вместо этого я получаю:

Pipe1 is set! 
Pipe2 is set! 

Почему оба дескриптора дескриптора конца файла установлены, когда я пишу только в одном конце записи? Является ли это частью поведения нормального pselect уведомления о ложных файловых дескрипторах? Что я делаю не так?

Вот программа:

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

    enum PipeEnd { 
     READ_END = 0, WRITE_END = 1, MAX_END 
    }; 

    int pipe1[MAX_END], pipe2[MAX_END]; 

    void handle_sigchld(const int signal) { 
     //In the SIGCHLD handler write the process ID on pipe1 
     int returnStatus; 
     int childPID = waitpid(static_cast<pid_t>(-1), &returnStatus, WNOHANG); 
     write(pipe1[WRITE_END], &childPID, sizeof(childPID)); 
    } 

    void createPipe(int newPipe[MAX_END]) { 
     pipe(newPipe); 
     fcntl(newPipe[READ_END], F_SETFL, O_NONBLOCK); 
     fcntl(newPipe[WRITE_END], F_SETFL, O_NONBLOCK); 
    } 

    int main(int argc, const char** argv) { 
     //Add a handler for the SIGCHLD signal 
     struct sigaction sa; 
     sa.sa_handler = &handle_sigchld; 
     sigemptyset(&sa.sa_mask); 
     sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; 
     sigaction(SIGCHLD, &sa, nullptr); 

     //Create two pipes 
     createPipe(pipe1); 
     createPipe(pipe2); 

     //Create a child process 
     if (0 == fork()) { 
      sleep(5); 
      exit(0); 
     } 

     fd_set read_fds; 
     FD_ZERO(&read_fds); 
     FD_SET(pipe1[READ_END], &read_fds); 
     FD_SET(pipe2[READ_END], &read_fds); 
     int maxfd = std::max(pipe1[READ_END], pipe2[READ_END]); 
     //Wait for a file descriptor to be notified 
     pselect(maxfd + 1, &read_fds, nullptr, nullptr, nullptr, nullptr); 

     //Check if the read ends of the two pipes are set/notified 
     if (FD_ISSET(pipe1[READ_END], &read_fds)) 
      std::cout << "Pipe1 is set!" << std::endl; 
     if (FD_ISSET(pipe2[READ_END], &read_fds)) 
      std::cout << "Pipe2 is set!" << std::endl; 
     return 0; 
    } 

ответ

1

Вы будете удивлены, что программа имеет такое же поведение, даже если обработчик сигнала не write ничего.

Причина в том, что pselect не удается. Цитирование man 7 signal,

следующие интерфейсы никогда не перезагружается после прерывания обработчиком сигнала, независимо от использования SA_RESTART; они всегда терпят неудачу с EINTR ошибки, когда прерывается обработчиком сигнала:

....

  • файла дескриптора мультиплексирование интерфейсов: epoll_wait (2), epoll_pwait (2), опрос (2), ppoll (2), выберите (2) и pselect (2).

Всегда проверяйте, что возвращает системный вызов.

+0

Я подозревал, что что-то подобное произошло, когда сигнал был получен в pselect. Большое спасибо! –