Linux下EPoll通信模型简析

EPoll基于I/O的事件通知机制,由系统通知用户那些SOCKET触发了那些相关I/O事件,事件中包含对应的文件描述符以及事件类型,这样应用程序可以针对事件以及事件的source做相应的处理(Acception,Read,Write,Error)。相比原先的SELECT模型(用户主动依次检查SOCKET),变成被动等待系统告知处于活跃状态的SOCKET,性能提升不少(不需要依次遍历所有的SOCKET,而只是对活跃SOCKET进行事件处理)。

  基本步骤:

  擅长对大量并发用户的请求进行及时处理,完成服务器与客户端的数据交互。一个简单实现步骤如下:

  (1) 创建侦听socket:ListenSock,将该描述符设定为非阻塞模式,调用Listen()函数在该套接字上侦听连接请求。

  (2) 使用epoll_create()函数创建文件描述,设定可管理的最大socket描述符数目。

  (3) 将ListenSock注册进EPoll中进行监测

  (4) EPoll监视启动,epoll_wait()等待epoll事件发生。

  (5)如果epoll事件表明有新的连接请求,则调用accept()函数,并将新建立连接添加到EPoll中。若为读写或者报错等,调用对应的Handle进行处理。

  (6) 继续监视,直至停止。

上诉过程只是一个简单的线性实例,在实际的应用过程中,为了提高监视效率,常常将EPOLL监听到的事件交给其他专门的任务线程进行处理,以提高EPoll监视的效率。

  主要涉及API

  1.EPoll创建

  int epoll_create(int size)

  该函数生成一个epoll专用文件描述符,其中的参数是指定生成描述符的最大范围。在linux-2.4.32内核中根据size大小初始化哈希表的大小,在linux2.6.10内核中该参数无用,使用红黑树管理所有的文件描述符,而不是hash.

  2、epoll_ctl函数

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

  该函数用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。

  参数:epfd:由 epoll_create 生成的epoll专用文件描述符;

  op:操作类型,有如下取值:

  EPOLL_CTL_ADD 注册、

  EPOLL_CTL_MOD 修改、

  EPOLL_CTL_DEL 删除

  fd:要控制的文件描述符;

  event:指向epoll_event的指针; 如果调用成功返回0,不成功返回-1

  epoll_event 结构体的events字段是表示感兴趣的事件,取值为:

  EPOLLIN:表示对应的文件描述符可以读;

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

  EPOLLPRI:表示对应的文件描述符有紧急的数据可读;

  EPOLLERR:表示对应的文件描述符发生错误;

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

  EPOLLET:表示对应的文件描述符有事件发生;

  3、事件等待函数

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

  该函数用于轮询I/O事件的发生;

  参数: epfd:由epoll_create 生成的epoll专用的文件描述符;

  epoll_event:用于回传等待处理的事件数组;

  maxevents:每次能处理的事件数;

  timeout:等待I/O事件发生的超时值(ms);-1永不超时,直到有事件产生才触发,0立即返回

  主要数据结构:

  typedef union epoll_data {

  void *ptr;

  int fd;

  __uint32_t u32;

  __uint64_t u64;

  } epoll_data_t;

  struct epoll_event {

  __uint32_t events; /* Epoll events */

  epoll_data_t data; /* User data variable */

  };

  一般我们在编程时,利用event变量存储事件对应的文件描述符以及事件类型。

  实例代码

  服务器段代码

  int EPollServer()

  {

  int srvPort = 6888;

  initSrvSocket(srvPort);

  /* 创建 epoll 句柄,把监听socket加入到epoll集合里 */

  epollfd = epoll_create(MAX_EVENTS);

  struct epoll_event event;

  event.events = EPOLLIN | EPOLLET;

  event.data.fd = srvfd;

  if ( epoll_ctl(epollfd, EPOLL_CTL_ADD, srvfd, &event) < 0 )

  {

  printf(“epoll Add Failed: fd=%d\n”, srvfd);

  return -1;

  }

  printf( “epollEngine startup:port %d”, srvPort);

  while(1)

  {

  /*等待事件发生*/

  int nfds = epoll_wait(epollfd, eventList, MAX_EVENTS, -1);

  if ( nfds == -1 )

  {

  printf( “epoll_wait”);

  continue;

  }

  /* 处理所有事件 */

  int n = 0;

  for (; n < nfds; n++)

  handleEvent(eventList + n);

  }

  close(epollfd);

  close(srvfd);

  };

  在事件处理handleEvent中(分为连接事件处理以及数据接收发送事件)

  void handleEvent(struct epoll_event* pEvent)

  {

  if (pEvent->data.fd == srvfd)

  {

  AcceptConn(srvfd);

  }else{

  RecvData(pEvent->data.fd);

  SendData(pEvent->data.fd);

  epoll_ctl(epollfd, EPOLL_CTL_DEL, pEvent->data.fd, pEvent);

  }

  }

  //从标准输入读取数据,发送给服务器端,服务器端在原样返回,客户端再接收并予以显示

  void handle(int sockfd)

  {

  char sendline[MAXLINE];

  char recvline[MAXLINE];

  int n;

  for (;;) {

  if (fgets(sendline, MAXLINE, stdin) == NULL)

  break;

  if (read(STDIN_FILENO, sendline, MAXLINE) == 0)

  break;

  n = write(sockfd, sendline, strlen(sendline));

  n = read(sockfd, recvline, MAXLINE);

  if (n == 0) {

  printf(“echoclient: server terminatedprematurely\n”);

  break;

  }

  write(STDOUT_FILENO, recvline, n);

  //如果用标准库的缓存流输出有时会出现问题

  //fputs(recvline, stdout);

  }

  }

  运行结果(Linux下截图麻烦,直接复制控制台结果)

  客户端:

  [email protected]:~$ ./echoclient

  welcome to echoclient

  123456

  123456

  服务器端:

  [email protected]:~/source/EPollProject$ ./EPoll

  epollEngine startup port 6888

  handleEvent function, HANDLE: 3, EVENT is 1

  Accept Connection: 5

  handleEvent function, HANDLE: 5, EVENT is 1

  RecvData function

  SOCKET HANDLE: 5: CONTENT: 123456

  content is 123456

  SendData function

  SendData: 123456

  注:

  1.此处只是学习了EPoll基本模型,在实际应用中,为了提高EPoll模型的监视效率,一般在监视线程中只做监视,不过事件处理工作,而是将事件交付其他线程处理。

  2. 为了提高事件处理的效率,所以我们尽量避免在有事件时开辟线程处理,处理完关闭,一般在系统启动时会创建线程池,将事件交与线程池中的空闲线程进行处理。在事件的处理过程中不会有县城的创建、销毁等操作。效率也提高了。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-06 13:52:03

