深入理解 Linux 的 epoll 机制

poll为什么使用poll_list链表结构而不是数组 - 深入内核源码分析 动态扩展性:链表可以根据实际需要动态分配和释放内存,避免了固定大小数组带来的内存浪费或不足问题。高效的插入和删除操作:链表在插入和删除节点时具有常数时间复杂度,而数组需要移动大量元素,导致性能下降。优化内存管理:页对齐的内存分配可以简化内存管理操作,提高内存管理的效率。 阅读详情

转自:深入理解 Linux 的 epoll 机制 (qq.com)

图片

在 Linux 系统之中有一个核心武器:epoll 池,在高并发的,高吞吐的 IO 系统中常常见到 epoll 的身影。

IO 多路复用

在 Go 里最核心的是 Goroutine ,也就是所谓的协程,协程最妙的一个实现就是异步的代码长的跟同步代码一样。比如在 Go 中,网络 IO 的 read,write 看似都是同步代码,其实底下都是异步调用,一般流程是:

write ( /* IO 参数 */ )
    请求入队
    等待完成
    
 后台 loop 程序
    发送网络请求
    唤醒业务方

Go 配合协程在网络 IO 上实现了异步流程的代码同步化。核心就是用 epoll 池来管理网络 fd 。

实现形式上,后台的程序只需要 1 个就可以负责管理多个 fd 句柄,负责应对所有的业务方的 IO 请求。这种一对多的 IO 模式我们就叫做 IO 多路复用。

多路是指?多个业务方(句柄)并发下来的 IO 。

复用是指?复用这一个后台处理程序。

站在 IO 系统设计人员的角度,业务方咱们没办法提要求,因为业务是上帝,只有你服从的份,他们要创建多个 fd,那么你就需要负责这些 fd 的处理,并且最好还要并发起来。

没办法对业务方提要求,那么只能要求后台 loop 程序了!

要求什么呢?快!快!快!这就是最核心的要求,处理一定要快,要给每一个 fd 通道最快的感受,要让每一个 fd 觉得,你只在给他一个人跑腿。

那有人又问了,那我一个 IO 请求(比如 write )对应一个线程来处理,这样所有的 IO 不都并发了吗?是可以,但是有瓶颈,线程数一旦多了,性能是反倒会差的。

这里不再对比多线程和 IO 多路复用实现高并发之间的区别,详细的可以去了解下 nginx 和 redis 高并发的秘密。

  1   最朴实的实现方式?

我不用任何其他系统调用,能否实现 IO 多路复用?

可以的。那么写个 for 循环,每次都尝试 IO 一下,读/写到了就处理,读/写不到就 sleep 下。这样我们不就实现了 1 对多的 IO 多路复用嘛。

while True:
    for each 句柄数组 {
        read/write(fd, /* 参数 */)
    }
    sleep(1s)

慢着,有个问题,上面的程序可能会被卡死在第三行,使得整个系统不得运行,为什么?

默认情况下,我们 create 出的句柄是阻塞类型的。我们读数据的时候,如果数据还没准备好,是会需要等待的,当我们写数据的时候,如果还没准备好,默认也会卡住等待。所以,在上面伪代码第三行是可能被直接卡死,而导致整个线程都得不到运行。

举个例子,现在有 11,12,13 这 3 个句柄,现在 11 读写都没有准备好,只要 read/write(11, /*参数*/) 就会被卡住,但 12,13 这两个句柄都准备好了,那遍历句柄数组 11,12,13 的时候就会卡死在前面,后面 12,13 则得不到运行。这不符合我们的预期,因为我们 IO 多路复用的 loop 线程是公共服务,不能因为一个 fd 就直接瘫痪。

那这个问题怎么解决?

只需要把 fd 都设置成非阻塞模式。这样 read/write 的时候,如果数据没准备好,返回 EAGIN 的错误即可,不会卡住线程,从而整个系统就运转起来了。比如上面句柄 11 还未就绪,那么 read/write(11, /*参数*/) 不会阻塞,只会报个 EAGIN 的错误,这种错误需要特殊处理,然后 loop 线程可以继续执行 12,13 的读写。

