阻塞与非阻塞是对于文件而言的,而不是指read、write等的属性。

阻塞IO

应用程序调用IO函数,导致应用程序阻塞,等待数据准备好。如果数据没有准备好,一直等待数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。

阻塞、非阻塞IO_IO

读常规文件是不会阻塞的,不管读多少字节,read一定会在有限的时间内返回。一般网络、终端设备IO都是阻塞I/O。

如果从终端输入的数据没有换行符,调用read读终端设备就会阻塞,如果网络上没有接收到数据包,调用read从网络读就会阻塞,至于会阻塞多长时间也是不确定的,如果一直没有数据到达就一直阻塞在那里。

示例代码:

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

int main()
{
char buf[1024] = {0};
printf("读数据前:\n");
read(0, buf, sizeof(buf)); //默认是阻塞的,从标准输入读取内容
printf("读取数据后:buf = %s\n", buf);

return 0;
}

运行结果:

阻塞、非阻塞IO_IO_02

非阻塞IO

我们把一个文件设置为非阻塞就是告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误(EAGAIN)。

这样我们的I/O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间,所有一般Web服务器都不使用这种I/O模型。

阻塞、非阻塞IO_非阻塞_03

示例代码:

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

int main()
{
int fd;
//以非阻塞方式打开当前终端
fd = open("/dev/tty", O_RDONLY|O_NONBLOCK);
if(fd < 0)
{
perror("open\n");
return -1;
}

char buf[1024];
while(1)
{
int n = read(fd, buf, sizeof(buf));
printf("n = %d, errno = %d, EAGAIN = %d\n", n, errno, EAGAIN);
sleep(1); //延时

if(n <= 0)
{
//如果为非阻塞,但是没有数据可读,此时全局变量 errno 被设置为 EAGAIN
if(errno == EAGAIN)
{
printf("没有数据可读\n");
}
else
{
perror("read");
break;
}
}
else
{//有数据
printf("buf = %s\n", buf);
break;
}
}

return 0;
}

运行结果:

阻塞、非阻塞IO_系统调用_04