TOP

20分钟了解Epoll + 聊天室实战(二)
2019-04-02 20:08:09 】 浏览:479
Tags:20分钟 了解 Epoll 聊天室 实战

代表select阻塞最多10秒超时返回,监听标准输入的 读ready状态,会发现程序处于阻塞状态,直到10秒后select返回,打印 0:,也就是没有ready

运行./select - 0r 代表select不会超时直到 标准输入 读ready,程序会一直挂起,直到我们敲回车,返回0:r

运行./select 5 0r 1w 代表最多5秒超时,同时监听标准输入的读状态,和标准输出的写状态,返回0: 1:w

2.2、poll

poll跟select机制基本一样,不同点在于select将监听的文件描述符分开到3个集合中,poll只需一个文件描述符列表

1>定义

#include <poll.h> 

int poll(struct pollfd fds[], nfds_t nfds, int timeout);

2>参数

  • struct pollfd fds:文件描述符数组
struct pollfd {     
    int   fd;               /* File descriptor */     
    short events;           /* Requested events bit mask */     
    short revents;          /* Returned events bit mask */ 
};
  • nfds:fds数组中文件描述符的总数
  • timeout:超时时间,传-1阻塞直到有文件描述符ready,传0不阻塞,传>0的值,不管有没有ready的文件描述符,到指定时间超时返回

3>返回值

  • 返回-1:有错误产生
  • 返回0:没有任何文件描述符ready
  • 返回正数:fds元素revents不为0的文件描述的个数
3、poll和select存在哪些问题?

1、每次调用poll()或select(),内核必须检查传过来的所有文件描述符,当文件描述符超过一定数量后,光是检查这一步就已经非常耗时了

2、每次调用poll()或select(),需要从用户态初始化文件描述符的数据结构,然后传递到内核态,在内核态检查IO状态,然以将状态更新到文件描述符数据结构,再返回这个数据结构,数据需不断的在用户态和内核态间拷贝,同样当文件描述符数量很大时,非常耗费CPU时间

3、每次调用完poll()或select(),还要遍历返回的所有文件描述符,判断状态,浪费时间

解决以上问题的关键是:1)不要每次都在内核态和用户态复制这些监听的文件描述符数据 2)只返回IO ready 的文件描述符,不要只标识状态,自己还需要再遍历一遍,因此,Linux给了我们epoll。

二、Epoll

Linux的epoll,也是I/O多路复用的一种实现方式,同样是同时监听多个文件描述符的I/O状态,他有如下几个优势:

  • 同时监听的大量文件描述符,效率高于select和epoll
  • epoll api同时提供了水平触发通知和边缘触发通知可选,select和epoll只有水平触发通知的方式(两种通知方式后面会讲)
  • 对于监听文件描述的具体事件(读、写等,通过bit mask的方式),更为灵活

不像select和poll直接就是系统调用的函数,epoll由3个系统调用函数组成

1、epoll的3个函数

1.1、epoll_create

通过调用epoll_create,创建一个新的epoll内核数据结构实例,返回该实例的文件描述符,然后,调用进程可以使用这个文件描述符向epoll实例添加、删除或修改它想要监视的其他文件描述符

#include <sys/epoll.h>
int epoll_create(int size);

1.2、epoll_ctl

进程可以通过调用epoll_ctl向epoll实例添加它希望监视的文件描述符,所有这些文件描述符由epoll实例维护在interest list中。当被监视的文件描述符为I/O做好准备时,他们就进入到ready list,ready list是interest list的一个子集

定义:

#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

参数:

  • epfd:epoll_create返回的文件描述符
  • fd:准备加入监视列表(interest list)的文件描述符
  • op:要对这个fd做什么操作,有3种
    • 添加:EPOLL_CTL_ADD
    • 删除:EPOLL_CTL_DEL
    • 修改:EPOLL_CTL_MOD
  • event:指向epoll_event结构体的指针,结构体中存储着我们实际想要监视fd的哪些事件

epoll_event结构:

struct epoll_event {
  __uint32_t events;  /* Epoll events */
  epoll_data_t data;  /* User data variable */
};

events:
事件的成员是位掩码,比如fd是一个socket,我们可能希望监视他,以获取套接字缓冲区上的新数据(使用EPOLLIN),如果希望事件的通知机制使用边缘触发的方式,可以使用EPOLLET。也就是读操作就绪,使用边缘触发的通知方式,需要这样指定events:EPOLLIN | EPOLLET

所有可以指定的events见http://man7.org/linux/man-pages/man2/epoll_ctl.2.html

data:
epoll_data_t 是一个union,可以存储一些数据,当该文件描述符ready的时候,返回给调用监听的进程

typedef union epoll_data {     
    void *ptr;          /* Pointer to user-defined data */     
    int fd;             /* File descriptor */     
    uint32_t u32;        /* 32-bit integer */     
    uint64_t u64;        /* 64-bit integer */ 
} epoll_data_t;

返回值:返回0执行成功,-1发生错误

1.3、epoll_wait

通过调用epoll_wait,可以获取之前监听的所有事件(interest list)中I/O准备好的事件(ready list)

定义:

#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *evlist, int maxevents, int timeout);

参数:

首页 上一页 1 2 3 4 下一页 尾页 2/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇[C]最大公约数和最小公倍数 下一篇C++编程丨C++11的指针