epoll学习及实例

从linux源码看epoll及epoll实战揭秘 从linux源码看epoll 前言 在linux的高性能网络编程中,绕不开的就是epoll。和select、poll等系统调用相比,epoll在需要监视大量文件描述符并且其中只有少数活跃的时候,表现出无可比拟的优势。epoll能让内核记住所关注的描述符,并在对应的描述符事件就绪的时候,在epoll的就绪链表中添加这些就绪元素,并唤醒对应的epoll等待进程。 本文就是笔者在探究epoll源码过程中,对kernel将就绪描述符添加到epoll并唤醒对应进程的一次源码分析(基于linux-2.6.32内核版 阅读详情

         epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,是IO多路复用的一种技术。它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。

         在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。

相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。并且,在linux/posix_types.h头文件有这样的声明:
#define __FD_SETSIZE    1024
表示select最多同时监听1024个fd,当然,可以通过修改头文件再重编译内核来扩大这个数目,但这似乎并不治本。

epoll比select效率高的原因

epoll精巧的使用了3个方法来实现select方法要做的事:

新建epoll描述符==epoll_create()
epoll_ctrl(epoll描述符,添加或者删除所有待监控的连接)
返回的活跃连接 ==epoll_wait(epoll描述符)

而select只有一个接口做了这三件事:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

与select相比,epoll分清了频繁调用和不频繁调用的操作。

例如,epoll_ctl是不太频繁调用的,而epoll_wait是非常频繁调用的。
这时,epoll_wait却几乎没有入参,这比select的效率高出一大截,

而且,它也不会随着并发连接的增加使得入参越发多起来,导致内核执行效率下降。

1.数据结构

在select中采用轮询处理,其中的数据结构类似一个数组的数据结构,轮询时线性遍历。
而epoll是维护一个队列,直接看队列是不是空就可以了。


2.内核与用户空间的消息传递--内存拷贝

无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,就避免不了内存拷贝。
每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大;
而epoll只需要拷贝一次(epoll_ctl的add),epoll是通过内核与用户空间mmap同一块内存实现的。


3.遍历效率上

select每调用一次,就需要轮询fd集合中的所有描述符,而且不管socket是否活跃,都会遍历一遍。
epoll只会对"活跃"的socket进行操作---这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。
那么,只有"活跃"的socket才会主动的去调用callback函数(把这个句柄加入队列),空闲状态的描述符则不会。

但是如果绝大部分的I/O都是 “活跃的”,每个I/O端口使用率很高的话,epoll效率不一定比select高(可能是要维护队列复杂)。


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

2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:
EPOLL_CTL_ADD:注册新的fd到epfd中;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中删除一个fd;
第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事,struct epoll_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 events */
    epoll_data_t data; /* User data variable */
};

events可以是以下几个宏的集合:

EPOLLIN
              The associated file is available for read(2) operations.
EPOLLOUT
              The associated file is available for write(2) operations.
EPOLLRDHUP
              Stream  socket peer closed connection, or shut down writing half
              of connection.  (This flag is especially useful for writing sim-
              ple code to detect peer shutdown when using Edge Triggered moni-
              toring.)
EPOLLPRI
              There is urgent data available for read(2) operations.
EPOLLERR
              Error condition happened  on  the  associated  file  descriptor.
              epoll_wait(2)  will always wait for this event; it is not neces-
              sary to set it in events.
EPOLLHUP
              Hang  up   happened   on   the   associated   file   descriptor.
              epoll_wait(2)  will always wait for this event; it is not neces-
              sary to set it in events.
EPOLLET
              Sets  the  Edge  Triggered  behavior  for  the  associated  file
              descriptor.   The default behavior for epoll is Level Triggered.
              See epoll(7) for more detailed information about Edge and  Level
              Triggered event distribution architectures.
