epoll函数及用法

  • epoll介绍
  • 相关函数
  • 相关结构体
  • 例子

一、epoll

Epoll 是一种高效的管理socket的模型,相对于select和poll来说具有更高的效率和易用性。传统的select以及poll的效率会因为 socket数量的线形递增而导致呈二次乃至三次方的下降,而epoll的性能不会随socket数量增加而下降。

二、相关函数

头文件:#include <sys/epoll.h>

  1. int epoll_create(int size):
    功能:生成一个epoll专用的文件描述符epollfd.
    参数size:用来告诉内核要监听的数目一共有多少个。
    返回值:成功时,返回一个非负整数的文件描述符,作为创建好的epoll句柄。调用失败时,返回-1,错误信息可以通过errno获得。
    说明:创建一个epoll句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

  2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    功能:epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。
    参数epfd:epoll_create()函数返回的epoll句柄。
    参数op:要进行的操作EPOLL_CTL_ADD 注册。
    其中参数op的可选值有以下3个:
    EPOLL_CTL_ADD:注册新的fd到epfd中;
    EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
    EPOLL_CTL_DEL:从epfd中删除一个fd;
    参数fd:进行操作的目标文件描述符listen_sock。
    参数event:struct epoll_event结构指针,将fd和要进行的操作关联起来。
    返回值:成功时,返回0,作为创建好的epoll句柄。调用失败时,返回-1,错误信息可以通过errno获得。
    说明:控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。这个系统调用用于操作epoll函数所生成的实例(该实例由epfd指向),向fd实施op操作。

  3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
    功能:等待事件epoll_event(EPOLLIN)的产生。并将发生的sokct fd和事件类型放入到events数组中。
    参数epfd:epoll_create()函数返回的epoll句柄。
    参数events:struct epoll_event结构指针,用于回传代处理事件的数组;用来从内核得到事件的集合。
    参数 maxevents:告诉内核这个events有多大
    参数 timeout: 等待时的超时时间,以毫秒为单位。
    返回值:成功时,返回需要处理的事件数目。调用失败时,返回0,表示等待超时。
    说明:该函数用于轮询I/O事件的发生。
    @epfd:由epoll_create 生成的epoll专用的文件描述符;
    @epoll_event:用于回传代处理事件的数组;
    @maxevents:每次能处理的事件数;
    @timeout:等待I/O事件发生的超时值;
    成功:返回发生的事件数;失败:-1

三、相关结构体 epoll_event

头文件 #include <fcntl.h>

 struct epoll_event {
     __uint32_t events;      /* epoll event */
     epoll_data_t data;      /* User data variable */
 };  //结构体epoll_event被用于注册所感兴趣的事件和回传所发生待处理的事件

typedef union epoll_data {
    void *ptr;
     int fd;
     __uint32_t u32;
     __uint64_t u64;
 } epoll_data_t;//保存触发事件的某个文件描述符listen_sock相关的数据

 其中events表示感兴趣的事件和被触发的事件,可能的取值为:
EPOLLIN :表示对应的文件描述符可以读;
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI: 表示对应的文件描述符有紧急的数可读;
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET:   ET的epoll工作模式;

四、例子

