2.4 epoll模型
epoll模型只有linux系统才有
epoll模型只有从linxu内核2.4版本之后才有
epoll从2.4内核到目前的4.X内核,没有更新的模型了,说明epoll模型本身已经很完美了
select的问题:
监视列表无法扩容
监视列表和返回的激活列表混在了一起
效率低下:
select需要自己管理激活的套接字
select查询哪个套接字激活了是一个双重循环,效率低下
select的内核部分,监视的套接字也是一个数组,查询哪个套接字激活了,效率也是低的
poll的问题:
效率低下:
select的内核部分,监视的套接字也是一个数组,查询哪个套接字激活了,效率也是低的
epoll彻底结局了select 和 poll的问题
epoll允许自动扩容
epoll的内核部分是以二叉树存储所有的描述符,所在查看哪个描述符激活的时候,效率很高
epoll会把所有激活的描述符,放在一个数组中,直接提供给用户,编程效率高
原型:int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
函数调用:int count = epoll_wait(fd,数组,监视的描述符的数量,-1)
功能描述:阻塞并等待监视列表中的描述符激活,并且将所有激活的描述符,放在一个数组中提供给我们
参数解析:
参数 epfd:监视列表
epoll要求先创建一个文件,然后将所有的要监视的描述符,写入这个文件中,并监视
参数 events:用来存放所有激活的描述符的数组
参数 maxevents:最大的监视描述符的数量
参数 timeout:阻塞时长,单位为毫秒,
0:表示不阻塞
-1:表示常阻塞
返回值:激活的描述符的数量
epoll如何创建监视列表
原型:int epoll_create(int size);
调用:int epfd = epoll_create(想要监视的描述符数量)
功能描述:创建一个文件,该文件最多能够监视 size个字节的描述符
参数解析:
参数 size:监视的描述符的最大值
返回值:返回创建出来的文件的描述符
原型:int epoll_create1(int flags);
调用:int epfd = epoll_create1(EPOLL_CLOEXEC
)
功能描述:创建一个文件,用来监视描述符,该文件能够监视的描述符数量能够自动扩容
epoll如何将被监视的描述符写入文件中
epoll如何将被监视的描述符写入文件中
原型:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
调用:epoll_ctl();
功能描述:操作监视列表,可以删除,可以添加,可以指定监视类型(监视可读,监视可写)
参数解析:
参数 epfd:监视列表
参数 op:具体针对监视列表的操作行为
EPOLL_CTL_ADD:将参数 fd 描述符,添加进入epfd监视列表,并且由参数event决定,以何种形式进行监视
EPOLL_CTL_DEL:将参数 fd 描述符,从epfd监视列表中移除,此时event参数被忽略,可以直接写NULL
EPOLL_CTL_MOD:根据参数 event 更改 fd描述符的监视类型
参数 fd:等待操作的描述符
参数 event:一个结构体指针,结构如下
struct epoll_event {
uint32_t events; /* Epoll events */ 监视的事件类型
EPOLLIN:监视描述符是否可读
EPOLLOUT:监视描述符是否可写
epoll_data_t data; /* User data variable */
};
data 也是一个结构体,结构如下
typedef union epoll_data {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
其中关键数据是:fd
epoll_event 这个结构体,在 epoll_wait的时候,传入的是这个结构体的数组
我们判断哪个描述符激活,就是依靠这个fd来判断的
epoll_ctl函数调用的时候,这个events.data.fd 必须和 参数 fd保持一致
#include <myhead.h>
int main(int argc, const char *argv[])
{
if (argc != 2)
{
printf("请输入正确的端口号\n");
return 1;
}
int port = atoi(argv[1]);
int epfd = epoll_create1(EPOLL_CLOEXEC);
if (epfd == -1)
{
perror("epoll_create1");
return 1;
}
int server = socket(AF_INET, SOCK_STREAM, 0);
if (server == -1)
{
perror("socket");
return 1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (bind(server, (struct sockaddr *)&addr, sizeof(addr)) == -1)
{
perror("bind");
return 1;
}
if (listen(server, 100) == -1)
{
perror("listen");
return 1;
}
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = server;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, server, &event) == -1)
{
perror("epoll_ctl");
return 1;
}
int fd_count = 100;
struct epoll_event readfds[fd_count];
while (1)
{
int signal_count = epoll_wait(epfd, readfds, fd_count, -1);
for (int i = 0; i < signal_count; i++)
{
int fd = readfds[i].data.fd;
if (fd == server)
{
int client = accept(server, NULL, NULL);
event.events = EPOLLIN;
event.data.fd = client;
if (epoll_ctl(epfd, EPOLL_CTL_ADD, client, &event) == -1)
{
perror("epoll_ctl");
close(client);
}
else
{
printf("有新客户端连接\n");
}
}
else
{
char buf[128] = {0};
int res = read(fd, buf, 128);
if (res == 0)
{
printf("有客户端断开连接\n");
epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
close(fd);
}
else
{
printf("接收到客户端的消息:%s\n", buf);
}
}
}
}
close(server);
return 0;
}
epoll的代码模型
int epfd = epoll_create1(EPOLL_CLOEXEC)
struct epoll_event event
event.events = EPOLLIN(监视类型)
event.data.fd = 想要监视的描述符
epoll_ctl(epfd,EPOLL_CTL_ADD,想要监视的描述符,&event)
struct epoll_event readfds;
while(1){
int signal_count = epoll_wait(epfd,readfds,监视描述符的数量,-1)
for(遍历readfds){
判断哪个描述符激活了{
执行对应的逻辑cv
}
}
}