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

相关推荐
大志若愚YYZ1 小时前
嵌入式Linux学习——Framebuffer 应用编程详解(理论知识)
学习
AI360labs_atyun1 小时前
学习教学AI指南,附4个提示词指令(Prompts)
人工智能·科技·学习·ai·chatgpt
FAREWELL000752 小时前
Lua学习记录(4) --- Lua中多文件调用 以及 关于位运算符的零碎知识点
开发语言·学习·lua
woodykissme2 小时前
齿轮如何撬动工业文明的进程
学习·机械·齿轮·传动
码界奇点2 小时前
Java大数据在智能教育个性化学习资源推荐中的冷启动解决方案
java·大数据·学习·动画·推荐算法
DJ斯特拉2 小时前
Tlias智能学习辅助系统(一)
学习
列星随旋3 小时前
redis分片集群的部署和使用
redis·学习
Chennnng3 小时前
rsl_rl框架学习
学习
xixixi777774 小时前
剖析Agent(代理)攻击面
网络·学习·安全·架构·网络攻击模型·代理