Linux下EPoll通信模型简析的相关文章

linux下epoll机制实现多用户并发连接

linux下epoll机制我就不再阐述了,网上找了好多资料和例子,发现和我想要的功能完全不一样,所以就自己写了一个. 实现的功能是,有很多客户端同时连接服务器,例如,S为服务器,有客户端A和客户端B要连接服务器,他们都需要验证密码,若A先连接服务器,此时不输入密码:B再连接服务器,此时,B输入密码的话,服务器不会响应,必须等A先验证完,才可以.我给的例子,可以实现:若A连接了服务器而没有输入密码,B再连接,这是服务器可以为B生成一个线程,确认B的密码后,将客户端的client_sockfd加入到

linux 下 epoll 编程

转载自 Linux epoll模型 ,这篇文章讲的非常详细! 定义: epoll是Linux内核为处理大批句柄而作改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著的减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率.因为它会复用文件描述符集合来传递结果而不是迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一个原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符

linux文件系统写过程简析

linux写入磁盘过程经历VFS ->  页缓存(page cache) -> 具体的文件系统(ext2/3/4.XFS.ReiserFS等) -> Block IO ->设备驱动 -> SCSI指令(或者其他指令),总体来说linux文件写入磁盘过程比较复杂 1.VFS(虚拟文件系统) Linux中采用了VFS的方式屏蔽了多个文件系统的差别, 当需要不同的设备或者其他文件系统时,采用挂载mount的方式访问其他设备或者其他文件系统(这里可以把文件系统理解为具体的设备).正是

/etc下主要配置文件简析

etc /etc/rcor/etc/rc.dor/etc/rc*.d 启动.或改变运行级时运行的scripts或scripts的目录. etc/rc.d/rc.sysinit 系统初始化相关,主要完成的任务 1.激活udev和selinux 2.根据etc/sysctl.conf文件,来设定内核参数 3.设定系统时钟 4.装载键盘映射 5.启用交换分区 6.设置主机名 7.根文件系统检测,并以读写方式重新挂载 8.激活RAID和LVM设备 9.启用磁盘配额 10.根据/etc/fstab,检查并

高性能网络服务器编程:为什么linux下epoll是最好,Netty要比NIO.2好?

基本的IO编程过程(包括网络IO和文件IO)是,打开文件描述符(windows是handler,java是stream或channel),多路捕获(Multiplexe,即select和poll和epoll)IO可读写的状态,而后可以读写的文件描述符进行IO读写,由于IO设备速度和CPU内存比速度会慢,为了更好的利用CPU和内存,会开多线程,每个线程读写一个文件描述符. 但C10K问题,让我们意识到在超大数量的网络连接下,机器设备和网络速度不再是瓶颈,瓶颈在于操作系统和IO应用程序的沟通协作的方

epoll原理简析

一.epoll在linux环境下的一种IO多路复用技术,可以非常高效的处理数以百万计的socket句柄,比起以前的select和poll效率高(当然,如果socket连接数不多,并且大多都是"活跃"的,epoll相对于select也就没有什么优势了) 二.原理解析: int epoll_create(int size); int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int epoll_wait(

Linux 系统故障排查思路简析

处理linux系统故障的思路:    1.重视错误提示信息.    2.查阅日志文件 .message (系统日志) 服务的日志 error(应用日志)    3.分析,定位问题.    4.解决问题.    忘记root密码?        重置:centos5或者6    启动加载grub引导菜单时 按esc 找到当前系统引导选项.        按方向键找到需要的系统引导内核,按 "e" 进入编辑状态.        然后通过方向键选中带有kernel指令的行 继续按"

Linux进程启动过程简析

朱宇轲 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 今天,我们将通过阅读linux的内核代码来对linux系统中进程的创建过程进行简单的分析. 大家都知道,linux通过进程控制块PCB来对进程进行控制和管理,它存放了进程的数据.在linux中,PCB的代码如下(当然是节选的==): struct task_struct { volatile long state;//进程状

Linux下df与du命令输出区别简析

PS:前些时间有童鞋问我,为什么他的服务器里用df和du命令查询的文件大小显示不一样.其实这两个命令查询原理是不一样的,简析如下: 1.正常情况下,df和du输出结果都会有差距 du -sh命令通过将指定文件系统中所有的目录.符号链接和文件使用的块数累加得到该文件系统使用的总块数: 而df命令通过查看文件系统磁盘块分配图得出总块数与剩余块数. 文件系统分配其中的一些磁盘块用来记录它自身的一些数据,如i节点,磁盘分布图,间接块,超级块等.这些数据对大多数用户级的程序来说是不可见的,通常称为Meta