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参数被忽略,可以直接写NULLEPOLL_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;其中关键数据是:fdepoll_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 }}
}