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

相关推荐
Peter-OK3 小时前
PostgreSQL学习笔记(入门-进阶-精通说明)
笔记·学习·postgresql
承渊政道4 小时前
Linux网络学习【Reactor反应堆模式】
linux·网络·学习·ubuntu·reactor
橙橙笔记5 小时前
具身智能的学习 - 完成一个机械臂 第1节
python·学习·机械臂·具身智能
Hhy_11078 小时前
《C++深度解构06》手撕 string —— 从底层内存管理到深浅拷贝彻底吃透
c语言·开发语言·c++·学习·visual studio
山甫aa9 小时前
锁一篇搞定:乐观锁、悲观锁、CAS、数据库锁与分布式锁【从零开始的 Web 后端学习】
java·数据库·分布式·后端·学习·web
m0_5648768410 小时前
CMake学习
学习
只愿云淡风清10 小时前
正则表达式学习笔记 02:基础语法
笔记·学习·正则表达式
wangqiaowq13 小时前
Ontology 学习 二
学习
该学习了咕咕20 小时前
顶刊论文学习笔记
学习
Linux-lucky21 小时前
45-Linux学习之旅之防火墙与WEB加固
linux·前端·网络·学习·ubuntu