以上就是最朴实的 IO 多路复用的实现了。但好像在生产环境没见过这种 IO 多路复用的实现?为什么?

因为还不够高级。for 循环每次要定期 sleep 1s,这个会导致吞吐能力极差,因为很可能在刚好要 sleep 的时候,所有的 fd 都准备好 IO 数据,而这个时候却要硬生生的等待 1s,可想而知。。。

那有同学又要质疑了,那 for 循环里面就不 sleep 嘛,这样不就能及时处理了吗?

及时是及时了,但是 CPU 估计要跑飞了。不加 sleep ,那在没有 fd 需要处理的时候,估计 CPU 都要跑到 100% 了。这个也是无法接受的。

纠结了,那 sleep 吞吐不行,不 sleep 浪费 cpu,怎么办?

这种情况用户态很难有所作为,只能求助内核来提供机制来协助。因为内核才能及时的管理这些事件的通知和调度。

我们再梳理下 IO 多路复用的需求和原理。IO 多路复用就是 1 个线程处理 多个 fd 的模式。我们的要求是:这个 “1” 就要尽可能的快,避免一切无效工作,要把所有的时间都用在处理句柄的 IO 上,不能有任何空转,sleep 的时间浪费。

有没有一种工具,我们把一箩筐的 fd 放到里面,只要有一个 fd 能够读写数据,后台 loop 线程就要立马唤醒,全部马力跑起来。其他时间要把 cpu 让出去。

能做到吗?能,但这种需求只能内核提供机制满足你。

 2   这事 Linux 内核必须要给个说法?

是的,想要不用 sleep 这种辣眼睛的实现,Linux 内核必须出手了,毕竟 IO 的处理都是内核之中,数据好没好内核最清楚。

内核一口气提供了 3 种工具 select,poll,epoll 。

为什么有 3 种?

历史不断改进,矬 -> 较矬 -> 卧槽、高效 的演变而已。

图片

 

Linux 还有其他方式可以实现 IO 多路复用吗?

好像没有了!

这 3 种到底是做啥的?

这 3 种都能够管理 fd 的可读可写事件,在所有 fd 不可读不可写无所事事的时候,可以阻塞线程,切走 cpu 。fd 有情况的时候,都要线程能够要能被唤醒。

而这三种方式以 epoll 池的效率最高。为什么效率最高?

其实很简单,这里不详说,其实无非就是 epoll 做的无用功最少,select 和 poll 或多或少都要多余的拷贝,盲猜(遍历才知道)fd ,所以效率自然就低了。

举个例子,以 select 和 epoll 来对比举例,池子里管理了 1024 个句柄,loop 线程被唤醒的时候,select 都是蒙的,都不知道这 1024 个 fd 里谁 IO 准备好了。这种情况怎么办?只能遍历这 1024 个 fd ,一个个测试。假如只有一个句柄准备好了,那相当于做了 1 千多倍的无效功。

epoll 则不同,从 epoll_wait 醒来的时候就能精确的拿到就绪的 fd 数组,不需要任何测试,拿到的就是要处理的。

epoll 池原理

下面我们看一下 epoll 池的使用和原理。

 1   epoll 涉及的系统调用

epoll 的使用非常简单,只有下面 3 个系统调用。

epoll_create
epollctl
epollwait

 就这?是的,就这么简单。

  • epollcreate 负责创建一个池子,一个监控和管理句柄 fd 的池子;

  • epollctl 负责管理这个池子里的 fd 增、删、改;

  • epollwait 就是负责打盹的,让出 CPU 调度,但是只要有“事”,立马会从这里唤醒。

  2   epoll 高效的原理

Linux 下,epoll 一直被吹爆,作为高并发 IO 实现的秘密武器。其中原理其实非常朴实:epoll 的实现几乎没有做任何无效功。 我们从使用的角度切入来一步步分析下。

首先,epoll 的第一步是创建一个池子。这个使用 epoll_create 来做:

原型:

int epoll_create(int size);

示例:

epollfd = epoll_create(1024);
if (epollfd == -1) {
    perror("epoll_create");
    exit(EXIT_FAILURE);
}

