高并发服务器--epoll相关函数
一、创建epoll_create()
1.功能
是 Linux 系统中用于创建一个 epoll
实例的系统调用。epoll
是一种高效的 I/O 事件通知机制,通常用于高性能网络服务器或需要处理大量文件描述符的场景。
2.函数原型
int epoll_create(int size);
3.参数
①. size
: 指定 epoll
实例可以监控的最大文件描述符数量。这个参数在 Linux 内核 2.6.8 及更高版本中实际上被忽略,因为内核会动态调整资源分配。
4.返回值
①. 成功时返回一个非负的文件描述符,表示创建的 epoll 实例。
②. 失败时返回 -1,并设置 errno 为相应的错误码。
二、上树、下树、修改epoll_ctl()
1.功能
用于向 epoll
实例中添加、修改或删除文件描述符的事件监听。
2.函数原型
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);
3.参数
①. epfd: 通过 epoll_create 或 epoll_create1 创建的 epoll 实例的文件描述符。
②. op: 操作类型,可以是以下之一:
EPOLL_CTL_ADD: 添加一个新的文件描述符到 epoll 实例中。
EPOLL_CTL_DEL: 从 epoll 实例中删除一个文件描述符。
EPOLL_CTL_MOD: 修改一个已存在的文件描述符的事件掩码。
③. fd: 需要操作的文件描述符。
④. event: 指向 epoll_event 结构体的指针,用于指定监听的事件类型和关联数据。
struct epoll_event {
uint32_t events; // 事件掩码
epoll_data_t data; // 用户数据
};
events: 指定需要监听的事件类型,例如 EPOLLIN(可读)、EPOLLOUT(可写)等。
data: 用户数据,可以是文件描述符、指针或其他自定义数据。
4.返回值
①. 成功时返回 0。
②. 失败时返回 -1,并设置 errno 为相应的错误码。
三、监听epoll_wait()
1.功能
是 Linux 系统中用于等待 epoll
实例中事件发生的系统调用。它是 epoll
机制的核心部分,用于阻塞等待直到有事件发生或超时。
2.函数原型
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
3.参数
①. epfd: 通过 epoll_create 或 epoll_create1 创建的 epoll 实例的文件描述符。
②. events: 用于存储返回的事件的数组指针。每个事件由一个 epoll_event 结构体表示。
③. maxevents: 指定 events 数组的最大容量。必须大于 0。
④. timeout: 指定等待事件发生的超时时间(以毫秒为单位)。可以是以下值:
正数:等待指定的毫秒数。
0:立即返回,不阻塞。
-1:无限期阻塞,直到有事件发生。
4.返回值
①. 成功时返回触发事件的数量(可能小于 maxevents)。
②. 失败时返回 -1,并设置 errno 为相应的错误码。
③. 如果超时返回 0。
四、epoll的两种触发模式
1.水平触发(Level-Triggered,LT)
水平触发是epoll的默认模式,它在文件描述符处于就绪状态时持续通知应用程序,直到事件被处理完毕。
①. 触发条件:只要文件描述符处于就绪状态(例如可读或可写),epoll_wait就会持续返回该事件。
②. 事件处理:应用程序需要持续轮询事件,直到事件不再就绪。
③. 适用场景:适用于简单的场景,特别是当事件处理较快时。
2.边缘触发(Edge-Triggered,ET)
边缘触发只在文件描述符的状态发生变化时通知应用程序一次。
①. 触发条件:只有当文件描述符的状态从非就绪变为就绪时,epoll_wait才会返回该事件。
②. 事件处理:应用程序需要一次性处理所有就绪的事件,因为后续即使事件仍然就绪,也不会再次通知。
③. 适用场景:适用于高并发场景,因为它减少了事件通知的次数,从而提高了性能。
五、程序示例
1.管道中运用epoll
#include
#include
#include
#include
int main()
{
int fd[2];
pid_t pid;
pipe(fd);
pid = fork();
if(pid < 0)
perror("fork");
else if(pid == 0){
close(fd[0]);
char buf[5];
char ch = 'a';
while(1){
sleep(3);
memset(buf,ch++,sizeof(buf));
write(fd[1], buf, 5);
}
}
else{
close(fd[1]);
int epfd = epoll_create(1);
struct epoll_event ev, evs[1];
ev.data.fd = fd[0];
ev.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, fd[0], &ev);
while(1){
int n = epoll_wait(epfd, evs, 1, -1);
if(n == 1){
char buf[128] = "";
int ret = read(fd[0], buf, sizeof(buf));
if(ret <= 0){
close(fd[0]);
epoll_ctl(epfd, EPOLL_CTL_DEL,fd[0], &ev);
break;
}
else{
printf("%s
", buf);
}
}
}
}
return 0;
}
2.epoll高并发服务器
①.创建套接字。Ifd = tcp4bind=>socket, bind
②.套接字监听。Listen
③.创建树。epfd = epoll_create
④.将Ifd上树。初始化ev、epoll_ctl
⑤.监听while{
⑥.监听epfd。nready = epoll_wait
⑦.如果返回值nready<0,break跳出循环。
⑧.如果返回值nready =0,contine继出续循环,
⑨. 如果返回值nready>0,说明epfd监听到变化
⑩ . 如果是cfd变化,连接cfd。Accept⑪.cfd上树。epoll_ct
⑫.如果是cfd变化
⑬.检查数据while(1){
⑭.读客户端的数据n = read()
⑮.如果n == 0,则关闭cfd,并且下树
⑯.如果n>0,使用write将日志打印到终端页面,并write写回客户端。
}
}
#include
#include
#include
#include
#include
#include "wrap.h"
#include
int main()
{
//创建套接字
int lfd = tcp4bind(8000, NULL);
//套接字监听
Listen(lfd, 128);
//创建树
int epfd = epoll_create(1);
//将lfd上树
struct epoll_event ev, evs[1024];
ev.data.fd = lfd;
ev.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
//监听
while(1){
int nready = epoll_wait(epfd, evs, 1024, -1);
printf("epoll_wait-------
");
if(nready < 0){
perror("");
break;
}
else if(nready == 0){
continue;
}
else{
int i;
for(i = 0; i
六、epoll机制
参考:
Linux epoll完全图解,彻底搞懂epoll机制 - 知乎
本文地址:https://www.vps345.com/1731.html