天天看點

APUE程式設計:20---進階I/O之(阻塞IO與非阻塞IO)

一、阻塞IO

  • 在“系統調用中斷”文章中(javascript:void(0))我們将系統調用分為兩類:低速系統調用和其他系統調用
  • 低速系統調用是可能會使程序永遠阻塞的一類系統調用,包括下面:
    • 如果某些檔案類型(如讀管道、終端裝置和網絡裝置)的資料并不存在,讀操作可能會使調用者永遠阻塞
    • 如果資料不能被相同的檔案類型立即接受(如管道中無空間、網絡流控制),寫操作可能會使調用者永遠阻塞
    • 在某種條件發生之前打開某些檔案類型可能會發生阻塞(如要打開一個終端裝置,需要先等待與之連接配接的數據機應答,又如若以隻寫模式打開FIFO,那麼在沒有其他程序已用讀模式打開該FIFO時也要等待)
    • 對已經加上強制性記錄鎖的檔案進行讀寫
    • 某些ioctl操作
    • 某些程序間通信函數
  • 我們也曾說過,雖然讀寫磁盤檔案會暫時阻塞調用者,但并不能将與磁盤I/O有關的系統調用視為“低速”

二、非阻塞IO

  • 概念:非阻塞I/O使我們可以調用不會永遠阻塞的I/O操作,例如open,read和write。如果這種操作不能完成,則立即出錯傳回,表示該操作如繼續執行将繼續阻塞下去
  • 有兩種方法來設定非阻塞IO:
    • 如果是調用open以獲得該描述符,則可指定O_NONBLOCK标志
    • 對于已經打開的一個描述符,則可調用fcntl打開O_NONBLOCK檔案狀态标志
APUE程式設計:20---進階I/O之(阻塞IO與非阻塞IO)

三、非阻塞IO案例

  • 下面是一個非阻塞I/O的執行個體,它從标準輸入讀500000位元組,并試圖将它們寫到标準輸出上。該程式先将标準輸出設定為非阻塞的,然後用for循環進行輸出,每次write的結果都在标準出錯上列印
  • 函數clr_fl清除檔案描述符标志,它清除1個或多個标志位 

代碼如下

#include<errno.h>
#include<fcntl.h>
#include<stdlib.h>
#include<stdio.h>

char buf[500000];
void clr_fl(int fd, int flags);
void set_fl(int fd,int flags);

int main(void)
{
    int ntowrite, nwrite;
    char *ptr;

    ntowrite = read(0, buf, sizeof(buf));//從标準輸入讀取資料
    fprintf(stderr, "read %d bytes\n", ntowrite);

    set_fl(1, O_NONBLOCK); //設定标準輸出非阻塞

    ptr = buf;
    while (ntowrite > 0) {
        errno = 0;
        nwrite = write(1, ptr, ntowrite);//将資料輸出到标準輸出上
        fprintf(stderr, "nwrite = %d, errno = %d\n", nwrite, errno);

        if (nwrite > 0) {
            ptr += nwrite;
            ntowrite -= nwrite;
        }
    }

    clr_fl(2, O_NONBLOCK); //設定标準輸出阻塞
    exit(0);
}
void clr_fl(int fd, int flags)
{
    int val;
    if ((val = fcntl(fd, F_GETFL, 0)) < 0)//先得到
        perror("fcntl F_GETFL error");
    val &= ~ flags; /* turn off flags */
    if (fcntl(fd, F_SETFL, val) < 0)//再設定
        perror("fcntl F_SETFL error");
}
void set_fl(int fd, int flags)
{
    int val;
    if ((val = fcntl(fd, F_GETFL, 0)) < 0)//先得到
        perror("fcntl F_GETFL error");
    val |= flags; /* turn on flags */
    if (fcntl(fd, F_SETFL, val) < 0)//再設定
        perror("fcntl F_SETFL error");
}           
  • 示範①:例如我們從/etc/services檔案中讀取資料,然後将資料輸出到檔案temp.file檔案中,則write隻執行一次
  • 示範②:我們從/etc/services檔案中讀取資料,然後将資料輸出到終端上,結果顯示write有時傳回小于50000的一個數字,有時傳回錯誤
APUE程式設計:20---進階I/O之(阻塞IO與非阻塞IO)

繼續閱讀