EPOLLONESHOT (since Linux 2.6.2)
              Sets  the  one-shot behavior for the associated file descriptor.
              This means that after an event is pulled out with  epoll_wait(2)
              the  associated  file  descriptor  is internally disabled and no
              other events will be reported by the epoll interface.  The  user
              must  call  epoll_ctl() with EPOLL_CTL_MOD to re-enable the file
              descriptor with a new event mask.


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

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


4、ET、LT两种工作模式
epoll 的事件触发模式两种:默认的 level-trigger 模式,以及通过 EPOLLET 启用的 edge-trigger 模式两种。

从 epoll 发展历史来看,它刚诞生时只有 edge-trigger 模式,后来因不易被开发者理解,又增加了 level-trigger 模式并作为默认处理方式。

二者的差异在于level-trigger模式下只要某个socket处于readable/writable状态,无论什么时候进行epoll_wait都会返回该socket;

而edge-trigger模式下只有某个socket从unreadable变为readable或从unwritable变为writable时,epoll_wait才会返回该socket。

从 kernel 代码来看,ET/LT模式的处理逻辑几乎完全相同,差别仅在于LT模式在event 发生时不会将fd从 ready list 中移除,略微增大了 event 处理过程中 kernel space 中记录数据的大小。

edge-trigger 模式的真正优势在于减少了每次 epoll_wait 可能需要返回的 fd 数量,在并发 event 数量极多的情况下能加快 epoll_wait 的处理速度。

然而,edge-trigger 模式一定要配合 ready list 结构,以便收集已出现 event 的 fd,再通过轮询方式挨个处理,以此避免通信数据量很大时,出现忙于处理热点 fd 而导致非热点 fd 饿死的现象。

edge-trigger 虽然能加快epoll_wait 的处理速度,但别忘了这只是针对 epoll 体系自己而言的提升,与此同时开发者 需要增加复杂的逻辑、花费更多的 cpu/mem 与其配合工作,总体性能收益究竟如何?只有实际测量才知道,无法一概而论。所以,为了降低处理逻辑复杂度,常用的事件处理库大部分都选择了 level-trigger 模式(如 libevent、boost::asio等)。

结论:
1. epoll 的 edge-trigger 和 level-trigger 模式处理逻辑差异极小,性能测试结果表明常规应用场景中二者性能差异可以忽略。
2. 使用 edge-trigger 的用户态开发比使用 level-trigger 的逻辑复杂,出错概率更高。
3. edge-trigger 和 level-trigger 的性能差异主要在于 epoll_wait 系统调用的处理速度,是否是 user app 的性能瓶颈需要视应用场景而定,不可一概而论。

5. 实例应用
那么究竟如何来使用epoll呢?其实非常简单。
通过在包含一个头文件#include <sys/epoll.h> 以及几个简单的API将可以大大的提高你的网络服务器的支持人数。

首先通过create_epoll(int maxfds)来创建一个epoll的句柄,其中maxfds为你epoll所支持的最大句柄数。这个函数会返回一个新的epoll句柄,之后的所有操作将通过这个句柄来进行操作。在用完之后,记得用close()来关闭这个创建出来的epoll句柄。

之后在你的网络主循环里面,每一帧的调用epoll_wait(int epfd, epoll_event events, int max events, int timeout)来查询所有的网络接口,看哪一个可以读,哪一个可以写了。基本的语法为:
nfds = epoll_wait(kdpfd, events, maxevents, -1);
其中kdpfd为用epoll_create创建之后的句柄,events是一个epoll_event*的指针,当epoll_wait这个函数操作成功之后,epoll_events里面将储存所有的读写事件。max_events是当前需要监听的所有socket句柄数。最后一个timeout是 epoll_wait的超时,为0的时候表示马上返回,为-1的时候表示一直等下去,直到有事件范围,为任意正整数的时候表示等这么长的时间,如果一直没有事件,则范围。一般如果网络主循环是单独的线程的话,可以用-1来等,这样可以保证一些效率,如果是和主逻辑在同一个线程的话,则可以用0来保证主循环的效率。

