unix网络编程——I/O多路复用之epoll

1. 基本概念

  当程序进行IO时,如果数据尚未准备好,那么IO将处于阻塞状态。当某个进程有多个打开的文件,比如socket,那么其后的所有准备好读写的文件将受到阻塞的影响而不能操作。不借助线程,单一进程无法在同一时间服务多个文件描述符。非阻挡式IO可以作为一个解决方案,但是效率并不高。首先进程需要不断发IO请求,其次,如果程序可以休眠,让出CPU将提高效率。多任务式IO是在其中任何一个文件描述符就绪时收到通知,此时IO将不会受到阻挡,其余时间处于休眠状态,将CPU资源让给别的进程。

  为了实现I/O多路复用,epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。

2. api

#include <sys/epoll.h>
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
  • int epoll_create(int size) [创建句柄]

  创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

  函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size就是你在这个epoll fd上能关注的最大socket fd数。随你定好了。只要你有空间。可参见上面与select之不同

  • int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)[事件注册函数]

  epoll的事件注册函数,对比select()函数,select()是在监听事件时告诉内核要监听什么类型的事件,而epoll是在调用epoll_ctl函数的时候,先注册要监听的事件类型。

  epfd:epoll_create()的返回值;
  op:表示要进行的操作,操作的动作使用了宏定义:

    1. EPOLL_CTL_ADD: 注册新的fd到epfd中;
    2. EPOLL_CTL_MOD: 修改已经注册的fd的监听事件;
    3. EPOLL_CTL_DEL: 删除epfd中的一个fd;

  fd:关联的文件描述符,表示需要监听的fd;
  event:指向epoll_event的指针,告诉内核需要监听什么事件,epoll_event的结构如下:

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

events描述事件类型,使用了宏定义:

  1. EPOLLIN:表示对应的文件描述符可以读(包括对端socket正常关闭);

  2. EPOLLOUT: 表示对应的文件描述符可以写;

  3. EPOLLPRI: 表示对应的文件描述符有紧急的数据可读(表示有外数据到来);

  4. EPOLLHUP: 表示对应的文件描述符被挂断;

  5. EPOLLET: 将EPOLL设置为边缘触发(Edge Triggered)模式;

  6. EPOLLONESHOT: 只监听一次事件,当监听玩这次事件之后,如果还需要继续监听,需要再次将该socket加入EPOLL队列中。

  • int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout) [等待事件触发]

  等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1表示不确定)。该函数返回需要处理的事件数目,如返回0表示已超时。

3.epoll工作模式

  epoll对文件描述符的操作有两种模式:LT(level trigger,水平触发)和ET(edge trigger,边缘触发)。

  LT模式:水平触发是缺省的工作方式。当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件(进行IO操作)。下次调用epoll_wait时,会再次响应应用程序并通知此事件。由于对于描述事件符在处理前会进行多次通知,因此出错的概率小;

  ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,此时它会假设你知道文件描述符已就绪,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件,直到做了一定操作导致该文件描述符再次变为未就绪状态。但是如果一直对该fd进行IO操作()

  ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

原文地址:https://www.cnblogs.com/Mr24/p/9527206.html

时间: 2024-10-11 22:58:19

unix网络编程——I/O多路复用之epoll的相关文章

【实习记】2014-08-25版本管理svn与git学习对比+看书UNIX网络编程

git也算中等熟练了,对其哲学也明白.但是svn一直半桶水. 上网搜索了几篇svn的好文,做一下总结: <svn分支开发与主干合并(branch & merge) >http://blog.csdn.net/bbirdsky/article/details/24620155 TortoiseSVN图形操作,适合初学者. <SVN中Branch的创建与合并>http://www.cnblogs.com/huang0925/p/3254243.html命令行的svn,适合真正要

【unix网络编程第三版】阅读笔记(五):I/O复用:select和poll函数

本博文主要针对UNP一书中的第六章内容来聊聊I/O复用技术以及其在网络编程中的实现 1. I/O复用技术 I/O多路复用是指内核一旦发现进程指定的一个或者多个I/O条件准备就绪,它就通知该进程.I/O复用适用于以下场合: (1) 当客户处理多个描述符(一般是交互式输入或网络套接字),必须适用I/O复用 (2) 当一个客户处理多个套接字时,这种情况很少见,但也可能出现 (3) 当一个TCP服务器既要处理监听套接字,又要处理已连接套接字,一般就要使用I/O复用 (4) 如果一个服务器既要适用TCP,

