Unix-epoll

本人不才,只能从表面理解epoll函数的机制,看了很多博客,由于缺乏基础知识,所以对内核中的实现和其数据结构理解不到位,粗浅地来认识一下。

系统打开的最大文件描述符 也是有限制的,并且这个最大量和内存有关

cat /proc/sys/fs/file-max
194720
 
int  epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);\\添加套接字
typedef union epoll_data{
void    *ptr;
int        fd;
_uint32_t u32;
_uint64_t u64;
}epoll_data_t;
struct epoll_event{
 _uint32_t events;
 epoll_data_t data;\\epoll高效的原因
};

.........................

看一下select低效的原因int nready=select(maxfd+1,&rset,NULL,NULL,NULL);//select的实现其实是对maxfd+1个描述符全部进行遍历,没发生事件的位被清除并且同时将发生事件描述符拷贝至rset。全部遍历maxfd+1检测到有I\O事件后,将发生事件的个数返回给nready,从这里可以看出,select经历了一次遍历和拷贝过程。时间复杂度为O(n)

同理poll也一样是O(n).

接着看epoll:

epoll_create(int size)//这个函数是创建一个哈希表,size表示哈希表的容量
epoll_create1(int flags)//这个是实现了一棵红黑树,不需要指定容量

#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include<iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include<sys/select.h>
#include<errno.h>
#include<sys/poll.h>
#include<vector>
#include<algorithm>
#include<sys/signal.h>
#include<fcntl.h>
#include<sys/epoll.h>
using namespace std;
typedef std::vector<struct epoll_event> EventList;
#define ERR_EXIT(m)     do    {        perror(m);        exit(EXIT_FAILURE);    }while(0)
void activate_nonblock(int fd){
    int ret;
    int flags=fcntl(fd,F_GETFL);
    if(flags==-1)
        ERR_EXIT("FCNTL");
    flags|=O_NONBLOCK;
    ret=fcntl(fd,F_SETFL,flags);
    if(ret==-1)
        ERR_EXIT("fcntl");
}
int main()
{   signal(SIGPIPE,SIG_IGN);
    //signal(SIGPIPE,handler);
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1)
    {
        perror("socket() err");
        return -1;
    }
    int on = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
    {
        perror("setsockopt() err");
        return -1;
    }
    int conn;
    vector<int> client;
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8888);
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    if (bind(sockfd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
    {
        perror("bind() err");
        return -1;
    }
    if (listen(sockfd, SOMAXCONN) == -1)
    {
        perror("bind() err");
        return -1;
    }
    struct sockaddr_in peeraddr;
   int nready;
   int epollfd;
   struct epoll_event event;
   event.data.fd=sockfd;
   event.events=EPOLLIN|EPOLLET;
   epollfd=epoll_create1(EPOLL_CLOEXEC);
   epoll_ctl(epollfd,EPOLL_CTL_ADD,sockfd,&event);//将sockfd和event加入epollfd进行管理
   EventList Events(16);
   int count=0;
   while(1){
       int i;socklen_t peerlen;
       nready=epoll_wait(epollfd,&*Events.begin(),static_cast<int>(Events.size()),-1);
       if((size_t)nready==Events.size())
           Events.resize(Events.size()*2);
       for( i=0;i<nready;i++){
           if(Events[i].data.fd==sockfd){
               peerlen=sizeof(peeraddr);
               conn=accept(sockfd,(struct sockaddr *)&peeraddr,&peerlen);
               printf("ip=%s port=%d\n",inet_ntoa(peeraddr.sin_addr),ntohs(peeraddr.sin_port));
               printf("count=%d\n",++count);
               client.push_back(conn);
               activate_nonblock(conn);
               event.data.fd=conn;
            event.events=EPOLLIN|EPOLLET;
               epoll_ctl(epollfd,EPOLL_CTL_ADD,conn,&event);
           }
           else if(Events[i].events&EPOLLIN){
               conn=Events[i].data.fd;
               if(conn<0)
                   continue;
            char recvbuf[1024]={0};
            int ret=read(conn,recvbuf,1024);
            if(ret==-1)
                ERR_EXIT("read");
            if(ret==0){
                printf("client close\n");
                close(conn);
                event=Events[i];
                epoll_ctl(epollfd,EPOLL_CTL_DEL,conn,&event);
                client.erase(std::remove(client.begin(),client.end(),conn),client.end());

            }
            fputs(recvbuf,stdout);
            write(conn,recvbuf,strlen(recvbuf));
           }
       }

   }
}

epoll_serverepoll

epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了O(1))

通过epoll_ctl函数添加进来的事件都会被放在红黑树的某个节点内,所以,重复添加是没有用的。当把事件添加进来的时候时候会完成关键的一步,那就是该事件都会与相应的设备(网卡)驱动程序建立回调关系,当相应的事件发生后,就会调用这个回调函数,该回调函数在内核中被称为:ep_poll_callback,这个回调函数其实就所把这个事件添加到rdllist这个双向链表中。一旦有事件发生,epoll就会将该事件添加到双向链表中。那么当我们调用epoll_wait时,epoll_wait只需要检查rdlist双向链表中是否有存在注册的事件,效率非常可观。这里也需要将发生了的事件复制到用户态内存中即可。