epoll_wait范围之后应该是一个循环,遍利所有的事件。

几乎所有的epoll程序都使用下面的框架:

for( ; ; )
    {
        nfds = epoll_wait(epfd,events,20,500);
        for(i=0;i<nfds;++i)
        {
            if(events[i].data.fd==listenfd) //有新的连接
            {
                connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen); //accept这个连接
                ev.data.fd=connfd;
                ev.events=EPOLLIN|EPOLLET;
                epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev); //将新的fd添加到epoll的监听队列中
            }
            else if( events[i].events&EPOLLIN ) //接收到数据,读socket
            {
                n = read(sockfd, line, MAXLINE)) < 0    //读
                ev.data.ptr = md;     //md为自定义类型,添加数据
                ev.events=EPOLLOUT|EPOLLET;
                epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);//修改标识符,等待下一个循环时发送数据,异步处理的精髓
            }
            else if(events[i].events&EPOLLOUT) //有数据待发送,写socket
            {
                struct myepoll_data* md = (myepoll_data*)events[i].data.ptr;    //取数据
                sockfd = md->fd;
                send( sockfd, md->ptr, strlen((char*)md->ptr), 0 );        //发送数据
                ev.data.fd=sockfd;
                ev.events=EPOLLIN|EPOLLET;
                epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); //修改标识符,等待下一个循环时接收数据
            }
            else
            {
                //其他的处理
            }
        }
    }

下面给出一个完整的服务器端例子,客户端直接连接到这个服务器就好了。

#include <iostream>
#include <cstdlib>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

using namespace std;

#define MAXLINE 80
#define LISTENQ 20
#define SERV_PORT 8000

int SetNonBlock(int iSock)
{
    int iFlags;

    iFlags = fcntl(iSock, F_GETFL, 0);
    iFlags |= O_NONBLOCK;
    iFlags |= O_NDELAY;
    int ret = fcntl(iSock, F_SETFL, iFlags);
    return ret;
}

int WriteNonBlock(int fd_, const char* send_buf, size_t send_len)
{
    int write_pos = 0;//发送位置
    int nLeft = send_len;//表示未发完的数据
    while(nLeft > 0)
    {
        int nWrite = write(fd_, send_buf + write_pos, nLeft);//已发完的数据长度
        if(nWrite == 0)//socket已关闭,发送失败
        {
            return -1;
        }
        if( nWrite < 0)
        {
            if(errno == EWOULDBLOCK || errno == EAGAIN)//没有空间可写数据
            {
                nWrite = 0;
            }else if(errno == EINTR){//连接正常,操作被中断,可继续发送
                continue;
            }
            else
            {
                return -1;
            }
        }
        else//发送成功
        {
            nLeft -= nWrite;
            write_pos += nWrite;
        }
    }
    return 0;
}

int ReadNonBlock(int fd_, char* recv_buf, size_t recv_len)
{
    while(1)
    {
        int ret = read(fd_, recv_buf, recv_len);
        if(ret > 0){//接收成功
            return 0;
        }
        if(ret == 0){//socket已关闭,发送失败
            return -1;
        }
        if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR){
            continue;
        }
    }
    return -1;
}

