• 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. }

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

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

  • 相关阅读:
    如何去掉word上方页眉处的横线
    Flask博客实战 - 实现个人中心及权限管理
    java8常用的新特性
    Golang 编译命令行
    前端性能优化汇总
    【代码随想录】算法训练营 第一天 第一章 数组 Part 1
    制造企业使用APS计划排产需要的条件
    12.5 Hierarchical names (层次化名称)
    小白量化《穿云箭集群量化》(2)量化策略编写(1)
    软件测试的发展与定义
  • 原文地址:https://blog.csdn.net/challenglistic/article/details/127126883