UNIX网络编程卷1 回射客户程序 TCP客户程序设计范式

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 下面我会介绍同一个使用 TCP 协议的客户端程序的几个不同版本,分别是停等版本.select 加阻塞式 I/O 版本. 非阻塞式 I/O 版本.fork 版本.线程化版本.它们都由同一个 main 函数调用来实现同一个功能,即回射程序客户端. 它从标准输入读入一行文本,写到服务器上,读取服务器对该行的回射,并把回射行写到标准输出上. 其中,非阻塞式 I/O 版本是所有版本中执行速度最快的,

Unix网络编程中的五种I/O模型_转

转自:Unix网络编程中的的五种I/O模型 下面主要是把unp第六章介绍的五种I/O模型. 1. 阻塞I/O模型 例如UDP函数recvfrom的内核到应用层.应用层到内核的调用过程是这样的:首先把描述符.接受数据缓冲地址.大小传递给内核,但是如果此时 该与该套接口相应的缓冲区没有数据,这个时候就recvfrom就会卡(阻塞)在这里,知道数据到来的时候,再把数据拷贝到应用层,也就是传进来的地址空 间,如果没有数据到来,就会使该函数阻塞在那里,这就叫做阻塞I/O模型,如下图: 2. 非阻塞I/O模

unix网络编程环境搭建

unix网络编程环境搭建 新建 模板 小书匠 1.点击下载源代码 可以通过下列官网中的源代码目录下载最新代码: http://www.unpbook.com/src.html 2.解压文件 tar -xzvf upv13e.tar.gz 3.上传至阿里云 本人本地已经配置好,这次实验是将环境搭建至云服务器中. scp -r unpv13e [email protected]120.76.140.119:/root/program/unp // -r 上传文件夹  4.编译文件 cd unpv13

unix网络编程代码(2)

继续贴<unix网络编程>上的示例代码.这次是一个反射程序,反射是客户端讲用户输入的文本发送到服务器端,服务器端读取客户端发过来的文本消息,然后原封不动的把文本消息返回给客户端.使用tcp协议连接客户端和服务端,我已经在我的阿里云服务器上测试过了,能够完美运行. 首先是头文件wrap.h,在该头文件中,声明了封装部分网络编程套接字api的包裹函数,以及某些宏定义. 1 #ifndef WRAP_H_ 2 #define WRAP_H_ 3 4 #include <stdio.h>

【LINUX/UNIX网络编程】之简单多线程服务器(多人群聊系统)

RT,Linux下使用c实现的多线程服务器.这个真是简单的不能再简单的了,有写的不好的地方,还希望大神轻拍.(>﹏<) 本学期Linux.unix网络编程的第四个作业. 先上实验要求: [实验目的] 1.熟练掌握线程的创建与终止方法: 2.熟练掌握线程间通信同步方法: 3.应用套接字函数完成多线程服务器,实现服务器与客户端的信息交互. [实验内容] 通过一个服务器实现最多5个客户之间的信息群发. 服务器显示客户的登录与退出: 客户连接后首先发送客户名称,之后发送群聊信息: 客户输入bye代表退

Unix网络编程 之 socket基础

基本结构 (这部分的地址均为网络地址<网络字节序>) 1.struct sockaddr:通用套接字地址结构 此结构用于存储通用套接字地址. 数据结构定义: typedef unsigned short sa_family_t; struct sockaddr { sa_family_t sa_family; /* address family, AF_xxx */ char sa_data[14]; /* 14 bytes of protocol address */ };    sa_fa

Unix网络编程之基本TCP套接字编程(上)

TCP客户/服务器实例 服务器程序 #include "unp.h" int main(int argc, char **argv) { int listenfd, connfd; pid_t childpid; socklen_t clilen; struct sockaddr_in cliaddr, servaddr; listenfd = Socket(AF_INET, SOCK_STREAM, 0); //1 bzero(&servaddr, sizeof(servad