这个池子对我们来说是黑盒,这个黑盒是用来装 fd 的,我们暂不纠结其中细节。我们拿到了一个 epollfd ,这个 epollfd 就能唯一代表这个 epoll 池。注意,这里又有一个细节:用户可以创建多个 epoll 池。

然后,我们就要往这个 epoll 池里放 fd 了,这就要用到 epoll_ctl 了

原型:

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

示例:

if (epoll_ctl(epollfd, EPOLL_CTL_ADD, 11, &ev) == -1) {
    perror("epoll_ctl: listen_sock");
    exit(EXIT_FAILURE);
}

上面,我们就把句柄 11 放到这个池子里了,op(EPOLL_CTL_ADD)表明操作是增加、修改、删除中的增加,event 结构体可以指定监听事件类型,可读、可写。

第一个跟高效相关的问题来了,添加 fd 进池子也就算了,如果是修改、删除呢?怎么做到快速?

这里就涉及到你用什么数据结构来管理 fd 了。

最常见的思路:用 list ,可以吗?功能上可以,但是性能上拉垮。list 的结构来管理元素,时间复杂度都太高 O(n),每次要一次次遍历链表才能找到位置。池子越大,性能会越慢。

那有简单高效的数据结构吗?

有,红黑树。Linux 内核对于 epoll 池的内部实现就是用红黑树的结构体来管理这些注册进来的句柄 fd。红黑树是一种平衡二叉树,时间复杂度为 O(log n),就算这个池子不断的增删改,也能保持非常稳定的查找性能。

现在思考第二个高效的秘密:怎么才能保证数据准备好之后,立马感知呢?

epoll_ctl 这里会涉及到这一点。秘密就是:回调的设置。在 epoll_ctl 的内部实现中,除了把句柄结构用红黑树管理,另一个核心步骤就是设置 poll 回调。

思考来了:poll 回调是什么?怎么设置?

先说说 file_operations->poll 是什么?

在 文件描述符 fd 究竟是什么 说过,Linux 设计成一切皆是文件的架构,这个不是说说而已,而是随处可见。实现一个文件系统的时候,就要实现这个文件调用,这个结构体用 struct file_operations 来表示。这个结构体有非常多的函数,精简了一些,如下:

struct file_operations {
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    __poll_t (*poll) (struct file *, struct poll_table_struct *);
    int (*open) (struct inode *, struct file *);
    int (*fsync) (struct file *, loff_t, loff_t, int datasync);
    // ....
};

你看到了 read,write,open,fsync,poll 等等,这些都是对文件的定制处理操作,对于文件的操作其实都是在这个框架内实现逻辑而已,比如 ext2 如果有对 read/write 做定制化,那么就会是 ext2_read,ext2_write,ext4 就会是 ext4_read,ext4_write。在 open 具体“文件”的时候会赋值对应文件系统的 file_operations 给到 file 结构体。

那我们很容易知道 read 是文件系统定制 fd 读的行为调用,write 是文件系统定制 fd 写的行为调用,file_operations->poll 呢?

这个是定制监听事件的机制实现。通过 poll 机制让上层能直接告诉底层,我这个 fd 一旦读写就绪了,请底层硬件(比如网卡)回调的时候自动把这个 fd 相关的结构体放到指定队列中,并且唤醒操作系统。

举个例子:网卡收发包其实走的异步流程,操作系统把数据丢到一个指定地点,网卡不断的从这个指定地点掏数据处理。请求响应通过中断回调来处理,中断一般拆分成两部分:硬中断和软中断。poll 函数就是把这个软中断回来的路上再加点料,只要读写事件触发的时候,就会立马通知到上层,采用这种事件通知的形式就能使浪费的时间窗完全消失了。

划重点:这个 poll 事件回调机制则是 epoll 池高效最核心原理。

划重点:epoll 池管理的句柄只能是支持了 file_operations->poll 的文件 fd。换句话说,如果一个“文件”所在的文件系统没有实现 poll 接口,那么就用不了 epoll 机制。

第二个问题:poll 怎么设置?

