Linux下的非阻塞IO(一)

非阻塞IO是相對於傳統的阻塞IO而言的。函數

咱們首先須要搞清楚,什麼是阻塞IO。APUE指出,系統調用分爲兩類,低速系統調用和其餘,其中低速系統調用是可能會使進程永遠阻塞的一類系統調用。可是與磁盤IO有關的系統調用是個例外。spa

咱們以read和write爲例,read函數讀取stdin,若是是阻塞IO,那麼:code

若是咱們不輸入數據,那麼read函數會一直阻塞,一直到咱們輸入數據爲止。blog

若是是非阻塞IO,那麼:進程

若是存在數據,讀取而後返回,若是沒有輸入,那麼直接返回-1,errno置爲EAGAINstring

咱們用write作一個實驗:it

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <errno.h>
#include <signal.h>

char buf[500000];

int main(int argc, const char *argv[])
{
    int ntowrite, nwrite;

    ntowrite = read(STDIN_FILENO, buf, sizeof buf);
    fprintf(stderr, "read %d bytes\n", ntowrite);

    activate_nonblock(STDOUT_FILENO, O_NONBLOCK);

    char *ptr = buf;
    int nleft = ntowrite; //剩餘的字節數
    while(nleft > 0)
    {
        errno = 0;
        nwrite = write(STDOUT_FILENO, ptr, nleft);
        fprintf(stderr, "nwrite = %d, errno = %d\n", nwrite, errno);

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

    deactivate_nonblock(STDOUT_FILENO);

    return 0;
}

該程序向標準輸出寫入500000個字節。io

若是使用: class

./test < test.mkv > temp.file

那麼輸出結果爲:test

read 500000 bytes
nwrite = 500000, errno = 0

由於磁盤IO的速度較快,因此一次就能夠寫入,下面咱們使用終端:

./test < test.mkv  2> stderr.txt

這行命令將500000的內容打印到屏幕上,同時將fprintf記錄的信息經過標準錯誤流寫入stderr.txt。

咱們查看stderr.txt文件:

read 500000 bytes
nwrite = 12708, errno = 0
nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = 11687, errno = 0
nwrite = -1, errno = 11
…………………………………………..

nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = -1, errno = 11
nwrite = 1786, errno = 0

採用命令統計了一下,總計read次數爲15247次,其中返回-1次數爲15203次,說明成功讀取次數爲44次。

上面例子中,這種採用非阻塞IO的方式稱爲「輪詢」,顯然這是一種低效的方式,非阻塞IO一般與IO複用模型結合使用。

 

另外,將fd設置爲阻塞和非阻塞的函數代碼以下:

void activate_nonblock(int fd)
{
    int ret;
    int flags = fcntl(fd, F_GETFL);
    if (flags == -1)
        ERR_EXIT("fcntl");
    flags |= O_NONBLOCK;
    ret = fcntl(fd, F_SETFL, flags);
    if (ret == -1)
        ERR_EXIT("fcntl");
}


void deactivate_nonblock(int fd)
{
    int ret;
    int flags = fcntl(fd, F_GETFL);
    if (flags == -1)
        ERR_EXIT("fcntl");
    flags &= ~O_NONBLOCK;
    ret = fcntl(fd, F_SETFL, flags);
    if (ret == -1)
        ERR_EXIT("fcntl");
}

 

未完待續。

相關文章
相關標籤/搜索