int main(int argc, char* argv[])
{
    int i, listenfd, connfd, epfd, nfds, portnumber=SERV_PORT;
    ssize_t n=-1;
    char line[MAXLINE];
    socklen_t clilen;
   
    struct sockaddr_in clientaddr;
    struct sockaddr_in serveraddr;
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if(listenfd <= 0){
        cout<<"create fd fail"<<endl;
        return -1;
    }
    SetNonBlock(listenfd);

    //声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件
    struct epoll_event ev,events[20];
    //生成用于处理accept的epoll专用的文件描述符
    epfd=epoll_create(256);
    //设置与要处理的事件相关的文件描述符
    ev.data.fd=listenfd;
    //设置要处理的事件类型
    ev.events=EPOLLIN|EPOLLET;
    //ev.events=EPOLLIN;

    //注册epoll事件
    epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);

    //listenfd绑定ip地址
    bzero(&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    char local_addr[20]="127.0.0.1";
    inet_aton(local_addr,&(serveraddr.sin_addr));
    serveraddr.sin_port=htons(portnumber);
    
    //bind和listen不是阻塞函数
    bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr));
    listen(listenfd, LISTENQ);

    cout << "server listening ..."  << endl;

    int ret = -1;

    for ( ; ; ) 
    {
        //等待epoll事件的发生
        nfds=epoll_wait(epfd,events,20,-1);

        //处理所发生的所有事件
        for(i=0;i<nfds;++i)
        {
            if(events[i].data.fd == listenfd)//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接
            {
                connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen);//以后读写都用这个返回的fd
                if(connfd < 0){
                    perror("connfd<0,accept fail");
                    exit(1);
                }
                SetNonBlock(connfd);//设置为非阻塞模式
                char *str = inet_ntoa(clientaddr.sin_addr);
                cout << "accapt a connection from " << str << endl;
                
                ev.data.fd=connfd;
                ev.events=EPOLLIN|EPOLLET;
                epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
            }
            else if(events[i].events & EPOLLIN)//如果是已经连接的用户,并且收到数据,那么进行读入
            {
                connfd = events[i].data.fd;

                ret = ReadNonBlock(connfd, line, MAXLINE);
                if(ret != 0){
                    cout<<"read line fail"<<endl;
                    close(connfd);
                    break;
                }
                cout << "receive:" << line << endl;

                ev.data.fd = connfd;
                ev.events = EPOLLOUT|EPOLLET;
                epoll_ctl(epfd,EPOLL_CTL_MOD, connfd, &ev);//修改标识符,等待下一个循环时发送数据,异步处理的精髓
            }
            else if(events[i].events & EPOLLOUT) // 如果有数据发送
            {
                connfd = events[i].data.fd;

                //将从client接收到的字母转化为大写,回送给client
                for (int i = 0; i < n; i++)
               		line[i] = toupper(line[i]);

                ret = WriteNonBlock(connfd, line, MAXLINE);
                if(ret != 0){
                    cout<<"send line fail"<<endl;
                    close(connfd);
                    break;
                }
                cout << "send:" << line << endl;

                ev.data.fd = connfd;
                ev.events = EPOLLIN|EPOLLET;
                epoll_ctl(epfd,EPOLL_CTL_MOD, connfd, &ev);
            }
        }
    }
    return 0;
}

注意:epoll在非阻塞模式下,才能发挥最大性能,所以这里的读写操作,都是非阻塞的方式。

Linux epoll 机制的核心控制函数——`epoll_ctl` epoll_ctl是Linux epoll机制的核心函数,用于动态管理epoll实例监控的文件描述符集合。它支持三种操作:EPOLL_CTL_ADD添加监听、EPOLL_CTL_MOD修改事件类型、EPOLL_CTL_DEL移除监控。使用时需指定epoll实例描述符、操作类型、目标文件描述符及事件配置结构体指针。函数成功返回0,失败返回-1并设置errno。典型应用包括:服务器启动时添加监听socket,连接建立后修改客户端socket事件类型,连接关闭时移除描述符。需注意操作顺序、错误处理及边缘触发模式的 阅读详情

相关推荐

epoll示例

其中最后一个成员 sin_zero 是一个填充字段,其目的是为了保证 struct sockaddr_in 结构体的总大小和 struct sockaddr 结构体的大小相同,因为在socket API中,地址通常是通过 struct sockaddr 类型来传递的。在许多实现中,这个填充可能并不是严格必要的,因为sockaddr_in和sockaddr的转换通常都能正常工作,但按照好的编程习惯,仍然建议对这部分内存进行清零处理。想要在一个进程中管理多个到同一个服务器的连接,不需要为每个连接创建新的进程。