在 epoll_ctl 下来的实现中,有一步是调用 vfs_poll 这个里面就会有个判断,如果 fd 所在的文件系统的 file_operations 实现了 poll ,那么就会直接调用,如果没有,那么就会报告响应的错误码。

static inline __poll_t vfs_poll(struct file *file, struct poll_table_struct *pt)
{
    if (unlikely(!file->f_op->poll))
        return DEFAULT_POLLMASK;
    return file->f_op->poll(file, pt);
}

你肯定好奇 poll 调用里面究竟是实现了什么?

总结概括来说:挂了个钩子,设置了唤醒的回调路径。epoll 跟底层对接的回调函数是:ep_poll_callback,这个函数其实很简单,做两件事情:

  1. 把事件就绪的 fd 对应的结构体放到一个特定的队列(就绪队列,ready list);

  2. 唤醒 epoll ,活来啦!

当 fd 满足可读可写的时候就会经过层层回调,最终调用到这个回调函数,把对应 fd 的结构体放入就绪队列中,从而把 epoll 从 epoll_wait 出唤醒。 

这个对应结构体是什么?

结构体叫做 epitem ,每个注册到 epoll 池的 fd 都会对应一个。

就绪队列需要用很高级的数据结构吗?

就绪队列就简单了,因为没有查找的需求了呀,只要是在就绪队列中的 epitem ,都是事件就绪的,必须处理的。所以就绪队列就是一个最简单的双指针链表。

小结下:epoll 之所以做到了高效,最关键的两点:

  1. 内部管理 fd 使用了高效的红黑树结构管理,做到了增删改之后性能的优化和平衡;

  2. epoll 池添加 fd 的时候,调用 file_operations->poll ,把这个 fd 就绪之后的回调路径安排好。通过事件通知的形式,做到最高效的运行;

  3. epoll 池核心的两个数据结构:红黑树和就绪列表。红黑树是为了应对用户的增删改需求,就绪列表是 fd 事件就绪之后放置的特殊地点,epoll 池只需要遍历这个就绪链表,就能给用户返回所有已经就绪的 fd 数组;

 图片

  3   哪些 fd 可以用 epoll 来管理?

再来思考另外一个问题:由于并不是所有的 fd 对应的文件系统都实现了 poll 接口,所以自然并不是所有的 fd 都可以放进 epoll 池,那么有哪些文件系统的 file_operations 实现了 poll 接口?

首先说,类似 ext2,ext4,xfs 这种常规的文件系统是没有实现的,换句话说,这些你最常见的、真的是文件的文件系统反倒是用不了 epoll 机制的。

那谁支持呢?

最常见的就是网络套接字:socket 。网络也是 epoll 池最常见的应用地点。Linux 下万物皆文件,socket 实现了一套 socket_file_operations 的逻辑( net/socket.c ):

static const struct file_operations socket_file_ops = {
    .read_iter =    sock_read_iter,
    .write_iter =   sock_write_iter,
    .poll =     sock_poll,
    // ...
};

我们看到 socket 实现了 poll 调用,所以 socket fd 是天然可以放到 epoll 池管理的。

还有支持的吗?

有的,很多。其实 Linux 下还有两个很典型的 fd ,常常也会放到 epoll 池里。

  • eventfd:eventfd 实现非常简单,故名思义就是专门用来做事件通知用的。使用系统调用 eventfd 创建,这种文件 fd 无法传输数据,只用来传输事件,常常用于生产消费者模式的事件实现;

  • timerfd:这是一种定时器 fd,使用 timerfd_create 创建,到时间点触发可读事件;

小结一下:

  1. ext2,ext4,xfs 等这种真正的文件系统的 fd ,无法使用 epoll 管理;

  2. socket fd,eventfd,timerfd 这些实现了 poll 调用的可以放到 epoll 池进行管理;

其实,在 Linux 的模块划分中,eventfd,timerfd,epoll 池都是文件系统的一种模块实现。

图片

 思考

前面我们已经思考了很多知识点,有一些简单有趣的知识点,提示给读者朋友,这里只抛砖引玉。

问题:单核 CPU 能实现并行吗?

不行。

问题:单线程能实现高并发吗?

