目录
前言:
一.五种IO模型
二.同步通信和异步通信对比
三.阻塞和非阻塞
四.其它高级IO
4.1 非阻塞IO
内存于外设进行数据交互叫做IO。
IO的过程中主要由两个动作,一个是等待,一个是拷贝。
比如:
读IO,就是在等待可以读数据的条件,条件成立将数据从内核空间拷贝到用户空间。
写IO,就是在等待可以写数据的条件,条件成立将数据从用户空间拷贝到内核空间。
高效IO的本质是:单位时间内,尽可能的减少等待的比重。
所有套接字,默认方式都是等待。阻塞IO是最常见的IO模型。
非阻塞IO需要程序员编码时,以循环的方式反复去尝试读写文件描述符,这个过程叫轮询。这对CPU来说是一种较大的浪费,一般只有在特定的情况下才使用。
SIGIO信号:文件描述符准备就绪, 可以开始进行输入/输出操作,默认捕捉动作是忽略。我们可以自定义捕捉动作为recvfrom或者sendto。当信号到来时,就可以将数据从内核拷贝到用户,或者从用户拷贝到内核。
注意:数据拷贝的过程还是需要进程来做。
通过调用select/pol/epoll可以实现同一时刻等待多个文件描述符的就绪状态。
select/pol/epoll也是负责等待,那为什么效率提高了呢?
因为同一时间等待多个文件,文件就绪的概率增加了,等待的时间就减少了。
当等待完毕后,调用read或者write进行IO就不需要等待了。
不需要进程等待和将数据空内核空间拷贝到用户空间,这些动作都是内核做的,当内核拷贝完成后,通知进程来使用数据即可。
这个和信号驱动IO不同,信号驱动IO,进程需要自己将数据从内核拷贝到用户。
总结:
前四种IO归类为同步IO,最后一个归类为异步IO。
任何IO过程中,都包含两个动作,一个是等待,另外一个是拷贝,在实际生活中,等待消耗的时间远远大于拷贝的时间。让IO高效,最核心的办法就是让等待的时间尽量少。
同步通信和异步通信关注的是消息通信机制。
拿上面的信号驱动IO举例,信号驱动IO的情况很像异步IO,但是,信号通信IO最后还需要调用者对数据进行拷贝,所以属于同步IO。
在多线程和多进程中,也有同步和互斥的概念。和这里是完全不相同的。
阻塞和非阻塞,是指条件不满足时,等待方式的不同。
非阻塞IO,记录锁,系统V流机制,IO多路转接(IO多路复用),readv和writev函数以及存储映射IO(mmap),这些统称为高级IO。
下面介绍两种IO,非阻塞IO和IO多路转接
一个文件描述符,默认情况下都是阻塞IO。调用fcntl函数可以改变文件描述符为非阻塞IO。
#include
#include
int fcntl(int fd, int cmd, ... /* arg */ );
参数 | 作用 |
fd | 打开的文件描述符 |
cmd | 具体操作 |
... | 可变参数 |
fcntl函数的功能:
我们可以使用第三个功能,获得/设置文件状态标记,就可以将一个文件描述符设置为非阻塞。
使用如下:
void SetNoBlack(int fd){
//获得文件状态
int fl = fcntl(fd,F_GETFL);
if(fl < 0){
perror("fcntl error\n");
return;
}
//设置文件状态
fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}
轮询方式读取标准输入:
轮询方式读取:read当读条件不满足是,实际是阻塞的。当设置为非阻塞后,不断循环调用read,就是在轮询检测read条件是否满足。
说明:read非阻塞状态下,当返回值为小于0时,说明是读的条件不满足,而不是read调用失败。当读条件不满足时,不仅read的返回值回小于0,还会将全局变量错误码errno设置为EAGAIN(一个宏,值为11)。
#include
#include
#include
#include
void SetNoBlack(int fd){
//获得文件状态
int fl = fcntl(fd,F_GETFL);
if(fl < 0){
perror("fcntl error\n");
return;
}
//设置文件状态
fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}
int main(){
//将标准输入设置为非阻塞
SetNoBlack(0);
char c = 0;
//轮询检测read
while(1){
sleep(1);
//printf("begin to read\n");
ssize_t n = read(0, &c, 1);
if(n > 0){
printf("%c\n",c);
}
//并不是read错误,而是read的条件不满足
else if(n < 0 && errno == EAGAIN){
printf("read cond is not met....\n");
}
else{
perror("read error\n");
}
printf("------------------\n");
}
return 0;
}
为了看起来方便,我将多路转接IO写在了另外一篇博客。
高效IO——IO多路转接select
高效IO——多路转接之poll
高效IO——多路转接epoll