数字人生 1746

epoll学习:epoll_ctl函数详解

epoll_ctl函数:向文件描述符epfd引用的epoll实例执行控制操作(op:添加、修改或者删除)。

个人工作中学到的专业技术与职业发展知识笔记 9591

告别电源纹波!手把手教你用UCC28019设计一个高效率PFC模块(附完整原理图与BOM清单)

本文详细介绍了如何使用UCC28019设计高效率PFC模块,解决电源纹波问题。通过优化补偿网络、关键元器件选型及PCB布局,实现功率因数提升至0.99以上,降低输入电流谐波。附完整原理图与BOM清单,适用于电赛等快速开发场景。

weixin_42652674的博客 335

epoll详解_Linux下的I/O复用与epoll详解

前言 I/O多路复用有很多种实现。在linux上,2.4内核前主要是select和poll,自Linux 2.6内核正式引入epoll以来,epoll已经成为了目前实现高性能网络服务器的必备技术。尽管他们的使用方法不尽相同,但是本质上却没有什么区别。本文将重点探讨将放在EPOLL的实现与使用详解。参考资料:嵌入式网络编程 - linux下的IO模型嵌入式应用层开发 - Linux开发必备...

weixin_39874881的博客 733

Unity WebGL发布避坑指南:从内存设置到字体打包,一次搞定所有常见问题

本文详细解析Unity WebGL发布的完整流程,从内存设置到字体打包,提供全面的避坑指南。涵盖关键检查清单、内存与性能优化配置、资源处理专项优化及平台适配技巧,帮助开发者高效解决常见问题,确保项目顺利发布。

weixin_28693309的博客 147

Revit获取族预览图

最近做到和族库相关的功能需要获取到族文件的预览图,在RevitAPI中提供了ElementType.GetPreviewImage方法,但是前提就是你是在Revit里面操作的,并且已经拿到了Symbol对象实例,才可以调用此方法。然后在网上找了另外一种方式,通过读取Revit文件,在REVITPREVIEW4.0字段截取文件信息,将其转换为图片格式。 一、使用RevitAPI中ElementType.GetPreviewImage方法 [Autodesk.Revit.Attributes.Transac.

weixin_44631419的博客 1756

epoll 和ractor模型学习

epoll和reactor模型的学习

weixin_57011178的博客 731

手把手式介绍 ADNI 影像数据下载

1.搜索 1.1登录 打开ADNI的官网:http://adni.loni.usc.edu/,进入下面的界面。 点击 DATA&SMPLES 下的 ACCESS DATA AND SAMPLES,如下图。 页面跳转以后点击中间的“LOGIN”,这时会跳出一个新的界面,然后在右上角输入自己的帐号、密码即可。如果没有权限可以跳转到第2部分看看如何申请权限。 登录以后选择DOWNLOAD下的 Image Collections就是搜索下载影像数据,其他还有一些学习测试数据和遗传数据。 1.2 sea

永远想不起来更新的博客 1万+

epoll学习总结

​​epoll 是 Linux 系统下一种高性能的 I/O 事件通知机制​​,它能让一个程序高效地同时监控成千上万个网络连接(或其他文件描述符),看看哪些已经准备好了进行读取或写入操作。​初始化与注册 (Setup)​​:应用程序首先调用创建一个 epoll 实例。内核会为其初始化上述的红黑树和就绪链表。对于每个需要监控的网络连接(fd),程序调用将其添加到红黑树中进行管理。在这个过程中,内核会为每个 fd 预先设置好一个回调函数​ (​等待事件 (Waiting)​​:应用程序调用epoll_wait。

2501_93311146的博客 1144

电力抄表协议DL/T645和698.45到底有啥区别?一张图帮你选对协议

本文深入对比了电力抄表协议DL/T645和DL/T698.45的技术差异,从协议架构、报文结构、功能覆盖到实施成本进行全面分析。通过实际案例和数据,揭示698.45协议在通信效率、业务扩展性和长期成本上的优势,并提供清晰的选型决策流程图,帮助工程师根据具体需求选择最适合的电力抄表协议。

weixin_38168696的博客 401

深入解析FDTD Script中的farfieldpolar3d命令:复电场计算与应用

本文深入解析了FDTD仿真中`farfieldpolar3d`命令的核心功能与应用。作为`farfield3d`的复数版本,该命令能计算并返回球坐标系下的复电场矢量,完整保留振幅与相位信息,为光学天线设计、近远场变换及偏振态分析等高级应用提供了关键数据基础。

grafana6viz的博客 286

网络编程epoll学习

epoll使用的基本步骤。

GHL284271090的博客 421

Chrome下载B站视频字幕的插件

本文主要介绍了Chrome下载B站视频字幕的插件,希望能对使用Chrome的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案

weixin_43178406的博客 3万+

epoll学习之路

        Socket 服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接...

蜜汁程序员的博客 719

【Proteus仿真】AT89C51单片机中断系列仿真——INT0中断控制LED小灯/INT0和INT1中断控制数码管

本文介绍了基于51单片机外部中断(INT0/INT1)的四个实践案例:1)INT0控制LED开关;2)INT0中断实现数码管计数/清零;3)INT0中断构建秒表功能(启动/暂停/清零);4)INT0/INT1双中断独立控制两组数码管计数。每个案例包含电路原理图、接线说明及完整源代码,重点演示了外部中断在硬件控制中的应用方法。附加硬件工程师笔试面试资料链接,涵盖器件知识、Multisim教程等内容,适合嵌入式学习者参考实践。(149字)

