zoukankan      html  css  js  c++  java
  • 关于EPoll的个人理解

    1.epoll 是I/o多路复用的一种解决方案,对比select的优点有:

      a.支持打开最大的文件描述符(可高达百万)

      b.效率并不随着描述符的增多而线性下降。select每次是轮询,所以描述符越多效率越低。epoll的好处是利用事件触发,内核通过回调函数帮他(这是亲儿子)。

      c.采用了mmap内存映射,减少内核区到用户区数据拷贝,又节省了不少时间。

    2.Epoll又分为水平触发和边缘触发。

      Level_triggered(水平触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait()时,它还会通知你在上没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你!!!如果系统中有大量你不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率!!!

      Edge_triggered(边缘触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符!!!

      优缺点就很明显了,如过访问量过大,切每个事件的内容又很多的话,推荐边缘触发,因为每个事件的读取时要花长时间的,用边缘触发能明显减少事件的触发次数。

    3.瓶颈。网上看的,不过倒是和我们的游戏服务器的限制想吻合。

      单线程epoll,触发量可达到15000,但是加上业务后,因为大多数业务都与数据库打交道,所以就会存在阻塞的情况,这个时候就必须用多线程来提速。

    代码实现:从服务端分析,什么时候该监听读,什么时候该监听写?(面试被问到过。。。)。从服务器的角度,客户端发送的请求都是需要读取的,所以是读事件,而服务器的返回数据是写事件。正常情况下应该是先读后写,有请求才返回嘛。写完之后这个描述符应该再切换回读事件,监听下次的请求。而且读是个被动事件,写是服务器的主动事件。所以网上大部分代码的实现都是默认监听读,服务器返回数据时将描述符置为写,写完后在还原读。

            if(events[i].data.fd==listenfd)
                  {
    
                      connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen);
                      if(connfd<0){
                          perror("connfd<0");
                          exit(1);
                      }
                      setnonblocking(connfd);
    
                      char *str = inet_ntoa(clientaddr.sin_addr);
                        //std::cout<<"connec_ from >>"<
                        //设置用于读操作的文件描述符
    
                      ev.data.fd=connfd;
                        //设置用于注测的读操作事件
    
                      ev.events=EPOLLIN|EPOLLET;
                        //注册ev
    
                      epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
                }
                else if(events[i].events&EPOLLIN)
                {
                        //printf("reading!/n");
    
                        if ( (sockfd = events[i].data.fd) < 0) continue;
                        new_task=new task();
                        new_task->fd=sockfd;
                        new_task->next=NULL;
                        //添加新的读任务
    
                        pthread_mutex_lock(&mutex);
                        if(readhead==NULL)
                        {
                          readhead=new_task;
                          readtail=new_task;
                        }
                        else
                        {
                         readtail->next=new_task;
                          readtail=new_task;
                        }
                       //唤醒所有等待cond1条件的线程
    
                        pthread_cond_broadcast(&cond1);
                        pthread_mutex_unlock(&mutex);
                  }
                   else if(events[i].events&EPOLLOUT)
                   {

                rdata=(struct user_data *)events[i].data.ptr;
                sockfd = rdata->fd;
                write(sockfd, rdata->line, rdata->n_size);
                delete rdata;
                //设置用于读操作的文件描述符
                ev.data.fd=sockfd;
                //设置用于注测的读操作事件
                ev.events=EPOLLIN|EPOLLET;
                //修改sockfd上要处理的事件为EPOLIN
                epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);

              }

     
  • 相关阅读:
    同步ajax请求
    Thinking in Java——笔记(11)
    Thinking in Java——笔记(10)
    Thinking in Java——笔记(9)
    Thinking in Java——笔记(8)
    Thinking in Java——笔记(7)
    Thinking in Java——笔记(6)
    Thinking in Java——笔记(5)
    Thinking in Java——笔记(4)
    Thinking in Java——笔记(3)
  • 原文地址:https://www.cnblogs.com/zendu/p/8257875.html
Copyright © 2011-2022 走看看