1. IO多路复用
- 核心目的 — 提高并发的程度
1.1 多路IO
- — I(input)O(output) 当前进程 有多处 输入和输出的操作
- — 点对点通信
1.2服务器
if (pid > 0)
{
//父进程从键盘获得数据 —写到socket中
}else if (pid == 0)
{
//子进程从socket中读取数据 —输出到终端
}
1.3 复用
复用的是当前进程或线程,在一个进程或线程中 有多路IO要同时处理
- 阻塞IO
- 非阻塞IO
- 信号驱动IO
- IO多路复用
2. 阻塞IO:
- scanf
- getchar
- fgets
3. 非阻塞IO:
3.1 特点:
- 当去内核读取数据时,如果内核没有数据,则不阻塞等待,而是立即返回,所以,非阻塞方式如果想要获得数据,必须配合轮询操作。
while (1)
{
recvfrom(); //非阻塞,必须轮询
轮询到 读到数据
} //很耗CPU
3.2 信号驱动IO:
设置好对SIGIO的信号处理函数,等有数据来了,内核会给进程发信号,进程收到信号后,再去做读取操作,不需要一直等,不需要轮询
3.3 使用流程:
1. 为内核发送的通知信号安装一个信号处理例程。默认情况下,这个通知信号为 SIGIO。
//signal 或 sigaction
signal(SIGIO,handler);
2.设定文件描述符的属主,
也就是当文件描述符上可执行 I/O 时会接收到通知信号的进程或进程组。通常我们让调用进程成为属主。设定属主可通过 fcntl()的 F_SETOWN 操作来完成:
//owner
fcntl(fd,F_SETOWN,pid); //设置文件描述符fd 属于 进程 pid
3.设置标志
通过设定 O_NONBLOCK 标志使能非阻塞 I/O。通过打开 O_ASYNC 标志使能信号驱动 I/O。 //使能 (使能够)这可以一个操作,因为它们都需要用到 fcntl()的 F_SETFL 操作
flags = fcntl(fd,F_GETFL); //获得当前标志
fcntl(fd,F_SETFL,flags|O_ASYNC|O_NONBLOCK);
3.4 特点:
1.异步的
2.效率高
3.问题 –只能处理一路IO
4. IO多路复用:
1.用函数 (select/epoll (poll))监控多路IO
2.如果那一路IO有数据,则对应的函数返回,告诉用户进程有就绪的IO
3.用户进程去对应的IO读取数据
4.1 select

int select( int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);
功能:
- 监控多路IO,看是否有就绪的
参数:
- @nfds //要监控的文件描述符 最大值+1
- @readfds //是否可读的集合
- @writefds //是否可写的集合
- @exceptfds //是否异常的集合
- @timeout
0 //时间为0 表示select是一个非阻塞
>0 //时间> 0 表示select阻塞对应的一段时间
NULL //NULL 表示默认使用 阻塞方式
返回值: 表示 就绪的文件描述符的数量
- 失败 -1&&错误码会被设置
void FD_CLR(int fd, fd_set *set); //从set中清除fd
int FD_ISSET(int fd, fd_set *set); //判断fd是否在set中
void FD_SET(int fd, fd_set *set); //将fd添加到set中
void FD_ZERO(fd_set *set); //将set清0
select使用流程:
1.准备监控文件描述符 表
fd_set readfds;
FD_ZERO(&readfds);
2.添加要监控的文件描述符
FD_SET(fd,&readfds);
FD_SET(0,&readfds);
3.准备 nfsd
nfds = fd + 1;
4.调用select函数
select(nfds,&readfds,NULL,NULL,NULL);
注意:
1. select函数调用完之后,会将监控结果的文件描述符集合带回给readfds
select监控到就绪的文件描述符后,会将未就绪的清零
2. select 监控 超时时,
select 函数 每次 会将 timeout中的时间往下减,减的结果放到timeout中
使用时timeout中值,每次调用前,需要重新给值。
4.2 epoll

1.创建监控的epoll对象 epoll_create
① int epoll_create(int size);
功能:
- 创建epoll对象,返回文件描述符
参数:
- @size 并不是限制了epoll所能监听的描述符最大个数,只是对内核初始分配内部数据结构的一个建议。
返回值:
- 成功 是epoll描述符。
- 失败 -1表示创建失败。
2.epoll_ctl 函数
② int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:
- epoll对象的控制函数
参数:
- @epfd —epoll对象
- @op
epoll_ctl控制对指定描述符fd执行op操作,event是与fd关联的监听事件。
op操作有三种:
添加EPOLL_CTL_ADD,
删除EPOLL_CTL_DEL,
修改EPOLL_CTL_MOD。
分别添加、删除和修改对fd的监听事件。 - @fd 就是要操作的fd
- @event —事件的结构体
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events —epoll事件 */
epoll_data_t data; /* User data variable — 用户的数据 */
};
event 这个参数中 —要存放 关心的事件 及 对应fd
EPOLLIN 可读
EPOLLOUT 可写
EPOLLERR 异常条件
EPOLLET 边沿触发模式,
在描述符状态跳变时才上报监听事件。(监听默认都是LT模式)
返回值:
- 返回0表示控制成功,返回-1表示失败。
③ int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
功能:
- 监控文件描述符是否就绪
参数:
- @epfd — 表示 监控的epoll对象
- @events — 表示 获得就绪事件的一个结构体指针 (理解为一个struct epoll_event 类型的数组名)
- @maxevents — 表示 该数组的最大长度
- @timeout — 表示超时的时间,单位 毫秒
timeout — 0 //非阻塞
timeout — -1 //阻塞
timeout — 非负数 //表示阻塞 多少 微秒
返回值:
- 成功 返回就绪的文件描述符数量
设置了超时,超时时间到了,没有就绪的,则返回0 - 失败 -1 && errno


