飞凌嵌入式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 buf100; 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 buf100; 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 buf100; 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使用率。

相关推荐
zyf1044161 小时前
暑期实践日志 Day48:复盘大纲与知识点对照表,系统汇总整理成果
学习·计算机网络·剪辑·暑期实践·课题任务
m4Rk_2 小时前
【论文阅读】Agent 记忆机制(61):CFGM——用粗到细的记忆落地贯通经验采集、知识蒸馏与在线纠错
论文阅读·人工智能·学习·开源·github
边境悍匪2 小时前
蜗牛学苑 Java 智能体学习 Day33|AOP 面向切面编程、日志技术思维导图复盘
学习
我是你的开心果7782 小时前
ai全栈软件开发day21(第四阶段喽)
人工智能·学习
网络工程小王3 小时前
【LLM开发实验】FastAPI 学习笔记
数据库·笔记·学习·mysql·fastapi
醉舞经阁半卷书13 小时前
量化交易基础之python库学习
学习
youm20033 小时前
【学习笔记】认识NoSQL——非关系型数据库
笔记·学习·nosql
传奇开心果编程4 小时前
【Rust入门知识点学与练】第11课:HashMap 键值对集合
开发语言·学习·rust
约克34 小时前
商水约克中央空调商用工程的行业洞察与落地要点
学习
王的宝库5 小时前
Linux 性能优化入门
linux·学习