为什么epoll使用非阻塞socket
时间: 2023-11-15 09:04:28 浏览: 217
非阻塞(non-blocking)socket 是一种 I/O 操作模式,它不会阻塞线程或进程的执行,而是立即返回操作的状态。而 epoll 是 Linux 下的一种高效的事件通知机制,用于管理大量的文件描述符(包括 socket),它可以监视多个文件描述符上的事件并统一管理。
在 epoll 中,当一个 socket 上没有数据可读时,传统的阻塞方式需要线程或进程一直等待直到有数据可读,这会导致资源浪费。而非阻塞方式下,当没有数据可读时,线程或进程可以继续执行其他任务,不需要一直等待。
epoll 使用非阻塞 socket 的原因是为了提高系统的并发处理能力。通过非阻塞 socket 和 epoll 的配合使用,可以实现高效的事件驱动编程模型,从而处理大量的并发连接。当有数据可读时,epoll 会通知应用程序进行读取操作,这样可以充分利用 CPU 资源,提高系统的性能。
相关问题
epoll阻塞和非阻塞模式
### epoll 阻塞与非阻塞模式的区别
#### 区别说明
在 Linux 系统中,`epoll` 提供了一种高效的 I/O 多路复用机制。其核心优势在于能够高效地管理大量并发连接。对于 `epoll` 来说,存在两种主要的操作方式:阻塞模式和非阻塞模式。
当处于 **阻塞模式** 下时,在调用如 `epoll_wait()` 函数等待事件发生的过程中,如果没有任何就绪的文件描述符,则当前线程会被挂起直到有新的事件到来[^1]。这种方式适用于服务器端应用中有较少数量高吞吐量需求的情况。
而在 **非阻塞模式** 中,即使没有准备好任何可读写的套接字或其他资源,函数也会立即返回并告知应用程序此时无可用数据或准备好的连接请求。这使得开发者可以在不浪费 CPU 资源的情况下快速响应其他任务,并通过轮询的方式持续检查是否有新事件产生[^4]。
#### 使用场景分析
- 对于大多数 Web 应用和服务而言,默认采用的是基于 `epoll` 的阻塞模式来简化逻辑实现,因为在这种情况下可以更方便地处理每一个客户端连接而无需额外考虑复杂的调度算法。
- 当面对极高并发数目的短链接服务(例如即时通讯工具),或是希望减少延迟时间提高实时性的场合下,则更适合选用非阻塞版本配合事件循环机制来进行优化设计[^2]。
```c
// 创建监听socket并设置为非阻塞
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
fcntl(listen_fd, F_SETFL, O_NONBLOCK);
struct sockaddr_in serv_addr;
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(portno);
bind(listen_fd, (struct sockaddr *) &serv_addr,sizeof(serv_addr));
listen(listen_fd,5);
// 初始化epoll实例
int epoll_fd = epoll_create(EPOLL_CLOEXEC);
// 添加监听socket到epoll监视列表里
struct epoll_event event;
event.events = EPOLLIN | EPOLLET; // 边缘触发模式
event.data.fd = listen_fd;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd,&event);
while(true){
struct epoll_event events[MAX_EVENTS];
int n_fds = epoll_wait(epoll_fd,events,MAX_EVENTS,-1);
for(int i=0;i<n_fds;++i){
if(events[i].data.fd==listen_fd){ /* handle new connection */ }
else{ /* read data from existing connections or write to them */}
}
}
```
epoll实现socket
epoll是一种I/O多路复用技术,主要用于在一个进程中管理多个socket的监听。它的作用是通过注册和监听多个socket,实现同时处理多个网络事件的能力。在常用的TCP/UDP程序中,如果只有一个socket,不需要使用epoll。但是如果有多个socket,就可以使用epoll来管理这些socket。
epoll主要由两个结构体组成:eventpoll和epitem。eventpoll是每一个epoll所对应的事件,而epitem是每一个IO所对应的事件。当调用epoll_ctl的EPOLL_CTL_ADD操作时,需要创建一个epitem来注册一个socket到epoll中。而通过调用epoll_wait方法可以获取已经监听到的事件。
下面是一个示例代码,展示了如何使用epoll来实现socket监听:
```
// 创建一个epoll对象
int epollFd = epoll_create(1);
// 创建一个socket
int listenSocket = socket(AF_INET, SOCK_STREAM, 0);
// 设置socket为非阻塞模式
fcntl(listenSocket, F_SETFL, O_NONBLOCK);
// 创建一个epoll_event结构体
struct epoll_event listenEvent;
listenEvent.data.fd = listenSocket;
listenEvent.events = EPOLLIN | EPOLLET; // 监听读事件,并设置为边沿触发模式
// 将socket注册到epoll中
epoll_ctl(epollFd, EPOLL_CTL_ADD, listenSocket, &listenEvent);
// 开始监听事件
struct epoll_event events[MAX_EVENTS];
while (true) {
int readyEventCount = epoll_wait(epollFd, events, MAX_EVENTS, -1);
if (readyEventCount == -1) {
// 发生错误,处理错误逻辑
break;
}
// 处理就绪的事件
for (int i = 0; i < readyEventCount; ++i) {
if (events[i].data.fd == listenSocket) {
// 监听socket有新的连接请求,处理连接逻辑
} else {
// 其他socket有数据可读,处理读取逻辑
}
}
}
// 关闭epoll和socket
close(epollFd);
close(listenSocket)
```
阅读全文