2016-04-17 147 views
2

可以在管道上執行非阻塞的I/O嗎? fcntl無法設置O_NONBLOCK。 Linux編程接口的頁面918包含一個表'從管道或FIFO(p)'讀取n個字節的語義。該表列出了一列標題爲O_NONBLOCK的管道和FIFO的行爲?這意味着你可以在管道上設置O_NONBLOCK標誌。它是否正確?以下代碼未能設置標誌,fcntl(2)雖然不報告錯誤。管道上的非阻塞讀取

#include <fcntl.h> 
#include <sys/wait.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <unistd.h> 

#define SLEEP 1 

int 
main(int argc, char *argv[]) { 
    pid_t childPid; 
    int pfd[2]; 
    int nread, flags; 
    int c = 'a';  

    setbuf(stdout, NULL); 

    if (pipe(pfd) == -1) { 
     printf("error: pipe"); 
     exit(EXIT_FAILURE); 
    } 

    switch (childPid = fork()) { 
    case -1: 
     printf("error: fork"); 
     exit(EXIT_FAILURE); 
    case 0:   /* child */ 
     if (close(pfd[0]) == -1) { 
      printf("child: close pfd read"); 
      exit(EXIT_FAILURE); 
     } 
     sleep(SLEEP); 
     _exit(EXIT_SUCCESS); 
    default: 
     break; 
       /* parent falls through */ 
    } 
    if (close(pfd[1]) == -1) { 
     printf("parent: close pipe write"); 
     exit(EXIT_FAILURE); 
    } 

    flags = fcntl(pfd[0], F_GETFD); 
    flags |= O_NONBLOCK; 
    if (fcntl(pfd[0], F_SETFD, flags)) 
     perror("fcntl"); 

    /* verify flags set correctly */ 
    flags = fcntl(pfd[0], F_GETFD); 
    if (!(flags & O_NONBLOCK)) { 
     printf("failed to set O_NONBLOCK\n"); 
     exit(EXIT_FAILURE); 
    } 

    wait(NULL); 
    exit(EXIT_SUCCESS); 
} 
+0

它看起來像'F_GETFD'操作中的一個錯誤。我已經確認這確實將描述符設置爲非阻塞,並且操作不會阻塞,但是'F_GETFD'仍然返回零。 –

回答

7

沒有什麼特別的管道和O_NONBLOCK。以下示例按預期工作。我沒有檢查每次調用的每個retval,以使該示例更具可讀性。真實世界的應用程序必須執行檢查。

#include <unistd.h> 
#include <stdio.h> 
#include <string.h> 
#include <fcntl.h> 

int main() 
{ 
    int fds[2]; 
    pid_t pid; 
    char buf[100]; 

    pipe(fds); 

    pid = fork(); 

    if (pid) 
    { 
     while (1) 
     { 
      memcpy(buf, "abcdefghi\0",10); 
      write(fds[1], buf, 10); 
      sleep(2); 
     } 
    } 
    else 
    { 
     int retval = fcntl(fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | O_NONBLOCK); 
     printf("Ret from fcntl: %d\n", retval); 
     while (1) 
     { 
      ssize_t r=read(fds[0], buf, 10); 
      printf("read: %d\n", r); 

      if (r > 0) 
      { 
       printf("Buffer: %s\n", buf); 
      } 
      else 
      { 
       printf("Read nothing\n"); 
       perror("Error was"); 
       sleep(1); 
      } 
     } 
    } 
} 

寫我的例子後,我檢查你的代碼,發現:

flags = fcntl(pfd[0], F_GETFD); 
flags |= O_NONBLOCK; 
if (fcntl(pfd[0], F_SETFD, flags)) 

請更改F_SETFDF_SETFL,也爲get操作。你會不會改變file descriptor flagsfile descriptor status flags :-)

man 3 fcntl

文件描述符標誌 以下命令處理與文件 描述符關聯的標誌。目前,只有一個這樣的標誌被定義:FD_CLOEXEC, 關閉執行標誌。如果FD_CLOEXEC位爲0,則文件描述符 將在execve(2)之間保持打開狀態,否則它將被關閉。

文件狀態標誌 每個打開的文件的描述有一定的相關聯的狀態標誌,ini- 由tialized開放(2),並可能通過的fcntl改性()。重複文件 描述符(用dup(2),fcntl(F_DUPFD),fork(2)等製作)參考 相同的打開文件描述,並因此共享相同的文件狀態 標誌。

F_SETFL(int) 將文件狀態標誌設置爲由arg指定的值。忽略arg中的文件 訪問模式(O_RDONLY,O_WRONLY,O_RDWR)和文件創建標誌 (即,O_CREAT,O_EXCL,O_NOCTTY,O_TRUNC)。 在Linux上,此命令只能更改O_APPEND,O_ASYNC, O_DIRECT,O_NOATIME和O_NONBLOCK標誌。不可能 更改O_DSYNC和O_SYNC標誌;請參閱下面的BUGS。

+0

克勞斯好,我有一種感覺,這是我正在做的傻事。感謝您的時間。 – Tobin