macOS의 FIFO에서 선택(2)

macOS의 FIFO에서 선택(2)

Linux에서는 포함된 프로그램이 다음 select위치에서 반환되고 종료됩니다.

$ gcc -Wall -Wextra select_test.c -o select_test
$ ./select_test
reading from read end
closing write end
first read returned 0
second read returned 0
selecting with read fd in fdset
select returned

OS X에서는 select영원히 차단되며 프로그램은 절대 종료되지 않습니다. Linux는 예상대로 작동하며 POSIX 매뉴얼 페이지의 다음 선택 섹션을 준수하는 것 같습니다.

O_NONBLOCK이 지워진 입력 함수에 대한 호출이 차단되지 않으면 설명자는 함수가 데이터를 성공적으로 전송했는지 여부에 관계없이 읽을 준비가 된 것으로 간주됩니다. (함수는 데이터, 파일 끝 표시 또는 차단되었음을 나타내는 오류를 반환할 수 있으며, 각각의 경우 설명자를 읽을 준비가 된 것으로 간주해야 합니다.)

fifo의 읽기 측에 있는 read(2)는 항상 를 반환하므로 EOF내 읽기에 따르면 항상 선택에 의해 준비된 것으로 간주되어야 합니다.

macOS의 동작은 잘 알려져 있거나 예상됩니까? 이 예에서 행동 차이에 기여하는 다른 요인이 있습니까?

read이러한 호출을 제거하면 macOS가 select대체됩니다. 이 실험과 다른 실험에서는 일단 파일에서 EOF를 읽으면 select나중에 호출해도 더 이상 준비된 것으로 표시되지 않는다는 것을 나타내는 것 같습니다.

샘플 프로그램

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define FILENAME "select_test_tmp.fifo"

int main() 
{
    pid_t pid;
    int r_fd, w_fd;
    unsigned char buffer[10];
    fd_set readfds;

    mkfifo(FILENAME, S_IRWXU);

    pid = fork();
    if (pid == -1) 
    {
        perror("fork");
        exit(1);
    }

    if (pid == 0) 
    {
        w_fd = open(FILENAME, O_WRONLY);

        if (w_fd == -1) 
        {
            perror("open");
            exit(1);
        }

        printf("closing write end\n");
        close(w_fd);
        exit(0);
    }

    r_fd = open(FILENAME, O_RDONLY);
    if (r_fd == -1) 
    {
        perror("open");
        exit(1);
    }

    printf("reading from read end\n");

    if (read(r_fd, &buffer, 10) == 0) 
    {
        printf("first read returned 0\n");
    } 
    else 
    {
        printf("first read returned non-zero\n");
    }

    if (read(r_fd, &buffer, 10) == 0) 
    {
        printf("second read returned 0\n");
    } 
    else 
    {
        printf("second read returned non-zero\n");
    }

    FD_ZERO(&readfds);
    FD_SET(r_fd, &readfds);

    printf("selecting with read fd in fdset\n");
    if (select(r_fd + 1, &readfds, NULL, NULL, NULL) == -1) 
    {
        perror("select");
        exit(1);
    }

    printf("select returned\n");
    unlink(FILENAME);
    exit(0);
}

답변1

Macintosh에서는 이 함수를 통해 파이프가 소켓과 동일하게 처리됩니다 read. 이 동작은 read파일에 액세스하려고 시도 select_test_tmp.fifo하고 무료 입력이 있는 한 차단되기 때문에 발생합니다. 기본적으로 EOF는 각 쓰기 작업 후에 파이프에 기록됩니다.

이를 확인하는 한 가지 방법은 cat select_test_tmp.fifo명령줄에서 실행하는 것입니다. 먼저 종료하지 않는 한 반환하기 전에 입력을 받을 때까지 기다립니다.

답변2

귀하의 가정이 잘못되었습니다.

예상되는 동작은 모든 작성자가 보유하고 있는 FIFO의 쓰기 쪽을 닫으면 읽기 쪽에서 read0바이트를 반환하는 호출을 제공하는 것입니다. 이후에는 extra를 호출 하면 안 됩니다 read. 이 작업을 수행하려면 읽기 측을 닫았다가 다시 열어야 하며, 파이프인 경우 다시 만들어야 합니다.

이 규칙을 따르면 플랫폼 전반에서 일관된 동작을 얻을 수 있습니다.

다음은 두 플랫폼 모두에서 올바르게 실행되는 귀하의 프로그램에서 영감을 받은 샘플 프로그램입니다.

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define FILENAME "select_test_tmp.fifo"

int main()
{
    pid_t pid;
    int r_fd, w_fd;
    unsigned char buffer[8192];
    fd_set master_set, working_set;
    char const * message = "Hello there\n";
    mkfifo(FILENAME, S_IRWXU);

    pid = fork();
    if (pid < 0) exit(1);

    if (pid == 0) {
        w_fd = open(FILENAME, O_WRONLY);
        if (w_fd == -1) exit(1);
        write(w_fd, message, strlen(message));
        close(w_fd);
        exit(0);
    }

    r_fd = open(FILENAME, O_RDONLY);
    if (r_fd < 0) exit(1);

    FD_ZERO(&master_set);
    FD_SET(r_fd, &master_set);
    int finished = 0;
    while (!finished) {
      memcpy(&working_set, &master_set, sizeof(master_set));
      int rc = select(r_fd + 1, &working_set, NULL, NULL, NULL);
      if (rc < 1) exit(1); // No timeout so rc == 0 is also an error here
      for (int fd = 0; fd < r_fd +1; ++fd) {
        if (FD_ISSET(fd, &working_set)) {
          if (fd == r_fd) { // Our fifo
            // Read data and print it in stdout
            ssize_t nb_bytes = read(r_fd, buffer, 8192);
            if (nb_bytes < 0) {
              exit(1);
            }
            else if (0 == nb_bytes) {
              finished = 1;
            }
            else {
              write(1,buffer,nb_bytes);
            }
          }
        }
      }
    }
    unlink(FILENAME);
    exit(0);
}

관련 정보