<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 網絡,第 7 部分:非阻塞 I O,select()和 epoll > 原文:<https://github.com/angrave/SystemProgramming/wiki/File-System%2C-Part-7%3A-Scalable-and-Reliable-Filesystems> ### 不要浪費時間等待 通常,當您調用`read()`時,如果數據不可用,它將等到數據準備就緒,然后函數返回。當您從磁盤讀取數據時,該延遲可能不會很長,但是當您從慢速網絡連接讀取時,如果數據到達,則可能需要很長時間才能到達該數據。 POSIX 允許您在文件描述符上設置一個標志,以便對該文件描述符的`read()`的任何調用都將立即返回,無論它是否已完成。使用此模式下的文件描述符,您對`read()`的調用將啟動讀取操作,當它正在工作時,您可以執行其他有用的工作。這稱為“非阻塞”模式,因為對`read()`的調用不會阻止。 要將文件描述符設置為非阻塞: ```c // fd is my file descriptor int flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK); ``` 對于套接字,可以通過將`SOCK_NONBLOCK`添加到`socket()`的第二個參數,在非阻塞模式下創建它: ```c fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); ``` 當文件處于非阻塞模式并且您調用`read()`時,它將立即返回任何可用的字節。假設已從套接字另一端的服務器到達 100 個字節,并調用`read(fd, buf, 150)`。 Read 將立即返回值 100,這意味著它會讀取您要求的 150 個字節中的 100 個。假設您嘗試通過調用`read(fd, buf+100, 50)`來讀取剩余數據,但最后 50 個字節仍未到達。 `read()`將返回-1 并將全局錯誤變量 **errno** 設置為 EAGAIN 或 EWOULDBLOCK。這是系統告訴你數據尚未準備好的方式。 `write()`也適用于非阻塞模式。假設您要使用套接字將 40,000 個字節發送到遠程服務器。系統一次只能發送這么多字節。通用系統一次可以發送大約 23,000 個字節。在非阻塞模式下,`write(fd, buf, 40000)`將返回它能夠立即發送的字節數,或大約 23,000。如果你再次調用`write()`,它將返回-1 并將 errno 設置為 EAGAIN 或 EWOULDBLOCK。這是系統告訴你它仍然忙于發送最后一塊數據的方式,并且尚未準備好發送更多數據。 ### 如何檢查 I / O 何時完成? 有幾種方法。讓我們看看如何使用 _ 選擇 _ 和 _epoll_ 來做到這一點。 #### 選擇 ```c int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); ``` 給定三組文件描述符,`select()`將等待任何這些文件描述符變為“就緒”。 * `readfds` - 當有數據可以讀取或達到 EOF 時,`readfds`中的文件描述符就緒。 * `writefds` - 當對 write()的調用成功時,`writefds`中的文件描述符就緒。 * `exceptfds` - 系統特定的,沒有明確定義。只需為此傳遞 NULL。 `select()`返回準備好的文件描述符總數。如果它們在 _ 超時 _ 定義的時間內沒有準備就緒,它將返回 0.在`select()`返回后,調用者需要遍歷 readfds 和/或 writefds 中的文件描述符以查看哪些文件描述符準備好了。由于 readfds 和 writefds 同時充當輸入和輸出參數,當`select()`指示存在準備好的文件描述符時,它將覆蓋它們以僅反映??準備好的文件描述符。除非調用者只打算調用`select()`一次,否則在調用它之前保存 readfds 和 writefds 的副本是個好主意。 ```c fd_set readfds, writefds; FD_ZERO(&readfds); FD_ZERO(&writefds); for (int i=0; i < read_fd_count; i++) FD_SET(my_read_fds[i], &readfds); for (int i=0; i < write_fd_count; i++) FD_SET(my_write_fds[i], &writefds); struct timeval timeout; timeout.tv_sec = 3; timeout.tv_usec = 0; int num_ready = select(FD_SETSIZE, &readfds, &writefds, NULL, &timeout); if (num_ready < 0) { perror("error in select()"); } else if (num_ready == 0) { printf("timeout\n"); } else { for (int i=0; i < read_fd_count; i++) if (FD_ISSET(my_read_fds[i], &readfds)) printf("fd %d is ready for reading\n", my_read_fds[i]); for (int i=0; i < write_fd_count; i++) if (FD_ISSET(my_write_fds[i], &writefds)) printf("fd %d is ready for writing\n", my_write_fds[i]); } ``` [有關 select()](http://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)的更多信息 ## epoll 的 _epoll_ 不是 POSIX 的一部分,但它受 Linux 支持。這是一種等待許多文件描述符的更有效方法。它會告訴你準確的描述符。它甚至為您提供了一種方法,可以使用每個描述符存儲少量數據,如數組索引或指針,從而可以更輕松地訪問與該描述符關聯的數據。 要使用 epoll,首先必須使用 [epoll_create()](http://linux.die.net/man/2/epoll_create)創建一個特殊的文件描述符。您不會讀取或寫入此文件描述符;你只需將它傳遞給其他 epoll_xxx 函數并在結尾處調用 close()。 ```c epfd = epoll_create(1); ``` 對于要使用 epoll 監視的每個文件描述符,您需要使用 [epoll_ctl()](http://linux.die.net/man/2/epoll_ctl)和`EPOLL_CTL_ADD`選項將其添加到 epoll 數據結構中。您可以向其添加任意數量的文件描述符。 ```c struct epoll_event event; event.events = EPOLLOUT; // EPOLLIN==read, EPOLLOUT==write event.data.ptr = mypointer; epoll_ctl(epfd, EPOLL_CTL_ADD, mypointer->fd, &event) ``` 要等待某些文件描述符準備就緒,請使用 [epoll_wait()](http://linux.die.net/man/2/epoll_wait)。它填充的 epoll_event 結構將包含您在添加此文件描述符時在 event.data 中提供的數據。這使您可以輕松查找與此文件描述符關聯的自己的數據。 ```c int num_ready = epoll_wait(epfd, &event, 1, timeout_milliseconds); if (num_ready > 0) { MyData *mypointer = (MyData*) event.data.ptr; printf("ready to write on %d\n", mypointer->fd); } ``` 假設您正在等待將數據寫入文件描述符,但現在您要等待從中讀取數據。只需將`epoll_ctl()`與`EPOLL_CTL_MOD`選項一起使用即可更改您正在監控的操作類型。 ```c event.events = EPOLLOUT; event.data.ptr = mypointer; epoll_ctl(epfd, EPOLL_CTL_MOD, mypointer->fd, &event); ``` 要從 epoll 中取消訂閱一個文件描述符,同時保留其他文件描述符,請將`epoll_ctl()`與`EPOLL_CTL_DEL`選項一起使用。 ```c epoll_ctl(epfd, EPOLL_CTL_DEL, mypointer->fd, NULL); ``` 要關閉 epoll 實例,請關閉其文件描述符。 ```c close(epfd); ``` 除了非阻塞`read()`和`write()`之外,對非阻塞套接字的`connect()`的任何調用也將是非阻塞的。要等待連接完成,請使用`select()`或 epoll 等待套接字可寫。 ## 有趣的 Blogpost 關于邊緣情況與選擇 [https://idea.popcount.org/2017-01-06-select-is-fundamentally-broken/](https://idea.popcount.org/2017-01-06-select-is-fundamentally-broken/)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看