• IO多路转接 —— 认识 poll 函数


    目录

    一、认识 poll 函数

    1、第三个参数 timeout

    2、第一、第二个参数

    (1) 认识 pollfd 结构体类型

    (2) 添加关注事件 / 判断事件是否就绪

    3、返回值

    二、使用poll模型监听标准输入


    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号文件描述符上的读事件

    1. struct pollfd rfds;
    2. rfds.fd = 0; //希望内核帮我们关注0号文件描述符上的事件
    3. rfds.events |= POLLIN; //希望内核关注0号文件描述符上的读事件
    4. //rfds.events = POLLIN | POLLOUT; //希望既监听读事件又监听写事件
    5. rfds.revents = 0; //这个参数用于内核通知我们有事件就绪了,让我们赶紧来取

    如果我们要判断读事件是否就绪

    1. if(rfds.revents & POLLIN){
    2. std::cout << "读事件就绪了..." << std::endl;
    3. }

    3、返回值

    poll函数调用出错,返回-1;

    非阻塞模式下,返回0;

    有事件就绪时,返回 有事件就绪的文件描述符的个数

    二、使用poll模型监听标准输入

    下面就使用 poll 模型来监听 标准输入(0号文件描述符)

    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. int main()
    6. {
    7. struct pollfd rfds;
    8. rfds.fd = 0; //希望内核帮我们关注0号文件描述符上的事件
    9. rfds.events |= POLLIN; //希望内核关注0号文件描述符上的读事件
    10. rfds.revents = 0; //这个参数用于内核通知我们有事件就绪了,让我们赶紧来取
    11. //int timeout = 0; //永久非阻塞等待
    12. int timeout = -1; //永久阻塞等待
    13. while (1)
    14. {
    15. int n = poll(&rfds, 1, timeout);
    16. if (n < 0)
    17. {
    18. std::cerr << "poll调用出错" << std::endl;
    19. }
    20. else if (n == 0)
    21. {
    22. std::cout << "数据还没有准备好" << std::endl;
    23. continue;
    24. }
    25. else
    26. {
    27. std::cout << "数据已经准备好了,赶紧来取吧" << std::endl;
    28. //如果上层一直不取,即不调用recv/read函数,该文件描述符上的读事件会一直处在就绪状态
    29. if (rfds.revents & POLLIN)
    30. {
    31. //说明该文件描述符上有读事件就绪了
    32. char buffer[128];
    33. ssize_t s = read(0, buffer, sizeof(buffer) - 1);
    34. if (s > 0)
    35. {
    36. buffer[s] = 0;
    37. std::cout << "读取到的内容为: " << buffer << std::endl;
    38. }
    39. }
    40. }
    41. }
    42. return 0;
    43. }

    测试结果一(非阻塞模式下)

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

  • 相关阅读:
    深度学习环境搭建【显卡算力、CUDA、pytorch、tensorflow选择】
    数据可视化【原创】vue+arcgis+threejs 实现流光边界线效果
    Android 13 - Media框架(11)- MediaCodec(一)
    应用配置文件 student-system.yaml 生成 Deployment报错,有没有厉害的解决一下的
    pyqt实现简易浏览器
    LRU缓存(哈希+双链表)
    Kafka - 3.x 文件存储不完全指北
    (完全理解)二重积分中的换元积分中的雅可比矩阵
    【网络安全培训】无线局域网的安全威胁都有哪些?
    腾讯云可用区怎么选择?随机可用区有什么区别?
  • 原文地址:https://blog.csdn.net/challenglistic/article/details/127126883