Linux高级IO之poll

 (。・∀・)ノ゙嗨!你好这里是ky233的主页:这里是ky233的主页,欢迎光临~icon-default.png?t=N7T8https://blog.csdn.net/ky233?type=blog

点个关注不迷路⌯'▾'⌯

目录

一、poll函数接口

1.接口

2.poll做了什么工作

3.events和revents的取值

二、代码

三、poll的优缺点


poll将输入参数和输出参数做了分离,所以我们就不用对参数进行重新设定了。并且把上限取消了 

一、poll函数接口

1.接口

#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
// pollfd结构
struct pollfd {
 int fd; /* file descriptor */
 short events; /* requested events */
 short revents; /* returned events */
}

参数说明:

  1. fds是一个poll函数监听的结构列表. 每一个元素中, 包含了三部分内容: 文件描述符, 监听的事件集合, 返 回的事件集合.
  2. nfds表示fds数组的长度.
  3. timeout表示poll函数的超时时间, 单位是毫秒(ms).

2.poll做了什么工作

poll只负责等!

1.用户告诉内核:你帮我关心,哪些fd,哪些事件

2.内核告诉用户:哪些fd,哪些时间已经就绪了

3.events和revents的取值

二、代码

#ifndef __POLL_SVR_H__
#define __POLL_SVR_H__

#include <iostream>
#include <string>
#include <vector>
#include <poll.h>
#include <sys/time.h>
#include "Log.hpp"
#include "Sock.hpp"

#define FD_NONE -1

using namespace std;
// select 我们只完成读取,写入和异常不做处理 -- epoll(写完整)
class PollServer
{
public:
    static const int nfds = 100;
public:
    PollServer(const uint16_t &port = 8080) : _port(port), _nfds(nfds)
    {
        _listensock = Sock::Socket();
        Sock::Bind(_listensock, _port);
        Sock::Listen(_listensock);
        logMessage(DEBUG,"%s","create base socket success");

        _fds = new struct pollfd[_nfds];
        for(int i = 0; i < _nfds; i++) {
            _fds[i].fd = FD_NONE;
            _fds[i].events = _fds[i].revents = 0;
        }
        _fds[0].fd = _listensock;
        _fds[0].events = POLLIN;

        _timeout = 1000;
    }

    void Start()
    {
        while (true)
        {
            int n = poll(_fds, _nfds, _timeout);
            switch (n)
            {
            case 0:
                logMessage(DEBUG, "%s", "time out...");
                break;
            case -1:
                logMessage(WARNING, "select error: %d : %s", errno, strerror(errno));
                break;
            default:
                // 成功的
                HandlerEvent();
                break;
            }
        }
    }

    ~PollServer()
    {
        if (_listensock >= 0)
            close(_listensock);
        if (_fds) delete [] _fds;
    }
private:
    void HandlerEvent() // fd_set 是一个集合,里面可能会存在多个sock
    {
        for(int i = 0; i < _nfds; i++)
        {
            // 1. 去掉不合法的fd
            if(_fds[i].fd == FD_NONE) continue;
            // 2. 合法的就一定就绪了?不一定
            if(_fds[i].revents & POLLIN)
            {
                //指定的fd,读事件就绪
                // 读事件就绪:连接事件到来,accept
                if(_fds[i].fd == _listensock) Accepter();
                else Recver(i);
            }
        }
    }
    void Accepter()
    {
        string clientip;
        uint16_t clientport = 0;
        // listensock上面的读事件就绪了,表示可以读取了
        // 获取新连接了
        int sock = Sock::Accept(_listensock, &clientip, &clientport); // 这里在进行accept会不会阻塞?不会!
        if(sock < 0)
        {
            logMessage(WARNING, "accept error");
            return;
        }
        logMessage(DEBUG, "get a new line success : [%s:%d] : %d", clientip.c_str(), clientport, sock);
        int pos = 1;
        for(; pos < _nfds; pos++){
            if(_fds[pos].fd == FD_NONE) break;
        }
        if(pos == _nfds){
            // 对struct pollfd进行自动扩容
            logMessage(WARNING, "%s:%d", "poll server already full,close: %d", sock);
            close(sock);
        }else{
            _fds[pos].fd = sock;
            _fds[pos].events = POLLIN;
        }
    }
    void Recver(int pos)
    {
        // 读事件就绪:INPUT事件到来、recv,read
        logMessage(DEBUG, "message in, get IO event: %d", _fds[pos]);
        // 暂时先不做封装, 此时select已经帮我们进行了事件检测,fd上的数据一定是就绪的,即 本次 不会被阻塞
        // 这样读取有bug吗?有的,你怎么保证以读到了一个完整包文呢?
        char buffer[1024];
        int n = recv(_fds[pos].fd, buffer, sizeof(buffer)-1, 0);
        if(n > 0){
            buffer[n] = 0;
            logMessage(DEBUG, "client[%d]# %s", _fds[pos].fd, buffer);
        }
        else if(n == 0){
            logMessage(DEBUG, "client[%d] quit, me too...", _fds[pos].fd);
            // 1. 我们也要关闭不需要的fd
            close(_fds[pos].fd);
            // 2. 不要让select帮我关心当前的fd了
            _fds[pos].fd = FD_NONE;
            _fds[pos].events = 0;
        }
        else{
            logMessage(WARNING, "%d sock recv error, %d : %s", _fds[pos].fd, errno, strerror(errno));
            // 1. 我们也要关闭不需要的fd
            close(_fds[pos].fd);
            // 2. 不要让select帮我关心当前的fd了
            _fds[pos].fd = FD_NONE;
            _fds[pos].events = 0;
        }
    }

