I/O多路复用之poll

之前有说过I/O多路复用中的select,这次的poll其实和select很相似。它们之间的区别在于我们要如何指定待检查的文件描述符。在select中,提供三个集合readfds,writefds和exceptfds用来标明所感兴趣的文件描述符集合。而在poll中提供一列文件描述符,并在每个文件描述符添加所感兴趣的事件。

poll

poll的参数

第一个参数struct pollfd*类型的参数,也就是该类型的一个数组。

struct pollfd结构体总共有三个成员

对于该结构体,第一个参数是所关心的文件描述符,第二个参数为文件描述符所关心的事件,第三个参数是该文件描述符实际发生的事件。

第二个参数nfds是前面数组对应的大小

第三个参数用来设置poll的阻塞行为。当timeout为-1时表示阻塞等待。为0时表示非阻塞等待,大于0时表示等待的时间。

poll的返回值

返回值大于0时,表示有文件描述符已经就绪。

返回值等于-1时,表示有错误发生。

返回值等于0时,表示poll的等待超时。

poll的测试

#include<iostream>                                                       
#include<poll.h>
#include<cstring>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
using namespace std;

const int NUM = 64;
class PollServer
{
  private:
    int lsock;
    int port;
  public:
    PollServer(int _port)
    {
      port = _port;
    }
    ~PollServer()
    {
      close(lsock);
    }
    void InitServer()//初始化服务器
    {
      lsock = socket(AF_INET,SOCK_STREAM,0);
      if(lsock < 0)
      {
        cerr<<"sock error!"<<endl;
        exit(-1);
      }
      sockaddr_in local;
      local.sin_family = AF_INET;
      local.sin_port = htons(port);
      local.sin_addr.s_addr = INADDR_ANY;
      if(bind(lsock,(struct sockaddr*)&local,sizeof(local)) < 0)
      {
        cerr<<"bind error!"<<endl;
        exit(-1);
      }
      if(listen(lsock,5) < 0)
      {
        cerr<<"listen error!"<<endl;
        exit(-1);
      }
    }
    void ServerStart()
    {
      struct pollfd rfds[NUM];//观测64个文件描述符
      for(int i=0;i < NUM;i++)//先初始化一下
      {
        rfds[i].fd = -1;
        rfds[i].events = 0;
        rfds[i].revents = 0;
      }
      rfds[0].fd = lsock;//首先将监听套接字添加进去
      rfds[0].events |= POLLIN;//关心监听套接字上面的输入事件
      rfds[0].revents = 0;
      while(true)
      {
        switch(poll(rfds,NUM,-1))//以阻塞的方式等待
        {
          case 0:
            cout<<"time out..."<<endl;
            break;
          case -1:
            cout<<"poll error!"<<endl;
            break;
          default:
            for(int i=0; i < NUM; i++)//返回值大于0,遍历所有的套接字
            {
              if(rfds[i].fd == -1)
                continue;
              if(rfds[i].revents & POLLIN)
              {
                if(rfds[i].fd == lsock)
                {  
                  cout<<"get a new link..."<<endl;
                  //新的连接到来
                  //to do
                }
                else 
                {    
                  //其它套接字上面的读事件触发
                  //to do
                }
              }
            }
            break;
        }
      }
    }
};

int main(int argc,char* argv[])
{
  if(argc!=2)
  {
    cout<<"parameter error!!!"<<endl;
    exit(-1);
  }
  PollServer* ps = new PollServer(atoi(argv[1]));
  ps->InitServer();
  ps->ServerStart();
  return 0;
}                                                                             

poll与select的对比

1、poll可以监测文件描述符的个数理论上不在受限,而select所使用的的数据类型fd_set对于被检查的文件描述符数量有一个上限限制(FD_SETSIZE)。

2、poll不需要每次调用的时候重新设置套接字所关心的事件,因为输入events与输出revents是分离的。但是select却不是这样,由于输入输出参数都是同一个参数readfds、writefds等,所以如果想继续关心该文件描述上的事件,需要更新fd_set参数。

虽然poll一定程度上解决了select的部分问题,但是二者还是具有相同的问题

1、内核是采用循环检测的方式来检测所有文件描述符上的事件是否处于就绪的状态,当面临大量的文件描述符时,这个过程将非常的耗时。

2、除了内核需要循环检测以外,用户也需要检查返回数据结构中的每个元素,以查明是哪个文件描述符就绪了,这样也很耗费时间。

3、用户空间到内核空间的频繁数据的拷贝。程序都必须传递一个表示所有需要被检查的文件描述符的数据结构到内核,内核检查过描述符后,修改这个数据结构并返回给程序。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值