也就是说我们得到epoll_wait返回准备好的时间并不需要遍历完整个描述符。这和底层实现有关,由于能力不足,只能理解到这。以后再慢慢补充

原文地址:https://www.cnblogs.com/illfuckingkyzb/p/10152623.html

时间: 2024-10-29 19:59:54

Unix-epoll的相关文章

apache分析之二、post-commit后台运行

一.问题描述希望在svn的post-commit中执行一个后台任务,但是发现该后台任务没有退出之前,svn提交始终不会返回.按照bash派生的后台任务的定义,就是在子进程派生之后,父进程不会把终端输入(终端的前台任务)派发给后台任务,也不会同步等该该子进程的返回.在父shell退出之后,内核会把退出进程的父进程设置为系统的始祖进程,也就是1号任务.此时svn已经不再是post-commit的父进程,所以它无权wait同步等待post-commit的退出,所以它一定使用了其它的同步机制.二.内核中

PythonNET网络编程3

IO IO input output 在内存中存在数据交换的操作都可以认为是IO操作 和终端交互 : input print 和磁盘交互 : read write 和网络交互 : recv send IO密集型程序:在程序执行过程中存在大量IO操作,而cpu运算操作较少,消耗cpu较少,运行效率较低 计算密集型程序(CUP密集型程序):在程序执行中CPU运算较多,IO操作相对较少,消耗CPU大,运行速度快 IO分类 阻塞IO 非阻塞IO IO多路复用 阻塞IO 阻塞IO是IO的默认形态,是效率较

IO并发

IO分类 IO分类:阻塞IO,非阻塞IO,IO多路复用,异步IO等 阻塞IO 定义:在执行IO操作时如果执行条件不满足则阻塞.阻塞IO是IO的默认形态. 效率:阻塞IO是效率很低的一种IO.但是由于逻辑简单所以是默认IO行为. 阻塞情况: 因为某种执行条件没有满足造成的函数阻塞 如:accept        input        recv等 处理IO的时间较长产生的阻塞状态 如:网络传输,大文件读写等 非阻塞IO 定义:通过修改IO属性行为,使原本阻塞的IO变为非阻塞的状态 设置套接字为非

操作系统常见面试知识点

1.请分别简单说一说进程和线程以及它们的区别. 进程是具有一定功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源调度和分配的一个独立单位. 线程是进程的实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位. 一个进程可以有多个线程,多个线程也可以并发执行 2.线程同步的方式有哪些 互斥量:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限.因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问. 信号量:它允许同一时刻多个线程访问同一资源,但

【UNIX】select、poll、epoll学习

三者都是UNIX下多路复用的内核接口,select是跨平台的接口,poll是systemV标准,epoll是linux专有的接口,基于poll改造而成. select 函数原型: int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);  select通过系统调用监视多个文件描述符的数组,select返回后,就绪的文件描述符会被修改标志位,使得进程可以获得准

UNIX网络编程:I/O复用技术(select、poll、epoll)

Unix下可用的I/O模型一共有五种:阻塞I/O .非阻塞I/O .I/O复用 .信号驱动I/O .异步I/O.此处我们主要介绍第三种I/O符复用. I/O复用的功能:如果一个或多个I/O条件满足(输入已准备好读,或者描述字可以承接更多输出)时,我们就被通知到.这就是有select.poll.epoll实现. I/O复用应用场合: 1.当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用.在这前一段中已做描述. 2.一个客户同时处理多个套接口是可能的,但很少出现. 3.如果

UNIX网络编程之epoll的 accept , read , write

本文转载自:http://www.it165.net/os/html/201308/5868.html 非阻塞模式下的网络编程,非阻塞模式常常需要不停地进行轮询,大量耗费CPU资源,这种方式并不可取. 在一个非阻塞的socket上调用read/write函数,返回EAGAIN或者EWOULDBLOCK(注:EAGAIN就是EWOULDBLOCK). 从字面上看,意思是: EAGAIN: 再试一次 EWOULDBLOCK:如果这是一个阻塞socket, 操作将被block perror输出:Res

【Unix环境编程】select、poll、epoll机制的联系与区别

在linux设计并发网络程序,主要有如下几种模型:Apache模型(Process Per Connection, PPC).TPC(Thread Per Connection)模型,select机制.poll机制以及epoll. 1.  PPC/TPC模型 这两种模型思想类似,每一个新的链接就用一个线程或者进程处理.PPC使用使用进程.TPC使用线程.缺点是连接多了以后,这么多进程和线程的切换非常大.因此这类模型的能接受的最大连接数不会太高,一般几百个左右. 2.  select机制 PPC/

linux/unix网络编程之epoll

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

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

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