    void DebugPrint()
    {
        cout << "_fd_array[]: ";
        for(int i = 0; i < _nfds; i++)
        {
            if(_fds[i].fd == FD_NONE) continue;
            cout << _fds[i].fd << " ";
        }
        cout << endl;
    }
private:
    uint16_t _port;
    int _listensock;
    struct pollfd *_fds;
    int _nfds;
    int _timeout;
};

#endif

三、poll的优缺点

优点:

  1. 效率高,和select一样
  2. 节省资源,有大量的连接,只有少量是活跃的
  3. 输入和输出参数分离,不需要大量的重置
  4. 参数级别没有fd的上限

缺点:

  1. poll还是需要遍历,而且不少,在用户层检测时间就绪,与在内核层检测fd就绪,都是一样的
  2. poll需要用户和内核进行拷贝只需要进行一次,但内核到用户需要一直拷贝,这个时少不了的
  3. poll编写也不太容易,但比select容易
`poll_wait` 函数是 Linux 内核中用于实现轮询等待的一个重要函数,其主要作用是将当前进程挂起,等待某个特定的事件发生,例如数据从 IO 设备上到达或者设备错误等。当事件发生时,`poll_wait` 将会唤醒等待的进程,使其可以继续执行。 下面详细介绍一下 `poll_wait` 函数的使用方法。 ## 语法 ```c void poll_wait(struct file *filp, wait_queue_head_t *queue, struct wait_queue_entry *wait); ``` ## 参数 - `filp`:待等待的文件指针; - `queue`:等待队列的头指针; - `wait`:等待队列的条目指针。 ## 功能 将当前进程加入到等待队列中,等待某个特定的事件发生。 ## 注意事项 1. `poll_wait` 函数只能在进程的上下文中使用,不能在中断上下文中使用。 2. 在调用 `poll_wait` 函数之前,必须先调用 `init_waitqueue_head` 函数对等待队列进行初始化。 3. 在等待队列中,每个等待事件都必须使用唯一的等待队列条目。 ## 示例 下面是一个示例程序,演示了如何使用 `poll_wait` 函数: ```c DEFINE_WAIT(wait); init_waitqueue_head(&wait_queue_head); // 等待事件发生 poll_wait(file, &wait_queue_head, &wait); // 判断事件是否已经发生 if (event_occurred) { // 唤醒等待队列中的进程 wake_up_interruptible(&wait_queue_head); } ``` 在上面的示例程序中,首先使用 `DEFINE_WAIT` 宏定义了一个等待队列条目,然后使用 `init_waitqueue_head` 函数对等待队列进行初始化。接着,使用 `poll_wait` 函数将当前进程加入到等待队列中,等待事件发生。当事件发生时,使用 `wake_up_interruptible` 函数唤醒等待队列中的进程,使其可以继续执行。 总之,`poll_wait` 函数是 Linux 内核中实现轮询等待的一个重要函数,它可以方便地实现进程挂起和等待事件发生的功能,是 Linux 内核中非常常用的一个函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值