何为IO
I/O指的是input和output,即输入和输出,深入的说是指数据的写入或读取。
读操作read
在这种操作中,操作系统会检查内核缓冲区有无需要的数据,如果有数据,就把内核缓冲区的数据拷贝到用户空间,供用户的应用程序使用。
写操作write
用户的数据从用户的空间copy到内核缓冲区中,内核缓冲区中的数据何时通过磁盘或者网络读取发送则与操作系统有关。
IO的阻塞与非阻塞
对于读操作来说,操作系统会检查内核缓冲区是否有数据,如果有数据就会进行正常的读操作,将数据从缓冲区拷贝到用户空间。但是,如果进行读操作时,如果内核缓冲区无数据,此时就会牵扯到IO的阻塞与非阻塞。
这里用IO读取函数recv来说明何为阻塞与非阻塞IO。函数原型如下:
int recv( SOCKET s, char *buf, int len, int flags);
- s表示一个接收端的套接字文件描述符(关于文件描述符的定义可参考其他博客)
- buf为指明的一个用户缓冲区,用来存放recv接受到的数据
- len表示用户缓冲区的长度
- 一般置为0
阻塞IO
如果我们的文件描述符s属性为阻塞(blocking),则调用recv时会进行阻塞IO。如果调用recv时,缓冲区中不存在数据,该线程会被挂起,只有等到得到结果后才会返回,即线程会阻塞到recv函数,不会继续向下执行,直到缓冲区被写入数据。
非阻塞IO
若s的属性被我们设置为非阻塞(默认一般都为阻塞,非阻塞需要手动设置),会进行非阻塞IO。当缓冲区中不存在数据时,线程不会被挂起,函数会立刻返回。对于函数recv来说,当其无法立即从缓冲区中读取数据时,会返回-1,并且将errno的值置为EAGAIN,表示此次IO操作并非出错,而是缓冲区无数据。
注意:对于非阻塞IO来说,EAGAIN一般不认为是一种错误,其值与EWOULDBLOCK相同。
二者的区别可以看用户程序的调用是否立即返回!