文章目录
poll
- 和select一样,poll也是一种就绪事件通知方案。poll也只有一个作用就是等。
- poll解决两个select的问题:poll等待的文件描述符没有上限。
- poll将输入和输出利用变量做了分离。所以每次循环都不需要再次进行重新添加。
poll函数介绍
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
第一个参数fds,是一个结构体数组,因为数组传参会退化成指针。
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
- fd代表你想要监控的文件描述符。
- events是用户告诉内核,代表你想要操作系统帮你监控哪些事件。
- revents是内核告诉用户,代表操作系统告诉用户,该文件描述符的哪些事件就绪。
- pollfd将输入和输出参数做了分离,每次添加新的fd,旧的数组不需要改变。减少了轮询的过程。
第二个参数:
nfds代表fd的数量。
第三个参数:
- 代表你想要poll阻塞等待一次的时间,单位是毫秒。超过时间后,会超时返回,跟select一样。
- timeout设置为0,表示poll非阻塞等待。
- timeout设置为-1,表示阻塞等待。
返回值:
- 返回值和select的完全一样。
事件的设置
- events和revents中,有很多事件。
- 这里介绍两个,POLLIN和POLLOUT。分别代表可读就绪和可写就绪。
- 这里使用的方法和open函数的类似,因为events是short类型,所以short的每一个比特位代表一种事件。我们使用按位或来添加事件,这样可以同时监测一个fd中的不同事件。
- 我们使用按位与来检测 revents。
使用poll
监测标准输入:
1 #include <iostream>
2 #include <poll.h>
3 #include <cstdlib>
4 #include <unistd.h>
5
6 using namespace std;
7
8 int main(){
9 //int poll(struct pollfd *fds, nfds_t nfds, int timeout);
10 struct pollfd fds[1];
11 fds[0].fd = 0;
12 fds[0].events = POLLIN;
13 fds[0].revents = 0; //revents 可以不设置,由内核设置。
14
15 char buf[1024] = {0};
16 for(;;){
17 cout << "poll begin..." << endl;
18 int ret = poll(fds, 1, 1000); //timeout的单位是毫秒!
19 if(ret < 0){
20 cerr << "poll error" << endl;
21 exit(1);
22 }
23 else if(ret == 0){
24 cout << "timeout..." << endl;
25 continue;
26 }
27 else{
28 ssize_t ss = read(0, buf, sizeof(buf) - 1);
29 buf[ss] = 0;
30 cout << "echo # "<< buf << endl;
31 }
32 }
- 如果将监控POLLIN改成监控POLLOUT,那么就会一直打印poll begin,因为输出缓冲区一直空着,一直有空间。读取看的是有没有数据,写入看的是有没有空间。
poll的伪代码:
struct polllfd fds[MAX]; //MAX的值由你来设定;
//这里将fds[i].fd都初始化成一个负数,用来标识未被使用!
fds[0].fd = lsock;
fds[0].events = POLLIN;
for(;;){
poll(fds, MAX, 1000);
for(int i = 0; i < MAX; ++i){
if(fds[i].fd == FLAG) //FLAG是你设置的负数,表示该位置未被使用
continue;
else if(fds[i].events & POLLIN) //这里还可以检测多种事件,
{
if(fds[i].fd == lsock) //连接
{
sock add to fds; //是连接就加入到fds中,然后设置想要检测的事件。
sock add events
}
else
{/** 处理数据 **/}
}
else if(fds[i].events & POLLOUT) //处理写事件,和上面一样的逻辑!
{/* 略 */}
}
}
poll优缺点
- 比起select,poll不用做更多的轮询检测。
- poll检测的文件描述符没有上限。
缺点:
- 仍然避免不了用户到内核,内核到用户的数据拷贝。
- 虽然用户没有进行过多的轮询,但是在内核中,仍然要使用轮询检测,这样文件描述符多了之后,效率仍会下降。
多路转接
不要神话多路转接:
- 多路转接不是任何情况都适用的,要看场景。
- 多路转接适用于长连接,长距离通信。这类场景等待的时间比重较大。
- 即多路转接是为了减少等的比重,如果等待的时间本来就少,那么多路转接就没有太大意义。甚至还不如其他的IO模型。
- 因为此时多路转接的轮询或者拷贝就会相当耗费资源,远远超过等的资源。
多路转接还要考虑上层协议:
- 多路转接只保证数据高于水位线就给你,但是不保证数据的完整。
- 比如你想要1024个字节的http报文,但是多路转接接收到100个字节就通知你。那么你需要先将100个字节数据存起来,然后再将该文件描述符重新设置进数组,继续等待。
- 等待完毕,将1024个字节进行解析,然后生成一个response,以写事件的方式塞入多路转接,然后发出去。