XU157303764的博客 1911

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

学习epoll

http://blog.youkuaiyun.com/sparkliang/article/details/4770655 Connection closed by foreign host的原因

天恒の永恒 527

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

EPOLL学习

一 基本知识   epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。   epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下

风之翊的专栏 421

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Epoll技术深入学习

目录 Epoll技术深入学习 一, Epoll概述 二,Epoll的工作原理(三个函数) 三,实际使用 四,LT和ET Epoll技术深入学习 一, Epoll概述 epoll就是一种典型的IO多路复用,支持高并发,号称一...

aihuiju2691的博客 219

epoll实现相关学习笔记

epoll实现相关知识

qq_42120843的博客 384

epoll 学习

名词解释:man epoll之后,得到如下结果:NAME       epoll - I/O event notification facilitySYNOPSIS       #include DESCRIPTION       epoll is a variant of poll(2) that can be used either as Edge or Level       T

永远在跟随,从未去超越 857

学习epoll模型

一直对网络编程特别感兴趣,也会每天看点UNP.

小马驹的博客 196

epoll源码,自己学习用

cgit logo index : kernel/git/torvalds/linux.git Linux kernel source tree Linus Torvalds aboutsummaryrefslogtreecommitdiffstats path: root/fs/eventpoll.c blob: 12eebcdea9c8a2a39c9dbf079dd41d8e67648fec (plain) // SPDX-License-Identifier: GPL-2.0-or-late

AdrianAndroid的专栏 547

学习完Linux epoll,感觉Linux很强大

学习完Linux epoll,感觉Linux很强大

张人杰的博客 329

epoll边缘模式收数据学习

下面问题你会怎么回答?1、epoll边缘模式收数据,如何表明数据收完了?收完的标志是什么?2、epoll边缘模式收数据代码怎么写。

GHL284271090的博客 284

网络编程-epoll学习

难点就在结构体的void*arg,它就是本身这个结构体本身。

chocochato的博客 1291
上一篇: linux下获取本机ip代码和判断ip
下一篇: Linux进程间通信方式
okiwilldoit
博客等级 码龄17年 86粉丝 283原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值