飞凌嵌入式ElfBoard-文件I/O的深入学习之阻塞I/O与非阻塞I/O

概念

阻塞I/O顾名思义就是对文件的I/O操作是阻塞式的,即假如对某些类型文件(管道文件、网络设备文件和字符设备文件)进行读操作时,如果数据未准备好、文件当前无数据可读,那么读操作可能会使程序阻塞等待,直到有数据可读时才会被唤醒返回。

普通文件的读写操作是不会阻塞的,不管读写多少个字节数据,read或 write一定会在有限的时间内返回,所以普通文件一定是以非阻塞的方式进行I/O操作,这是普通文件本质上决定的。

非阻塞式I/O,即使没有数据可读,也不会被阻塞、而是会立马返回。

实践

从前面介绍的open函数可以看到,打开文件时可以使用O_NONBLOCK标志指定非阻塞打开,未指定则以阻塞模块进行操作。

以鼠标输入为例,在ubuntu的/dev/input/下有很多event节点。

|---------------------------------------------------------------------------------------------|
| $ ls /dev/input/ by-id by-path event0 event1 event2 event3 event4 mice mouse0 mouse1 mouse2 |

先通过sudo hexdump /dev/input/eventX (X为上述打印中0 1 2 3 4)去判断哪个是鼠标输入节点(命令执行后,鼠标移动时,会产生原始数据打印信息,没有打印信息就代表这个节点没有数据输入、不是鼠标的设备文件)。

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| $ sudo hexdump /dev/input/event2 0000000 c1f7 6731 0000 0000 a274 0001 0000 0000 0000010 0003 0000 8c9c 0000 c1f7 6731 0000 0000 0000020 a274 0001 0000 0000 0003 0001 9af3 0000 0000030 c1f7 6731 0000 0000 a274 0001 0000 0000 0000040 0000 0000 0000 0000 c1f7 6731 0000 0000 0000050 c3d0 0001 0000 0000 0003 0001 9bc8 0000 |

sudo去执行的原因是,当前使用的elf用户是一个普通用户,对系统文件没有读写权限。

1.阻塞式

以阻塞式打开鼠标输入节点文件,读取其中内容:

|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> int main() { char buf[100]; int ret; int fd = open("/dev/input/event2", O_RDONLY); //打开鼠标输入文件,默认是阻塞式 if (fd < 0) { printf("error: event2 open\n"); return -1; } ret = read(fd, buf, 100); //读取文件内容 if (ret < 0) { printf("error: event2 read\n"); close(fd); return -1; } printf("read event2 num:%d", ret); close(fd); return 0; } |

编译运行并查看测试结果

|--------------------|
| read event2 num:48 |

可以看到,例程运行时如果不动鼠标,就会一直等待,移动鼠标就会立刻返回,并且确实读到了48字节的数据。

2.非阻塞式

以非阻塞式打开鼠标输入节点文件,读取其中内容:

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> int main() { char buf[100]; int ret; int fd = open("/dev/input/event2", O_RDONLY | O_NONBLOCK); //以非阻塞式打开 if (fd < 0) { printf("error: event2 open\n"); return -1; } ret = read(fd, buf, 100); if (ret < 0) { perror("event2 read error"); close(fd); return -1; } printf("read event2 num:%d\n", ret); close(fd); return 0; } |

编译运行并查看测试结果

|-----------------------------------------------------|
| event2 read error: Resource temporarily unavailable |

可以看到,例程运行时如果不动鼠标,就会直接报错退出。可以修改成while循环读取数据防止直接报错退出。

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> int main() { char buf[100]; int ret; int fd = open("/dev/input/event2", O_RDONLY | O_NONBLOCK); if (fd < 0) { printf("error: event2 open\n"); return -1; } while (1) { //死循环一直非阻塞式读取 ret = read(fd, buf, 100); if (ret > 0) { //读取到数据后就结束死循环 printf("read event2 num:%d\n", ret); close(fd); return 1; } } close(fd); return 0; } |

编译运行并查看测试结果

|--------------------|
| read event2 num:48 |

阻塞的优缺点

当对文件进行读取操作时,如果文件当前无数据可读,那么阻塞式I/O会将应用程序挂起、进入休眠阻塞状态,直到有数据可读时才会解除阻塞;而对于非阻塞I/O,应用程序不会被挂起,而是会立即返回,要么一直轮询等待,直到数据可读,要么直接退出。所以阻塞式I/O的优点在于能够提升CPU的处理效率,当自身条件不满足时,进入阻塞状态,交出 CPU资源,将CPU资源让给别人使用;而非阻塞式则是抓紧利用CPU资源,比如不断地去轮询,但是这样会导致该程序有非常高的CPU使用率。

相关推荐
MegaDataFlowers2 小时前
英语六级我还在背单词:Unit 1(Lesson 2)
学习
东京老树根2 小时前
SAP学习笔记 - BTP SAP Build12 - SAP Build Content Package
笔记·学习
幸福巡礼5 小时前
【LangChain 1.2 实战(一)】 概述
笔记·学习·langchain
知识分享小能手5 小时前
R语言入门学习教程,从入门到精通,R语言数据计算与分组统计(9)
开发语言·学习·r语言
lizhihai_996 小时前
股市学习心得—半导体12种核心材料
大数据·人工智能·学习
sakiko_6 小时前
UIKit学习笔记3-布局、滚动视图、隐藏或显示视图
前端·笔记·学习·objective-c·swift·uikit
嵌入式-老费7 小时前
瑞芯微soc的学习和应用(题外话之esp32开发)
学习
辰同学ovo8 小时前
从全局登录状态管理学习 Redux
前端·javascript·学习·react.js
ting94520008 小时前
告别无效学习:Scholé 如何用 AI 重构职场学习,让学习直接嵌入工作流
人工智能·学习·重构
xian_wwq8 小时前
【学习笔记】Harness到底是什么
笔记·学习·ai·harness