阻塞和非阻塞

  • 没有阻塞 - 读写常规文件 - read会在有限时间返回,只是文件大会慢一点

  • 产生阻塞

    • 读写设备文件 - 没有换行符

      • 设备在/dev/下

      • /dev/tty/ 对应终端文件,如屏幕

    • 读写网络文件 - 没有收到数据包

阻塞:进程调用到一个被阻塞的函数,则会被挂起置为睡眠状态,内核调度其他进程运行

阻塞是设备文件或网络文件的属性,而不是read、write导致的。

文件属性是可以改的,重新打开并加上O_NONBLOCK

设置非阻塞后,如果不输入,read就读不到东西,这时候返回0,就意味着读完了,不对。因此会返回-1,设置errno为EAGAIN或者EWOULDBLOCK,意味着非阻塞状态+无数据。

总结:read返回值为-1并且errno=EAGAIN或EWOULDBLOCK,说明不是read失败,而是非阻塞+无数据。

测试代码

COPY

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

#define BUFFERSIZE 1024

int main()
{
    //buffer
    char readbuf[BUFFERSIZE];
    //return
    int readrt;
    //set non-block
    int fd;
    fd = open("/dev/tty", O_NONBLOCK|O_RDONLY);
    //check return value
    if(fd<0)
    {
        perror("open tty failed");
        exit(1);
    }

//tryagain:

    // is read?
    int readflag = 0;
    while(readflag != 1)
    {
        //read from keyboard
        readrt = read(fd, readbuf, 10);
        //check read return value
        if(readrt<0)
        {
            if(errno!=EAGAIN)
            {
                perror("read STDIN_FILENO failed");
                exit(1);
            }
            else 
            {
                write(STDOUT_FILENO, "try again\\n", strlen("try again\\n"));
                                //wait 2s
                sleep(2);
                //goto tryagain;
            }
        }
        else
        {
            readflag = 1;
        }
    }
    write(STDOUT_FILENO, readbuf, readrt);
    close(fd);
    return 0;
}

设置超时

让终端一直这样占着也不行。一般会设置超时,超时了就关掉。

COPY

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

#define BUFFERSIZE 1024

int main()
{
    //buffer
    char readbuf[BUFFERSIZE];
    //return
    int readrt;
    //set non-block
    int fd;
    fd = open("/dev/tty", O_NONBLOCK|O_RDONLY);
    //check return value
    if(fd<0)
    {
        perror("open tty failed");
        exit(1);
    }

//tryagain:

    // is read?
    int readflag = 0;
    //timeout
    ***int timeout = 0;
    //5 times at most***
    while(readflag != 1 && timeout < 5)
    {
        //read from keyboard
        readrt = read(fd, readbuf, 10);
        //check read return value
        if(readrt<0)
        {
            if(errno!=EAGAIN)
            {
                perror("read STDIN_FILENO failed");
                exit(1);
            }
            else 
            {
                write(STDOUT_FILENO, "try again\\n", strlen("try again\\n"));
                sleep(2);
                //goto tryagain;
            }
        }
        else
        {
            readflag = 1;
        }
        timeout++;
    }

    ***if(timeout==5)
    {
        perror("timeout!");
        exit(1);
    }***

    write(STDOUT_FILENO, readbuf, readrt);
    close(fd);
    return 0;
}

但是最好的方法是设备有数据才来找我(响应模式),现在是轮询模式