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

相关推荐
sulikey1 小时前
个人Linux操作系统学习笔记11 - 环境变量
linux·笔记·学习
治愈系贝壳2 小时前
ARM --- day 6 时钟
linux·arm开发·学习
minglie12 小时前
zynq高频小数据量PS闭环的三个实验-第0课:目的和目标
学习
劈星斩月3 小时前
监督学习算法 之 决策树
学习·算法·决策树
AOwhisky3 小时前
Python 学习笔记(第五期)——组合数据类型:列表、元组、集合与字典精讲——核心知识点自测与详解
开发语言·笔记·python·学习·云计算
woshihuanglaoshi4 小时前
数据迁移与版本管理 - Flutter在鸿蒙平台实现数据库升级策略
数据库·学习·flutter·华为·harmonyos·鸿蒙·鸿蒙系统
世人万千丶13 小时前
参数管理_Flutter在鸿蒙平台路由参数最佳实践
学习·flutter·华为·harmonyos·鸿蒙
遇乐的果园16 小时前
前端学习笔记-vue加载渲染优化
前端·笔记·学习
遇乐的果园18 小时前
前端学习笔记-vue状态管理优化
前端·笔记·学习
从零开始的代码生活_18 小时前
C++ 继承详解:访问控制、对象模型、菱形继承与设计取舍
开发语言·c++·后端·学习·算法