IO复用一select, poll, epoll用法说明

…衆ロ難τιáo~ 提交于 2019-12-02 11:02:21

三种IO复用类型

  1. Select系统调用

#include<sys/select.h>

int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* execptfds,struct timeval* timeout);

#nfds表示监听的文件描述符总数;

#readfds,writefds,execptfds分别表示对应的fd_set类型的集合

可以如下定义:fd_set readfds,writefds,execptfds

#timeout表示select函数的超时时间

Struct timeval

{

Long tv_sec;

Long tv_usec;

}

如果timeval成员变量均为0,则select立即返回;如果timeout设置为NULL,则select将一直阻塞,直到某个文件描述符就绪。

# FD_ZERO(fd_set *fdset);清楚fdset的所有位,如FD_ZERO(&readfds);

#FD_SET(int fd,fd_set* fdset);设置fdset的位,也就是将某个文件描述符加入到fdset中,如FD_SET(0,&readfds),将标准输入加入到fdset中

#int FD_ISSET(int fd,fd_set * fdset);测试fdset的某个位是否被设置,也就是测试文件描述符中的fd是否有事件发生

   #select成功时返回就绪(可读,可写和异常事件)文件

  2. Poll系统调用

#include<poll.h>

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

#fds是pollfd类型的数组

 Struct pollfd

{

int fd;//文件描述符

int events;//注册该文件描述符要监听的事件

short revents;//由内核修改,判断该文件描述符实际发生的事件

}

#nfds表示文件描述符数组的大小

#timeout指定poll的超时值;当timeout位-1时,poll调用将永远阻塞;当timeout为0时,poll调用将立即返回。

例:

pollfd fds[2];//pollfd的数组

fds[0].fd=0;//注册标注输入

fds[0].events=POLLIN;//监听输入事件

fds[0].revents=0;//初始化

fds[1].fd=sockfd;//注册网络socket文件描述符

fds[1].events=POLLIN | POLLRFHUP;//监听

fds[1].revents=0;

int ret=poll(fds,2,-1);

#poll调用成功时返回就绪(可读,可写和异常事件)文件

  3. Epoll系统调用

#include<sys/epoll.h>

int epoll_create(int size);

#epoll把文件描述符放在内核的一个时间表中

#改函数注册内核事件表需要创建多大size

#返回值作为内核事件表的索引

 

#include<sys/epoll.h>

int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout)

#在一段时间内timeout等待一组文件描述符上的事件

#epfd表示上面注册的内核事件表的索引

#events用于存储内核事件表中就绪的事件,将内核事件表中就绪的事件复制到events中,events只用来输出epoll_wait检测到的就绪事件

#maxevents指定epoll_wait最大监听多少个事件

#timeout

#返回值表示就绪的文件描述符的个数,且就绪的文件描述符就存储于events中,直接遍历events[0,ret-1]

# struct epoll_event

{

_uint32_t events;

//表示注册的文件描述符索要监听的事件,与pollfd结构体中events类似(E)

epoll_data_t data;

}

#typedef union epoll_data//联合体

{

void *ptr;

int fd;//最常用fd,指向目标文件描述符

unint32_t u32;

uint64 u64;

}

#include<sys/epoll.h>

int epoll_ctl(int epfd,int op,struct epoll_event *event);//操作内核事件表

  • op选项:EPOLL_CTL_ADD,往事件表中注册fd事件

       EPOLL_CTL_MOD,修改fd上的注册事件

       EPOLL_CTL_DEL,删除fd上注册的事件

event表示对内核事件表进行操作的具体情况

   #向内核事件表中注册事件

void addfd(int epollfd,int fd)//epollfd表示内核事件表索引fd表示事件描述符

{

epoll_event event;

event.events=EPOLLIN;

event.data.fd=fd;//将文件描述符添加到内核事件表中

epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);

}

例:

epoll_event events[MAX_EVENT_NUMBER];

int epollfd=epoll_create(5);//注册大小为5的内核事件表

assert(epollfd!=-1);

addfd(epollfd,int fd);//将文件描述符fd添加到内核事件表

while(1)

{

int number=epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1)

for(int i=0;i<number;i++)

{

//遍历监听到的事件

int sockfd=events[i].data.fd;

if(sockfd==listenfd)

//对于socket,如果是监听端口有事件到来,则创建新的连接

{

  struct sockaddr_in client_address;

  socklen_t client_addrlength=sizeof(client_address);

int connfd=accept(listenfd,(struct sockaddr*) &client_address,&client_addrlength) ;//从监听端口中取出事件

addfd(epollfd,connfd);//将事件注册到内核事件表中

}

else if(events[i].events & EPOLLIN)//表示客户端有数据发送到服务端

{

//接收客户端的数据

}

 

}

}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!