1.多路复用:监听多个文件描述符是否就绪(read/write/error)
阻塞IO
(1)读
没有数据可读 read(类似的)函数就会阻塞(wait)
直到有数据或出错为止
(2)写
没有空间去写 write(类似的)函数就会阻塞(wait)
直到出现错误或者有空间可以写
内核默认的一种IO方式 也是最简单的方式
多路复用 (种类)
(1)select
(2)poll
-->epoll
2.select
NAME
select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchronous I/O multi‐
plexingSYNOPSIS
/* According to POSIX.1-2001, POSIX.1-2008 */
#include <sys/select.h>/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
nfds :感兴趣的最大文件描述符+1
readfds :需要监听读的文件描述符集合
writefds :需要监听写的文件描述符集合
exceptfds :需要监听出错的文件描述符集合
三个参数都可以为NULL 等效于一个延时函数
调用前,调用者填入的是 感兴趣的文件的文件描述符
调用后 保存的是已经就绪的文件描述符
timeout:超时设置
struct timeval {
long tv_sec; 秒
long tv_usec; 微秒
};
在调用前填的 超时时间
调用后保存的是 剩余时间
返回值:
>0 表示就绪的文件描述符的个数
=0 表示超时了
<0 出错了
void FD_CLR(int fd, fd_set *set);
将文件描述符fd从fd_set这个集合移除
int FD_ISSET(int fd, fd_set *set);
判断这个文件描述符fd是否在集合fd_set中
void FD_SET(int fd, fd_set *fd_set);
将文件描述符fd加入到set这个集合中去
void FD_ZERO(fd_set *fd_set);
将一个集合set全部清除
select的实现 是在内核中开辟一个 内核线程 去实现的
同时监听。轮询
while(没有超时)
{
先问第一个文件描述符是否准备就绪 [0,nfds)
..... 根据循环一致往后问
if(有文件就绪了)
{
记录那个文件就绪了
break;
}
sleep(1);
}
3.poll
NAME
poll, ppoll - wait for some event on a file descriptorSYNOPSIS
#include <poll.h>
poll功能和select是类似的 "监听"多个文件描述符 是否就绪
只不过 poll用一个结构体struct pollfd来描述 “监听请求”
struct pollfd {
int fd; /* file descriptor */
short events; /* 监听的事件 */ 位域
POLLIN :可读的事件
POLLOUT :可写的事件
POLLERR :出错的事件
如果你要监听 可读可写怎么办?
POLLIN | POLLOUT
short revents; /* returned events */ 位域
保存的是已经就绪的事件
怎么判断是否可读
if(revents & POLLIN)
{
可读了
}
};
一个结构体只能表示一个文件 多个文件怎么办?
结构体数组
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
fds :需要监听的struct pollfd 结构体数组
nfds :数组中有几个元素 要监听几个文件描述符
timeout :超时时间 单位毫秒
返回值:
>0 表示就绪的文件描述符的个数
=0 表示超时了
<0 出错了
4.epoll
(1)epoll_create:创建一个监听文件的集合,这个函数的返回值也是一个文件描述符
NAME
epoll_create, epoll_create1 - open an epoll file descriptorSYNOPSIS
#include <sys/epoll.h>int epoll_create(int size);
size :已经被忽略了 但是size还是要大于0
返回值
成功 返回一个epoll对象(实例),就是一个文件描述符
失败 返回-1 同时errno被设置
对象有了,但是怎么把需要监听的文件加入到这个对象中去呢?
(2)epoll_ctl
NAME
epoll_ctl - control interface for an epoll file descriptorSYNOPSIS
#include <sys/epoll.h>int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epfd :epoll的对象 也就是 epoll_create的返回值
op :具体的操作
EPOLL_CTL_ADD :
把一个需要监听的文件描述符和需要监听的事件添加到epoll对象中去
EPOLL_CTL_MOD :
修改一个已经在epoll对象中文件描述符的监听事件
EPOLL_CTL_DEL :
从一个epoll对象中删除一个文件描述符
fd :要操作的文件描述符
event :要监听事件的结构体指针
struct epoll_event
{
uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
先看events
要监听的事件标志 位域
EPOLLIN :可读的事件
EPOLLOUT :可写事件
EPOLLERR :出错事件
EPOLLRDHUP : 监听流式套接字的对方
是否已经关闭写或者关闭读写
EPOLLET :边缘触发 Edge-Triggered
LT Level-Triggered 等级触发
只要有数据就会不停的上报可读
ET Edge-Triggered 边缘触发
需要数据量发生变化才会上报可读
data : 用来保存用户的一些数据的
typedef union epoll_data
{
void *ptr;//保存用户数据指针
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
返回值:
成功返回0 失败返回-1同时errno被设置
(3)epoll_wait:用来等待监听事件的发生
NAME
epoll_wait, epoll_pwait - wait for an I/O event on an epoll file descriptorSYNOPSIS
#include <sys/epoll.h>
监听epoll对象中的文件描述符的事件
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
epfd :epoll的对象
events :结构体数据 用来保存已经就绪的事件信息 这是数组
maxevents :第二个结构体数组最多可以保存多少个元素
timeout :超时时间 单位ms
返回值:
>0 表示就绪的文件描述符的个数
=0 表示超时了
<0 出错了
优点
1.效率比poll要高
在文件描述符比较多的时间 poll需要去看每一个文件描述对应的事件是否就绪
epoll直接只返回那些文件描述符需要监听的事件就绪
2.边缘触发的功能