可以。

问题:那并发和并行的区别是?

一个看的是时间段内的执行情况,一个看的是时间时刻的执行情况。

问题:单线程如何做到高并发?

IO 多路复用呗,今天讲的 epoll 池就是了。

问题:单线程实现并发的有开源的例子吗?

redis,nginx 都是非常好的学习例子。当然还有我们 Golang 的 runtime 实现也尽显高并发的设计思想。

总结

  1. IO 多路复用的原始实现很简单,就是一个 1 对多的服务模式,一个 loop 对应处理多个 fd ;

  2. IO 多路复用想要做到真正的高效,必须要内核机制提供。因为 IO 的处理和完成是在内核,如果内核不帮忙,用户态的程序根本无法精确的抓到处理时机;

  3. fd 记得要设置成非阻塞的哦,切记;

  4. epoll 池通过高效的内部管理结构,并且结合操作系统提供的 poll 事件注册机制,实现了高效的 fd 事件管理,为高并发的 IO 处理提供了前提条件;

  5. epoll 全名 eventpoll,在 Linux 内核下以一个文件系统模块的形式实现,所以有人常说 epoll 其实本身就是文件系统也是对的;

  6. socketfd,eventfd,timerfd 这三种”文件“fd 实现了 poll 接口,所以网络 fd,事件fd,定时器fd 都可以使用 epoll_ctl 注册到池子里。我们最常见的就是网络fd的多路复用;

  7. ext2,ext4,xfs 这种真正意义的文件系统反倒没有提供 poll 接口实现,所以不能用 epoll 池来管理其句柄。那文件就无法使用 epoll 机制了吗?不是的,有一个库叫做 libaio ,通过这个库我们可以间接的让文件使用 epoll 通知事件,以后详说,此处不表;

后记

 epoll 池使用很简洁,但实现不简单。还是那句话,Linux 内核帮你包圆了。今天并没有罗列太多源码实现,以很小的思考点为题展开,简单讲了一些 epoll 的思考。

