高级IO——非阻塞IO

读某些文件时,如果文件没有数据的话,往往会导致读操作会阻塞(休眠)。比如

①读鼠标、键盘等字符设备文件

读键盘阻塞

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<unistd.h>
 4
 5 int main(void)
 6 {
 7     char buf[100]={0};
 8     int ret=0;
 9     while(1)
10     {
11         printf("~~~~~~~~~~~~~\n");
12         ret=read(0,buf,sizeof(buf));
13         if(ret > 0) printf("%s\n",buf);
14         printf("~~~~~~~~~~~~~\n");
15     }
16     return 0;
17 }

读鼠标阻塞

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<unistd.h>
 4 #include<errno.h>
 5 #include<string.h>
 6 #include <sys/types.h>
 7 #include <sys/stat.h>
 8 #include <fcntl.h>
 9
10
11 void print_err(char *str,int line,int err_no)
12 {
13     printf("%d,%s: %s\n",line,str,strerror(err_no));
14     exit(-1);
15 }
16 int main(void)
17 {
18     int cord=0;  //鼠标坐标
19     int ret=0;
20     int mousefd = -1;
21
22     mousefd=open("/dev/input/mouse0",O_RDONLY);
23     if(-1 == mousefd) print_err("open mouse0 fail",__LINE_,errno);
24
25     while(1)
26     {
27         printf("~~~~~~~~~~~~~\n");
28         ret=read(mousefd,&cord,sizeof(cord));
29         if(ret > 0) printf("%d\n",cord);
30         printf("~~~~~~~~~~~~~\n");
31     }
32     return 0;
33 }

read这个函数,不管是成功返回 还是 出错返回,只要返回就接着往下执行

②读管道文件(有名无名)

读普通文件会阻塞吗?

读普通文件时,如果读到了数据就成功返回,如果没有读到数据返回0(注意是返回0,不是出错返回),总之不会阻塞。

总之,不管有没有从普通文件读到数据,读普通文件时都不会阻塞。

写文件时会阻塞吗?

在写某些文件时,当文件不能立即接收写入的数据时,也可能会导致写操作阻塞,一直阻塞到写成功为止。一把来说,写文件不会阻塞,因此我们不考虑写文件阻塞的情况,我们这里只讲读文件阻塞的情况。

阻塞是好还是坏

实际上读文件因为没有数据而阻塞,其实是好事,因为这样子就进入休眠状态,休眠时就不会占用CPU,节省了cpu的资源。

我能不能将阻塞的读修改为非阻塞的读呢?

答:可以,非阻塞读的意思就是说,如果有数据就成功读到,如果没有读到数据就出错返回,而不是阻塞。

疑问:既然阻塞读很好,为什么提供非阻塞读呢?

答:尽管我们很少非阻塞的读,但是有些时候还真需要非阻塞的读,因此OS还是为我们提供了非阻塞操作方式。

如何实现非阻塞读

打开文件时指定O_NONBLOCK状态标志

以读鼠标为例

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<unistd.h>
 4 #include<errno.h>
 5 #include<string.h>
 6 #include <sys/types.h>
 7 #include <sys/stat.h>
 8 #include <fcntl.h>
 9
10
11 void print_err(char *str,int line,int err_no)
12 {
13     printf("%d,%s: %s\n",line,str,strerror(err_no));
14     exit(-1);
15 }
16 int main(void)
17 {
18     int cord=0;  //鼠标坐标
19     int ret=0;
20     int mousefd = -1;
21
22     mousefd=open("/dev/input/mouse0",O_RDONLY|O_NONBLOCK);
23     if(-1 == mousefd && errno!=EAGAIN) print_err("open mouse0 fail",__LINE_,errno);
24
25     while(1)
26     {
27         ret=read(mousefd,&cord,sizeof(cord));
28         if(ret > 0) printf("%d\n",cord);
29     }
30     return 0;
31 }

当制定O_NONBLOCK读鼠标的时候,没有数据的话,ret回返回-1,并且errno被设置为EAGAIN

在讲IPC有名管道时,如果不希望阻塞的话,就可以在open打开“有名管道”时,指定O_NONBLOCK,然后读有名管道无数据时就不会阻塞。

通过fcntl函数指定O_NONBLOCK来实现

什么情况下会使用fcntl来实现,

情况1:当文件已经被open打开了,但是open是并没有指定你要的文件状态标志,而你又不下你给去修改open的参数,此时就是可以使用fcntl来重设或者补设。

情况2:没办法在open指定,你手里只有一个文件描述符fd,此时就使用fcntl来重设或者补设。比如无名管道,无名管道连名字都没有,没办法使用open函数,无名管道是使用pipe函数来返回文件描述符的,如果过你想非阻塞的读无名管道的话,是没有办法通过open来指定O_NONBLOCK的,此时就需要使用fcntl来重设或者补设。

