目录
select模型的实现基于select函数,poll模型的实现也是基于 poll函数,poll可以看作是select的升级版,在功能层面是一样的,都是只负责等待事件就绪,不参与拷贝过程。
一、认识 poll 函数
poll函数的作用是等待某些事件是否就绪,函数声明如下:
1、第三个参数 timeout
这个参数是一个输入型参数,单位是毫秒,代表阻塞等待的时间,超过该时间就会变为非阻塞等待。
- timeout = -1,代表永久阻塞等待
- timeout = 0,代表永久非阻塞等待
- timeout > 0,代表先阻塞等待 timeout 毫秒,超过这个时间变为非阻塞等待,poll函数返回
2、第一、第二个参数
为了记录哪些文件描述符需要被关注,我们一般会将这些文件描述符放到一个数组里。
- 第一个参数:这个数组的元素首地址,元素类型是pollfd 结构体类型
- 第二个参数:需要关注的文件描述符的数目。如果有多个文件描述符,我们可以通过这个参数来遍历数组中的文件描述符。
(1) 认识 pollfd 结构体类型
pollfd结构体包含了每一个需要内核关注的文件描述符的信息,比如这个文件描述符需要关注什么事件、有什么事件已经就绪了等等。pollfd 的结构体声明如下:
- 第一个参数fd:你希望内核帮你关注哪个文件描述符
- 第二个参数events:你希望内核帮你关注该文件描述符上的什么事件(读/写事件),事件类型以及设置方式在下面介绍。
- 第三个参数revents:内核通知你该文件描述符上的某个事件就绪了。
(2) 添加关注事件 / 判断事件是否就绪
部分常用事件的类型如下:
事件 | 描述 | 是否可作为输入 | 是否可作为输出 |
POLLIN | 数据(普通数据或者优先数据) 可读 | 是 | 是 |
POLLINNORM | 普通数据可读 | 是 | 是 |
POLLOUT | 数据(普通数据或者优先数据) 可写 | 是 | 是 |
POLLWRNORM | 普通数据可写 | 是 | 是 |
从下面这个图可以看出这些事件都是宏,我们可以以按位或的方式来为文件描述符添加需要关注的事件。short是16位,每一位都可以表示一个事件,也就是说我们最多可以传入16种事件。
比如我们希望内核帮我们关注 0号文件描述符上的读事件
struct pollfd rfds;
rfds.fd = 0; //希望内核帮我们关注0号文件描述符上的事件
rfds.events |= POLLIN; //希望内核关注0号文件描述符上的读事件
//rfds.events = POLLIN | POLLOUT; //希望既监听读事件又监听写事件
rfds.revents = 0; //这个参数用于内核通知我们有事件就绪了,让我们赶紧来取
如果我们要判断读事件是否就绪
if(rfds.revents & POLLIN){
std::cout << "读事件就绪了..." << std::endl;
}
3、返回值
poll函数调用出错,返回-1;
非阻塞模式下,返回0;
有事件就绪时,返回 有事件就绪的文件描述符的个数
二、使用poll模型监听标准输入
下面就使用 poll 模型来监听 标准输入(0号文件描述符)
#include <iostream>
#include <unistd.h>
#include <poll.h>
using namespace std;
int main()
{
struct pollfd rfds;
rfds.fd = 0; //希望内核帮我们关注0号文件描述符上的事件
rfds.events |= POLLIN; //希望内核关注0号文件描述符上的读事件
rfds.revents = 0; //这个参数用于内核通知我们有事件就绪了,让我们赶紧来取
//int timeout = 0; //永久非阻塞等待
int timeout = -1; //永久阻塞等待
while (1)
{
int n = poll(&rfds, 1, timeout);
if (n < 0)
{
std::cerr << "poll调用出错" << std::endl;
}
else if (n == 0)
{
std::cout << "数据还没有准备好" << std::endl;
continue;
}
else
{
std::cout << "数据已经准备好了,赶紧来取吧" << std::endl;
//如果上层一直不取,即不调用recv/read函数,该文件描述符上的读事件会一直处在就绪状态
if (rfds.revents & POLLIN)
{
//说明该文件描述符上有读事件就绪了
char buffer[128];
ssize_t s = read(0, buffer, sizeof(buffer) - 1);
if (s > 0)
{
buffer[s] = 0;
std::cout << "读取到的内容为: " << buffer << std::endl;
}
}
}
}
return 0;
}
测试结果一(非阻塞模式下)
测试结果二(阻塞模式下)