54 深入解析poll多路复用技术
摘要: 本文详细介绍了Linux系统中的poll多路复用机制。poll通过pollfd结构体数组管理文件描述符,支持监听多个文件描述符上的事件(如POLLIN可读和POLLOUT可写),相比select具有更高的灵活性和无数量限制的优势。文章解析了poll接口参数(fds、nfds、timeout)及返回值含义,并提供了基于poll的服务器实现代码示例,涵盖事件派发、连接管理(Accepter)和
🔥个人主页:Milestone-里程碑
❄️个人专栏: <<力扣hot100>> <<C++>><<Linux>>
🌟心向往之行必能至
一.多路复用 poll
1.1 poll接口
NAME
poll, ppoll - wait for some event on a file descriptor
SYNOPSIS
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <signal.h>
#include <poll.h>
int ppoll(struct pollfd *fds, nfds_t nfds,
const struct timespec *tmo_p, const sigset_t *sigmask);
参数说明
• fds是⼀个poll函数监听的结构列表. 每⼀个元素中, 包含了三部分内容: ⽂件描述符, 监听的事件集合, 返回的事件集合.• nfds表⽰fds数组的⻓度.• timeout表⽰poll函数的超时时间, 单位是毫秒(ms)
events的取值,对于我们来说,注重看POLLIN和POLLOUT即可,一个可读,一个可写

poll一次可以等待多个fd, fd&&events有效:用户告诉内核,你帮我关心,fd上面的events事件
poll成功返回时,fd&&events有效:用户告诉内核,你要我关心的fd上的events事件,已经就绪了
细节:
1. poll输入和输出参数分离了,所以不用再poll进行重置了,提升效率
2.poll等待的个数,没有上限,与文件描述符所处的数组是动态的对应
其中对应fd<0,不合法的,在内核中,不会关心这些fd的events
返回结果
• 返回值⼩于0, 表⽰出错;
• 返回值等于0, 表⽰poll函数等待超时;
• 返回值⼤于0, 表⽰poll由于监听的⽂件描述符就绪⽽返回
1.2 poll的使用
大致用法与select差不多, 且需要注意的细节也差不多
#pragma once
#include <iostream>
#include <memory>
#include <unistd.h>
#include <sys/poll.h>
#include "Socket.hpp"
#include "Log.hpp"
using namespace SocketModule;
using namespace LogModule;
class PollServer
{
const static int size = 4096;
const static int defaultfd = -1;
public:
PollServer(int port) : _listensock(std::make_unique<TcpSocket>()), _isrunning(false)
{
_listensock->BuildTcpSocketMethod(port);
for (int i = 0; i < size; i++)
{
_fds[i].fd = defaultfd;
_fds[i].events = 0;
_fds[i].revents = 0;
}
_fds[0].fd = _listensock->Fd();
_fds[0].events = POLLIN;
}
void Start()
{
_isrunning = true;
while (_isrunning)
{
int timeout = 1000; // 1000毫秒 1秒
int n = poll(_fds, size, -1);
// rfds: 0000 0000
switch (n)
{
case -1:
LOG(LogLevel::ERROR) << "poll error";
break;
case 0:
LOG(LogLevel::INFO) << "time out...";
break;
default:
// 有事件就绪,就不仅仅是新连接到来了吧?读事件就绪啊?
LOG(LogLevel::DEBUG) << "有事件就绪了..., n : " << n;
Dispatcher(); // 处理就绪的事件啊!
break;
}
}
_isrunning = false;
}
// 事件派发器
void Dispatcher()
{
// 就不仅仅是新连接到来了吧?读事件就绪啊? // 指定的文件描述符,在rfds里面,就证明该fd就绪了
for (int i = 0; i < size; i++)
{
if (_fds[i].fd == defaultfd)
continue;
// fd合法,不一定就绪
if (_fds[i].revents & POLLIN)
{
// fd_array[i] 上面一定是读就绪了
// listensockfd 新连接到来,也是读事件就绪啊
// sockfd 数据到来,读事件就绪啊
if (_fds[i].fd == _listensock->Fd())
{
// listensockfd 新连接到来
Accepter();
}
else
{
// 普通的读事件就绪
Recver(_fds[i].fd,i);
}
}
// if (FD_ISSET(fd_array[i], &wfds))
// {
// // fd_array[i] 上面一定是读就绪了
// }
}
}
// 链接管理器
void Accepter()
{
InetAddr client;
int sockfd = _listensock->Accept(&client); // accept会不会阻塞?
if (sockfd >= 0)
{
// 获取新链接到来成功, 然后呢??能不能直接
// read/recv(), sockfd是否读就绪,我们不清楚
// 只有谁最清楚,未来sockfd上是否有事件就绪?select!
// 将新的sockfd,托管给select!
// 如何托管? 将新的fd放入辅助数组!
LOG(LogLevel::INFO) << "get a new link, sockfd: "
<< sockfd << ", client is: " << client.StringAddr();
int pos = 0;
for (; pos < size; pos++)
{
if (_fds[pos].fd == defaultfd)
break;
}
if (pos == size)
{
LOG(LogLevel::WARNING) << "select server full";
close(sockfd);
}
else
{
_fds[pos].fd = sockfd;
_fds[pos].events=POLLIN;
}
}
}
// IO处理器
void Recver(int fd, int pos)
{
char buffer[1024];
// 我在这里读取的时候,会不会阻塞?
ssize_t n = recv(fd, buffer, sizeof(buffer) - 1, 0); // recv写的时候有bug吗?
if (n > 0)
{
buffer[n] = 0;
std::cout << "client say@ " << buffer << std::endl;
}
else if (n == 0)
{
LOG(LogLevel::INFO) << "clien quit...";
// 必须先关闭再修改,反了就成为关闭-1了
close(fd);
_fds[pos].fd = defaultfd;
}
else
{
LOG(LogLevel::ERROR) << "recv error";
// 必须先关闭再修改,反了就成为关闭-1了
close(fd);
_fds[pos].fd = defaultfd;
}
}
void PrintFd()
{
std::cout << "_fd_array[]: ";
for (int i = 0; i < size; i++)
{
if (_fds[i].fd == defaultfd)
continue;
std::cout << _fds[i].fd << " ";
}
std::cout << "\r\n";
}
void Stop()
{
_isrunning = false;
}
~PollServer()
{
}
private:
std::unique_ptr<Socket> _listensock;
bool _isrunning;
struct pollfd _fds[size];
};
1.3 poll的优点
1.4 poll的缺点
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)