uv_poll_t这个结构体是干啥用的 uv_poll_t` 是 libuv 库中定义的一个结构体,用于封装和管理与轮询(polling)相关的操作。轮询是一种I/O多路复用技术,它允许程序监控多个文件描述符(或其他I/O对象),并确定哪些文件描述符已经准备好进行读取或写入操作。在 libuv 中,`uv_poll_t` 结构体通常与 `uv_poll_init` 和 `uv_poll_start` 等函数一起使用,以设置和启动轮询机制。这在需要处理多个I/O流时非常有用,例如在网络服务器中监听多个客户端的连接请求。 阅读详情

相关推荐

深入理解Linux epoll机制与高性能网络编程

网络IO多路复用技术是构建高并发服务器的核心基础,其中Linux的epoll机制因其高效的O(1)事件通知复杂度成为主流解决方案。通过红黑树管理文件描述符和就绪链表实现快速事件分发,epoll完美解决了C10K问题。相比传统的select/poll,epoll支持边缘触发(ET)和水平触发(LT)两种模式,配合非阻塞IO可实现百万级并发连接。在Nginx、Redis等高性能服务器中,epoll都发挥着关键作用。理解其底层实现原理和最佳实践,对开发分布式系统、即时通讯等需要处理海量连接的场景至关重要。

weixin_33507732的博客 331

设备驱动轮询(poll)操作

阻塞和非阻塞I/O是设备访问的两种模式,阻塞操作是指在执行设备操作时,若不能获得资源,则挂起进程直到满足可操作得条件后在进行操作,被挂起的进程进入睡眠状态,被从调度器的运行队列移走,直到等待的条件被满足。设备驱动中阻塞I/O一般基于等待队列或基于等待队列的内核API实现。 非阻塞操作的进程在不能进行设备操作时,并不会被挂起,它要么放弃,要么不停的查询直到可以进行操作为止。 非阻塞I/O的应用程序可以借助轮询函数来查询设备是否能被立即访问,在用户程序中,通常使用select(),poll(),epoll()接

qq_34100435的博客 979

Linux高性能TCP服务器编程:深入理解epoll机制

epoll通过内核中的红黑树管理所有被监听的文件描述符。当文件描述符状态发生变化时,内核会将其加入到就绪队列中,然后由epoll_wait函数返回就绪的文件描述符。通过这种方式,epoll能够在高并发场景下保持良好的性能。事件驱动编程是一种通过在检测到事件发生时调用事件处理函数来响应事件的编程范式。这种模式通常应用于需要高效处理大量并发事件的场景。

weixin_35414260的博客 742

2022.12.30work

驱动作业

weixin_60375366的博客 183

ESP32 TWAI CAN库实战:从零构建CyberGear微电机控制框架

本文详细介绍了如何利用ESP32的TWAI控制器和Arduino环境,从零构建一个面向对象的CyberGear微电机控制框架。该框架将复杂的CAN通信协议封装为简洁的API,开发者无需深究底层细节,即可轻松实现电机的速度、位置和扭矩控制,大幅简化机器人关节、云台等项目的开发流程。

oil88的博客 499

MAX96712在车载摄像头系统中的带宽分配与传输能力计算解析

在计算摄像头带宽与传输接口的关系时,需要考虑视频流的像素分辨率、帧率、颜色深度等因素。以 MAX96712 这种 GMSL2 产品为例,它支持每个链路在正向方向以 3Gbps 或 6Gbps 的固定速率工作。

weixin_43199439的博客 3330

poll&&epoll实现分析(一)——poll实现

0.等待队列 在Linux内核中等待队列有很多用途,可用于中断处理、进程同步及定时。我们在这里只说,进程经常必须等待某些事件的发生。等待队列实现了在事件上的条件等待: 希望等待特定事件的进程把自己放进合适的等待队列,并放弃控制全。因此,等待队列表示一组睡眠的进程,当某一条件为真时,由内核唤醒它们。 等待队列由循环链表实现,由等待队列头(wait_queue_head_t)和等待队列

977

企业碳排放计算方法与实践:从年度总量到月度分配的完整流程

本文系统介绍了企业碳排放计算的全流程方法,包括数据预处理、能源活动与产品生产碳排放计算、年度汇总及月度分配优化。通过ARIMA模型预测补充缺失数据,采用二次规划算法实现基于用电量模式的月度碳排放分配,确保计算结果的准确性与合理性。该方法为企业碳排放管理提供了科学工具,有助于精准监控排放、制定减排策略并满足合规要求。实施中需注重数据质量控制和排放因子选择,并根据企业特点调整参数。该流程适用于制造业企业,为实现碳中和目标提供可靠的数据基础和管理支持。

applehunqiu的博客 1573

【深入理解 Linux 的 Epoll:高效的 I/O 事件处理机制】

当多路复用的文件数量庞大、IO流量频繁的时候,一般不太适合使用select()和poll(),这种情况下select()和poll()表现较差,推荐使用epoll()。本文部分来源:https://subingwen.cn/linux/epoll/#2-%E6%93%8D%E4%BD%9C%E5%87%BD%E6%95%B0。epoll_create()函数的作用是创建一个红黑树模型的实例,用于管理待检测的文件描述符的集合。2、控制操作:使用 epoll_ctl 添加、修改或删除监视的文件描述符。

weixin_43845530的博客 935

Anthropic API接入指南:连接失败、403鉴权与Claude Code集成排查

在AI应用开发中,API接入与鉴权机制是开发者绕不开的基础工程问题。无论是调用云端模型服务还是自建网关,网络连通性、HTTP状态码语义、模型路由映射都会直接影响服务可用性。本文从通用的网络诊断与鉴权原理出发,梳理了从TCP连接失败到403权限拒绝的典型链路,并深入解析网关模型路由匹配机制。在此基础上,进一步说明如何通过环境变量完成Claude Code与VS Code的本地集成,以及一套基于Python的可复现接入方案。内容涵盖Anthropic API高频报错定位、curl冒烟测试、SDK工程化实践,适合

weixin_28743405的博客 469

CRC校验查表法详解

CRC校验的查表法理解算法原理基础计算查表法原理及计算查表法中的表CRC校验中数据逆序CRC校验中的其它参数 算法原理 CRC(Cyclic Redundance Check)循环冗余校验,是基于某个多项式计算出一组校验码,用于核对数据传输过程中是否被更改或传输错误 基础计算 假设需要检验的数据 g :10100111,这串二进制码可以表示为代数多项式g(x) = x^7 + x^5 + x^2 + x +1, 而作为除数的多项式 h(x) 可以自由选择或者按照国际标准选择,一般h(x)的阶数如果是m

boom的博客 1万+

【Linux 网络编程】深入理解 Linux 高并发核心:epoll 机制全解析

本文深入解析Linux高性能网络编程中的epoll机制,针对C10K问题提出解决方案。传统模型如select/poll因资源消耗大、效率低(O(n)扫描)而受限。epoll通过三大API(创建实例、管理事件、等待事件)实现高效事件通知,内核与用户空间共享信息减少拷贝开销,时间复杂度优化为O(1)。其核心优势在于动态数据结构、就绪事件直接返回,并通过水平/边沿触发模式灵活适配场景,成为高并发服务器的关键技术。

2403_88316985的博客 911

微信小程序 - 实现人脸识别对比+人脸相似度数据,用户从相册选择或拍照上传两张图片进行人脸对比得到人脸相似度及判断是否是同一个人,微信小程序检测人脸相似度(免费纯前端方案+调用第三方API接口方案)

微信小程序实现人脸识别对比,微信小程序人脸对比 人脸对比识别 人脸对比相似度,实现获取手机摄像头权限,调用相机拍照实现人脸识别相似度对比,小程序人脸相似度对比,快速识别两张照片的相似程度(用百分比)图片相似度对比在线测试,图片相似度对比,微信小程序人脸相似度对比分析,计算两张人脸的相似度,从而判断是否同一个人,并给出相似度评分。小程序如何实现人脸相似度比对,小程序纯前端怎么对比两张图片的相似度,计算两幅图像的相似度,图像相似度对比方法,调用百度AI的人脸识别,微信小程序中实现人脸识别认证,实现人脸检测、人脸

🏆 前端领域优质创作者 4863

Linux 高性能网络编程:深入理解 poll 和 epoll 机制

poll和epoll。poll作为select的改进版本,解决了 FD 数量限制的问题,但仍然存在遍历所有 FD 的性能开销。而epoll作为 Linux 特有的高性能 I/O 多路复用机制,采用事件驱动方式,避免了遍历所有 FD,在大规模并发场景下表现优异。在实际应用中,应根据具体场景选择合适的 I/O 多路复用机制。对于连接数较少、活跃连接比例高或有跨平台需求的应用,poll是一个不错的选择。而对于连接数众多、活跃连接比例低的高性能网络应用,epoll则是首选。掌握poll和epoll。

2301_79533973的博客 966

Python 爬虫实战:Twitter 数据定向抓取(特定话题 + 用户推文爬取与分析)

在社交媒体研究、舆情监测和市场分析等领域,Twitter 数据具有极高的价值。本文将详细介绍如何使用 Python 构建一个完整的 Twitter 数据爬取系统,包括特定话题和用户推文的爬取与分析。我们将涵盖从认证到数据存储、清洗、分析和可视化的全流程。

u014481728的博客 1038

YOLOv9改进策略【注意力机制篇】| 添加SE、CBAM、ECA、CA、Swin Transformer等注意力和多头注意力机制

这篇文章带来一个经典注意力模块的汇总,虽然有些模块已经发布很久了,但后续的注意力模块也都是在此基础之上进行改进的,对于初学者来说还是有必要去学习了解一下,以加深对模块,模型的理解。

Limiiiing的博客 2892

深入理解linux内核Epoll机制,源码分析

https://www.toutiao.com/i6748299406518256135/#6649976-qzone-1-84791-3c8d8e8bb11b3fb9a4fd89478693d6c1 epoll嵌套限制 头条号:s无邪

cf125313的专栏 343

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

上一篇: C++11新特性之十:enable_shared_from_this
下一篇: error: template with C linkage
ChingDawn
博客等级 码龄11年 17粉丝 12原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值