目录
select模型的实现基于select函数,poll模型的实现也是基于 poll函数,poll可以看作是select的升级版,在功能层面是一样的,都是只负责等待事件就绪,不参与拷贝过程。
poll函数的作用是等待某些事件是否就绪,函数声明如下:

这个参数是一个输入型参数,单位是毫秒,代表阻塞等待的时间,超过该时间就会变为非阻塞等待。
为了记录哪些文件描述符需要被关注,我们一般会将这些文件描述符放到一个数组里。
pollfd结构体包含了每一个需要内核关注的文件描述符的信息,比如这个文件描述符需要关注什么事件、有什么事件已经就绪了等等。pollfd 的结构体声明如下:

部分常用事件的类型如下:
| 事件 | 描述 | 是否可作为输入 | 是否可作为输出 |
| 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;
- }
poll函数调用出错,返回-1;
非阻塞模式下,返回0;
有事件就绪时,返回 有事件就绪的文件描述符的个数
下面就使用 poll 模型来监听 标准输入(0号文件描述符)
- #include
- #include
- #include
- 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;
- }
测试结果一(非阻塞模式下)

测试结果二(阻塞模式下)
