多路I/O复用:epoll整理

1. epoll的优势:

(1) epoll模型是基于事件的通知方式,事先为每个建立连接的文件描述符注册事件,一旦该描述符就绪,内核会采用类似callback的回调机制,将文件描述符加入epoll指定的文件描述符集中。虽然epoll机制中返回的同样是就绪文件描述符的数量,但epoll中的文件描述符集只存储了就绪的文件描述符,服务器进程无需再扫描所有连接的文件描述符。
(2) epoll 没有对fd描述符有限制,理论上取决于系统内存大小,可以通过cat/proc/sys/fs/file-max查看,大概1G内存创建10w个连接
在这里插入图片描述
(3) epoll的具体实现使用mmap(内存映射机制)加速内核与用户空间消息传递,不必再将内核中的文件描述符复制到内存空间。

2. epoll 的三个系统调用接口

2.1 int epoll_create (int size)

SYNOPSIS:  #include <sys/epoll.h>
FUNCTION:  epoll_create
DESCRIPTION:	
epoll_create() returns a file descriptor referring to the new epoll instance
(创建一个epoll实例与文件描述符)。
PARAMETER: 参数size 为该epoll中可监听的文件描述符的最大个数。
RETURN VALUE:
函数调用成功,返回一个非负的文件描述符;否则失败,返回-1并设置错误
On success, these system calls return a nonnegative file descriptor. On error, -1 is returned, and errno is set to indicate the error.
ERRORS:EINVAL size is not positive(失败并报参数size 不是正数的错误)

2.2 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)

SYNOPSIS:  #include <sys/epoll.h>
FUNCTION:  epoll_ctl
DESCRIPTION:用于添加,修改,删除要监听的event事件。
	Valid values for the op argument are:
    EPOLL_CTL_ADD:将新的fd注册到epfd(epoll实例对应得文件符)中
    EPOLL_CTL_MOD:修改已注册的fd
    EPOLL_CTL_DEL:删除epfd中的fd
PARAMETER: 
(1)Op参数:
    EPOLL_CTL_ADD:将新的fd注册到epfd(epoll实例对应得文件符)中
    EPOLL_CTL_MOD:修改已注册的fd
    EPOLL_CTL_DEL:删除epfd中的fd
(2)参数event: 用于设置要监听的事件,event是一个epoll_event类型的指针,其结构体定义如下:
     struct epoll_event {
               uint32_t     events;      /* Epoll events */
               epoll_data_t data;        /* User data variable */
};
struct epoll_event中成员events表示要监控的事件,该成员由一些单一事件组成的位集,宏定义如下:
事件宏
•	EPOLLIN:表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
•	EPOLLOUT: 表示对应的文件描述符可以写;
•	EPOLLPRI: 表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
•	EPOLLERR: 表示对应的文件描述符发生错误;
•	EPOLLHUP: 表示对应的文件描述符被挂断;
•	EPOLLET: 将 EPOLL设为边缘触发(Edge Triggered)模式(默认为水平触发),这是相对于水平触发(Level Triggered)来说的。
•	EPOLLONESHOT: 只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
struct epoll_event中成员data的数据类型是共用体epoll_data_t,其定义如下
 	typedef union epoll_data {
           void        *ptr;
           int          fd;
           uint32_t     u32;
           uint64_t     u64;
  	 } epoll_data_t;

RETURN VALUE:函数调用成功,返回0;否则失败,返回-1并设置错误

ERRORS:
 (1)EBADF:  epfd or fd is not a valid file descriptor.
EEXIST: op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered with this epoll instance.

 (2)EINVAL: epfd is not an epoll file descriptor, or fd is the same as epfd, or the requested operation op is not supported by this interface.

 (3)ENOENT:op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll instance.

 (4)ENOMEM:There was insufficient memory to handle the requested op control operation.

 (5)ENOSPC:The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance.  See epoll for further details.

 (6) EPERM:The target file fd does not support epoll.  This error can occur if fd refers to, for example, a regular file or a directory.

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

SYNOPSIS:  #include <sys/epoll.h>
FUNCTION:  epoll_wait
DESCRIPTION:	
	用于等待epoll句柄中所监控的事件的发生,当有一个或者多个事件发生或等待超时后,epoll_wait返回。