当然我们使用fcntl不仅仅只能重设或者补设O_NONBLOCK,也可以重设或者补设O_TRUNC/O_APPEND等任何你需要的“文件状态”标志。

例子:将0设置为O_NONBLOCK。设置有两种方式:

重设

fcntl(0, F_SETFL, O_RDONLY|O_NONBLOCK);

代码演示

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<unistd.h>
 4 #include<errno.h>
 5 #include<string.h>
 6 #include <sys/types.h>
 7 #include <sys/stat.h>
 8 #include <fcntl.h>
 9
10 void print_err(char *str,int line,int err_no)
11 {
12     printf("%d,%s: %s\n",line,str,strerror(err_no));
13     exit(-1);
14 }
15
16 int main(void)
17 {
18     char buf[100]={0};
19     int ret=0;
20     fcntl(0, F_SETFL, O_RDONLY|O_NONBLOCK);
21     while(1)
22     {
23         ret=read(0,buf,sizeof(buf));
24         if(ret > 0) printf("%s\n",buf);
25     }
26     return 0;
27 }

补设

1 flag = fcntl(0, F_GETFL);     //获取原有文件状态标志
2 flag = flag | O_NONBLOCK;     //通过|操作,在已有的标志上增设O_NONBLOCK
3 fcntl(0, F_SETFL, flag);         //将修改后的“文件状态标志”设置回去

代码演示

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<unistd.h>
 4 #include<errno.h>
 5 #include<string.h>
 6 #include <sys/types.h>
 7 #include <sys/stat.h>
 8 #include <fcntl.h>
 9
10 void print_err(char *str,int line,int err_no)
11 {
12     printf("%d,%s: %s\n",line,str,strerror(err_no));
13     exit(-1);
14 }
15
16 int main(void)
17 {
18     char buf[100]={0};
19     int ret=0;
20     int flag=0;
21     flag = fcntl(0, F_GETFL);
22     flag = flag | O_NONBLOCK;
23     fcntl(0, F_SETFL, flag);
24     while(1)
25     {
26         ret=read(0,buf,sizeof(buf));
27         if(ret > 0) printf("%s\n",buf);
28     }
29     return 0;
30 }

实现同时“读鼠标”和“读键盘”

由于一般情况下,“读鼠标”和“读键盘”都是阻塞的,为了不要让“读鼠标”和“读键盘”因为阻塞而相互干扰,可以采取如下办法来读。

①fork子进程,然后父子进程两线任务

父进程:读键盘

子进程:读鼠标

这样可以,但是并不主张这么做。多线任务使用进程来做开销太大

代码演示

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 #include<unistd.h>
 4 #include<errno.h>
 5 #include<string.h>
 6 #include <sys/types.h>
 7 #include <sys/stat.h>
 8 #include <fcntl.h>
 9
10 void print_err(char *str,int line,int err_no)
11 {
12     printf("%d,%s: %s\n",line,str,strerror(err_no));
13     exit(-1);
14 }
15
16 int main(void)
17 {
18     int cord=0;  //鼠标坐标
19     int mousefd = -1;
20     char buf[100]={0};//读键盘
21     int ret=0;
22
23     mousefd=open("/dev/input/mouse0",O_RDONLY);
24     if(-1 == mousefd) print_err("open mouse0 fail",__LINE__,errno);
25
26     fcntl(0, F_SETFL, O_RDONLY|O_NONBLOCK);
27     fcntl(mousefd, F_SETFL, O_RDONLY|O_NONBLOCK);
28
29     ret = fork();
30     if(ret > 0)//父进程
31     {
32         while(1)
33         {
34             ret = 0;
35             ret=read(0,buf,sizeof(buf));
36             if(ret > 0) printf("%s\n",buf);
37         }
38     }
39     else if(ret == 0)//子进程
40     {
41         while(1)
42         {
43             ret=read(mousefd,&cord,sizeof(cord));
44             if(ret > 0) printf("%d\n",cord);
45         }
46     }
47     return 0;
48 }

②创建次线程,主线程和次线程两线任务

主线程:读键盘

次线程:读鼠标

这才是我们经常实现的方式。代码演示:

③将鼠标和键盘设置为“非阻塞”,while轮询的读。

原文地址:https://www.cnblogs.com/kelamoyujuzhen/p/9451510.html

时间: 2024-08-30 14:26:24

高级IO——非阻塞IO的相关文章

IO模型--阻塞IO,非阻塞IO,IO多路复用,异步IO

IO模型介绍: * blocking IO 阻塞IO * nonblocking IO 非阻塞IO * IO multiplexing IO多路复用 * signal driven IO 信号驱动IO () * asynchronous IO 异步IO IO模型介绍: 为了更好地了解IO模型,我们需要事先回顾下:同步.异步.阻塞.非阻塞 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,