/*
 *@brief:epoll接收
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include "x_log.h"

#define MAX_EVENTS 100
#define LISTENQ 10  
#define DPORT 10003 

//设置socket连接为非阻塞模式
void setnonblocking(int sockfd)
{
	int opts = fcntl(sockfd,F_GETFL);
	if(opts < 0)
	{
		perror("fcntl(sockfd,F_GETFL)\n");
		exit(1);
	}

	opts = (opts | O_NONBLOCK);
	if(fcntl(sockfd,F_SETFL,opts) < 0)
	{
		perror("fcntl(sockfd,F_SETFL)\n");
		exit(1);
	}
}

int main()
{
	int listen_sock,conn_sock;
	struct sockaddr_in clientaddr, serveraddr;
	socklen_t socklen;
	int epollfd;
	int i,nfds;
	struct epoll_event ev,events[100];//ev用于注册事件,数组用于回传要处理的事件

	listen_sock = socket(AF_INET, SOCK_STREAM, 0);
	if(0 > listen_sock)
	{
		perror("socket");
		return -1;
	}	
	//printf("listen_sock: %d\n",listen_sock);	
	setnonblocking(listen_sock);

    //创建一个epoll对象
	epollfd=epoll_create(MAX_EVENTS);//生成用于处理accept的epoll专用的文件描述符
	ev.events = EPOLLIN|EPOLLET;//文件描述符可以读|ET边缘触发模式
	ev.data.fd = listen_sock;
	//注册epoll事件
	epoll_ctl(epollfd,EPOLL_CTL_ADD,listen_sock,&ev);

	//设置服务器端地址信息
	bzero(&serveraddr, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(DPORT);
	serveraddr.sin_addr.s_addr = ntohl(INADDR_ANY);

	//设置套接字选项避免地址使用错误 
	int on=1;  
	if((setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)  
	{  
		perror("setsockopt");  
		return -1;
	} 

	//绑定
	if(0 > bind(listen_sock,(struct sockaddr *)&serveraddr, sizeof(serveraddr)))
	{
		perror("bind");
		return -1;
	}

	//监听
	if(0 > listen(listen_sock,LISTENQ))
	{
		perror("listen");
		return -1;
	}

	while(1)
	{
		nfds = epoll_wait(epollfd,events,MAX_EVENTS,200);
		for(i=0;i<nfds;++i)//开始处理所发生的事件
		{
			if((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))) 
			{
				close (events[i].data.fd);
				continue;
			}
			/*有新连接到来*/
			else if(events[i].data.fd == listen_sock)
			{
				conn_sock = accept(listen_sock,(struct sockaddr *)&clientaddr, &socklen);
				setnonblocking(conn_sock);

				ev.events = EPOLLIN|EPOLLET;//设置用于读操作的事件
				ev.data.fd = conn_sock;
				epoll_ctl(epollfd,EPOLL_CTL_ADD,conn_sock,&ev);//注册ev
				continue;
			}
			/*有新数据到来*/
			else
			{
				//读取数据
				// ......
			}
		}
	}

	close(conn_sock);
	close(listen_sock);
	return 0;
}
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`epoll`是一个Linux系统调用,它允许程序监听一个文件系统的变化,即文件的读写完成或可写事件等。这通常对于多路复用器或网络编程特别有用,因为它们需要监控多个套接字的操作,并且对于同时到达的事件,需要进行选择并执行适当的操作。 下面是一些基本的用法: 1. **创建epoll实例**:使用`epoll_create()`函数创建一个新的epoll实例。这个函数返回一个文件描述符,可以用于后续的调用。 ```c int epoll_create(int size); ``` 其中,`size`参数指定了文件描述符数组的大小。 2. **添加文件描述符到epoll**:使用`epoll_ctl()`函数将文件描述符添加到epoll实例中。添加的方式有两种: * `EPOLL_CTL_ADD`:添加新的文件描述符到epoll实例。 * `EPOLL_CTL_MOD`:修改已存在的文件描述符在epoll实例中的行为。对于已经添加的文件描述符,必须使用这个选项。 ```c int epoll_ctl(int epollfd, int op, int fd, struct epoll_event *event); ``` 其中,`epollfd`是创建的epoll实例的文件描述符,`op`是操作类型(通常为`EPOLL_IN`或`EPOLL_OUT`),`fd`是要添加的文件描述符,`event`是一个结构体,包含了要添加的文件描述符的行为信息。 3. **读取事件**:使用`epoll_wait()`函数读取事件。这个函数会阻塞当前进程,直到有事件发生或者超时。它返回一个文件描述符数组,每个元素对应一个新的事件。返回的文件描述符的数量可以是一个固定的值,也可能是大于之前设定的值的值。可以通过遍历数组获取具体。的事件 ```c int epoll_wait(int epollfd, struct epoll_event *events, int maxevents, int timeout); ``` 注意,调用此函数后返回的文件描述符数组的指针可能会在之后的调用中被更改,以反映在事件之间发生的更改。 4. **清理**:当不再需要epoll实例时,需要调用`close()`函数关闭它。这样可以释放与该实例关联的资源。 这就是基本的`epoll`的使用方法。请注意,在实际使用中,还需要考虑一些其他的因素,例如错误处理、并发控制等。此外,为了正确使用`epoll`,你需要理解事件如何被表示以及如何处理这些事件。具体的使用细节和例子可以在Linux内核源代码中找到。 在使用这些函数时,你需要注意它们的返回值和错误代码,以确保你的程序在发生错误时能够正确地处理。另外,你也需要根据你的应用程序的需求来配置和选择合适的事件类型和行为。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值