PARAMETER: The memory
(1)	events: 指向发生epoll_create()调用时系统事先预备的空间,当有监听事件发生时,内核将该事件复制到此空间中
(2)	events:表示events事件的大小,不能超过epoll_create()调用时所传入参数的大小
(3)	timeout: 超时时间,用于设置epoll_wait的工作方式。若设置为0,则立刻返回;若设置为-1,则无限等待;否则表示等待一定时长。

RETURN VALUE:
	函数调用成功,返回就绪文件描述符的数量;若等待超时后并无就绪文件描述符则返回0;若调用失败则返回-1并设置错误。
ERRORS:
(1)	EBADF:  epfd is not a valid file descriptor.
(2)	EFAULT: The memory area pointed to by events is not accessible with write permissions.
(3)	EINTR: The call was interrupted by a signal handler before either  any of the requested events occurred or the timeout expired; see signal
(4)	EINVAL:epfd is not an epoll file descriptor, or maxevents is less than or equal to zero.

3. epoll两种工作模式 LT 和 ET

二者的差异在于 level-trigger (LT) 模式下只要某个 socket 处于 readable/writable 状态,无论什么时候进行 epoll_wait 都会返回该 socket;而 edge-trigger (ET) 模式下只有某个 socket 从 unreadable 变为 readable 或从unwritable 变为 writable 时,epoll_wait 才会返回该 socket。

从socket读数据:
在这里插入图片描述
从socket写数据:
在这里插入图片描述
所以, 在epoll的ET模式下, 正确的读写方式为:
读: 只要可读, 就一直读, 直到返回0, 或者 errno = EAGAIN
写: 只要可写, 就一直写, 直到数据发送完, 或者 errno = EAGAIN

Epoll的工作模式在调用注册函数epoll_ctl()时确定,
由该函数的参数event的成员events指定。默认情况下epoll的工作模式为水平触发,若要将其设置为边缘触发模式,需使用宏EPOLLET对event进行设置,具体示例如下:
event.events=EPOLLIN|EPOLLET
之后需要在循环中不断调用,保证将文件描述符中的数据全部读出。
下面给出具体案例,来展示epoll在边缘触发模式下如何实现双端通讯。ET模式只能工作在非阻塞模式下,否则单纯使用epoll(单进程)无法同时出来多个文件描述符。在实现案例之前先介绍一下设置文件描述符状态的方法,linux系统中可使用fcntl()函数来设置文件描述符的属性。

	int fcntl(int fd,int cmd,…/*arg*/);
/**********************************************************
SYNOPSIS:  #include < fcntl.h>
FUNCTION:  epoll_ fcntl
DESCRIPTION:	获取或修改已打开文件的属性
PARAMETER: 
(1)	fd:为被操作文件描述符
(2)	cmd:为操作fd的命令
(3)	用来接收命令cmd所需要的参数,该值可以为空
**********************************************************/
案例:
	flag = fcntl(fd,F_GETFL);
	flag |= O_NONBLOCK;
	fcntl(fd,F_SETFL,flag);

4. 实例

使用epoll模型搭建多路I/O转接服务器,服务器可接受客户端数据并将接收的数据转换为大写,写回客户端;客户端可向服务端发送数据,并将服务端的数据打印在终端。
(1)服务端为LT模式时
使用epoll模型搭建多路I/O转接服务器,服务器可接受客户端数据并将接收的数据转换为大写,写回客户端;客户端可向服务端发送数据,并将服务端的数据打印在终端。

#include <cstdio>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/epoll.h>
#include<sys/socket.h>
#include<ctype.h>
#include<errno.h>

#define MAXLINE 80                   //缓冲区数组大小
#define SERV_PORT 58000               //端口号
#define OPEN_MAX 1024                //最大打开文件描述符的数量


int main()
{
   
    int listenfd, connfd, sockfd;
    int nready, efd;
    int res, maxi;
    char buf[MAXLINE], str[INET_ADDRSTRLEN];
    socklen_t clilen;
    int client[OPEN_MAX];
    struct  sockaddr_in cliaddr, servaddr;
    struct  epoll_event tep, ep[OPEN_MAX];
    listenfd = socket(PF_INET, SOCK_STREAM, 0);
    if (listenfd == -1)
    {
   
        perror("socket");
        exit(EXIT_FAILURE);
    }
    bzero(&servaddr, sizeof(servaddr))
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值