{python之IO多路复用} IO模型介绍 阻塞IO(blocking IO) 非阻塞IO(non-blocking IO) 多路复用IO(IO multiplexing) 异步IO(Asynchronous I/O) IO模型比较分析 selectors模块

阅读目录 一 IO模型介绍 二 阻塞IO(blocking IO) 三 非阻塞IO(non-blocking IO) 四 多路复用IO(IO multiplexing) 五 异步IO(Asynchronous I/O) 六 IO模型比较分析 七 selectors模块 一 IO模型介绍 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这个问题其实不同的人给出的答案都可能

阻塞IO,非阻塞IO,异步IO和非异步IO 的区别

最近在研究java IO.NIO.NIO2(或者称AIO)相关的东西,有些概念还是要明确下. 按照<Unix网络编程>的划分,IO模型可以分为:阻塞IO.非阻塞IO.IO复用.信号驱动IO和异步IO,按照POSIX标准来划分只分为两类:同步IO和异步IO. 如何区分呢?首先一个IO操作其实分成了两个步骤: 1.发起IO请求 2.实际的IO操作 阻塞和非阻塞IO:在于第一个步骤是否会会被阻塞,如果会则是阻塞IO,否则是非阻塞IO. 异步和非异步(同步)IO:在于第二个步骤是否会阻塞,如果实际的I

阻塞IO、非阻塞IO的区别

1.类与类之间的关系:依赖,实现,泛化(继承),关联,组合,聚合. 1)依赖(虚线):一个类是 另一个类的函数参数 或者 函数返回值. 2)实现(实线加小圆):对纯虚函数类(抽象类)的实现. 3)继承 即 泛化(实线加空心三角形,从子类指向父类):表示一个类与另一个类之间的继承关系: 4)关联(实线):一个类是另一个类的成员变量. 5)聚合(菱形加实线):整体和部分的关系.不太紧密的(如:汽车与发动机,汽车可以选择各种型号的发动机) 6)组合(实心菱形加实线):生命体整体与部分的关系.紧密的关系

python(十)下:事件驱动与 阻塞IO、非阻塞IO、IO多路复用、异步IO

上节的问题: 协程:遇到IO操作就切换. 但什么时候切回去呢?怎么确定IO操作完了? 一.事件驱动模型介绍 通常,我们写服务器处理模型的程序时,有以下几种模型: (1)每收到一个请求,创建一个新的进程,来处理该请求: (2)每收到一个请求,创建一个新的线程,来处理该请求: (3)每收到一个请求,放入一个事件列表,让主进程通过非阻塞I/O方式来处理请求 第三种就是协程.时间驱动的方式,一般普遍认为第(3)种方式是大多数网络服务器采用的方式 论事件驱动模型 在UI编程中,,常常要对鼠标点击进行相应,

Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO

Java网络编程和NIO详解5:Java 非阻塞 IO 和异步 IO Java 非阻塞 IO 和异步 IO 转自https://www.javadoop.com/post/nio-and-aio 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Java网络编程和NIO https://blog.csdn.net/column/details/21963.html 部分代码会放在我的的Github:https://github.com/h2p

网络编程之IO模型——非阻塞IO

网络编程之IO模型--非阻塞IO 非阻塞IO(non-blocking IO) Linux下,可以通过设置socket使其变为non-blocking.当对一个non-blocking socket执行读操作时,流程是这个样子: 从图中可以看出,当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error.从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果.用户进程判断结果是一个error时,它就

非阻塞IO服务器模型

我们来考虑一个情形,你跟千千万万个玩家是魔兽世界的超级粉丝,每周末准时组团打boss.每当周末游戏服务器就亚历山大,因为起码几十万用户同时在线.如果用我们的多线程阻塞服务器作为游戏服务器是否可行呢?先分析游戏服务器有哪些特点: ①  网络游戏并非像网页一样,打开一旦下载完就可以关闭连接结束.网游必须是有一个持久有状态的连接,每一个客户端都需要跟服务器存在一个持久的连接,以便快速及时发送消息.而随着并发用户数量的增加,多线程阻塞服务器不可能为每一个客户端分配一个线程. ②  跟一般的应用服务器不同

Nginx:异步非阻塞IO

在使用socket编程中,经常会看到阻塞.非阻塞.同步.异步,那么它们之间到底有什么关系跟区别呢? 本次将那Nginx的异步非阻塞的事件驱动模型来解释一下它们之间的关系. 阻塞IO 在linux中,默认所有socket都是阻塞的. 这意味着使用该socket调用诸如recv的函数时,在没有数据到达之前,该函数将不会返回,导致线程被阻塞,直到数据到达. 非阻塞IO 我们可以使用fcntl把socket设置为非阻塞的. 这意味着使用该socket调用诸如recv的函数时,该函数将立刻返回,可以根据返