小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
1 IO模型
1.1 分类
在UNIX/Linux下主要有4种I/O 模型:
- 阻塞I/O:最常用、最简单、效率最低
- 非阻塞I/O:可防止进程阻塞在I/O操作上,需要轮询
- I/O 多路复用:允许同时对多个I/O进行控制
- 信号驱动I/O:一种异步通信模型
1.2 阻塞IO
几乎所有的阻塞函数默认都是阻塞IO,
以读阻塞为例,
如果要读取的缓冲区中有数据,则正常执行
如果缓冲区中没有数据,则读函数会一直阻塞等待,当有数据的时候,==内核将会自动唤醒当前进程==,接着执行读操作
以写阻塞为例,
一般写操作是不会阻塞,只有当写操作对应的缓冲区写满时,会发生阻塞
1 | c复制代码#include <stdio.h> |
1.3 非阻塞IO
如果将一个函数设置为非阻塞,意味着:
- 如果要操作的缓冲区中有数据,则正常执行
- 如果要操作的缓冲区中没有数据,则当前函数立即返回(当前函数执行失败),接着执行下面的代码
当一个应用程序使用了非阻塞模式套接字,它需要使用一个循环来不停地测试是否一个文件描述符有数据可读(称作polling),当应用程序不停地polling内核来检查是否I/O操作已经就绪,这是非常浪费CPU的资源的。
有一部分函数自带标志位,可以设置非阻塞,但是大多数函数都无法直接设置非阻塞,需要通过一些函数来设置
使用fcntl函数设置非阻塞IO
- WNOHANG
- MSG_DONTWAIT
- O_NONBLOCK
- 头文件:
#include <unistd.h>
#include <fcntl.h>
- 原型:
int fcntl(int fd, int cmd, ... /* arg */ );
- 功能:操作一个文件描述符
- 参数:
- fd:文件描述符
- cmd:命令选项
- F_GETFL 获取文件状态标志位
- F_SETFL 设置文件状态标志位
- O_NONBLOCK 非阻塞
- …arg:可变参,是否需要由cmd后面括号里面内容决定,如果是int就需要,如果是void就不需要
- 返回值:
- 成功:
F_GETFL 文件状态标志位
F_SETFL 0 - 失败:-1
- 成功:
读改写:一位一位的改
- 第一步:读,获取之前标志位
- 第二步:改,改变标志位
- 第三步:写,将改后的标志位设置回去
1 | c复制代码#include <stdio.h> |
2 IO多路复用
❓当一个代码中有多个阻塞函数时,因为代码默认都有先后执行顺序,所以无法做到每一个阻塞函数独立执行,相互没有影响,如何解决这个问题?
- 如果按照默认阻塞形式,无法解决;
- 如果设置为非阻塞,每一个函数都轮询查看缓冲区中是否有数据,可以解决这个问题,但是轮询比较消耗CPU资源,所以也不推荐;
- 如果使用多进程或者多线程,需要考虑资源释放问题,也不推荐。
与多线程、多进程相比,I/O多路复用系统开销小,系统不需要建立新的进程或者线程,也不必维护这些线程和进程。
- 处理多个描述符
- 服务器要处理多个服务或者多个协议
🀄相对比较好的方法是使用IO多路复用
IO多路复用的基本思想是:
- 先构造一张有关描述符的表,保存要操作的文件描述符;
- 然后调用一个函数,阻塞等待文件描述符准备就绪;
- 当有文件描述符准备就绪;
- 则函数立即返回;
- 执行相应的IO操作。
调用select或poll,在这两个系统调用中的某一个上阻塞,而不是阻塞于真正I/O系统调用。 阻塞于select调用,等待数据报套接口可读。当select返回套接口可读条件时,调用recevfrom将数据报拷贝到应用缓冲区中。
2.1使用select实现IO多路复用
1 | c复制代码#include <sys/select.h> |
2.1.2 服务器
1 | c复制代码//TCP网络编程之服务器 |
2.1.3客户端
1 | c复制代码//TCP网络编程之客户端 |
2.2poll
1.6.1 poll
1 | c复制代码#include <poll.h> |
本文转载自: 掘金