从 select 到 epoll:拆解 I/O 多路复用的演进与实战
目录
一、引言:为什么需要 I/O 多路复用?
二、select
1.函数介绍
2.原理
3.样例代码
4.优缺点总结
三、poll
1.函数介绍
2.样例代码
3.优缺点总结
四、epoll
1.函数介绍
2.原理
3.LT和ET两种工作模式
4.优缺点总结
五、核心机制对比:select vs poll vs epoll
六、高频面试题解析
七、结语
一、引言:为什么需要 I/O 多路复用?
I/O 多路复用(I/O Multiplexing)是为了解决 高并发场景下传统阻塞式或非阻塞式 I/O 模型的效率缺陷 而诞生的核心技术。阻塞式IO,无论是单线程阻塞式IO还是多线程阻塞式IO,在数据未就绪时线程会被挂起,直到数据就绪后才恢复执行。而且,多线程还涉及到线程切换,以及内存开销。线程切换那是需要时间的,频繁的切换会导致效率降低。至于非阻塞式的IO,轮询高频发生,浪费CPU资源。试想这样的场景:当你有一万个网络连接需要同时处理时,为每个连接开一个线程是否可行?答案显然是否定的,抛开线程上下文切换,单从内存上看,Linux默认一个线程大小是8MB,所以内存消耗80000MB / 1024 ≈ 78G。
二、select
多路复用(也叫多路转接)三剑客之一,单进程就可以监视多个文件描述符的可读、可写和异常状态。
1.函数介绍
select函数原型:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
1)nfds:监视的最大文件描述符+1。
2)readfds、writefds、 exceptfds分别表示可读、可写、异常文件描述符的集合。
3)timeout,用来设置等待时间,可避免一直阻塞。
4)返回值
● 返回值大于0,表示监视的文件描述符中已就绪的个数。
● 返回值等于0,表示并没有文件描述符就绪,超过timeout时间。
● 返回值小于0,表示发生错误。
5)timeval结构体,第一个成员变量单位是秒,第二个单位是微秒。

2.原理
select底层是用三张位图来存放对应的对应的文件描述符集合的,并且提供了一套对位图的操作方法。
void FD_CLR(int fd, fd_set *set); / /将指定文件描述符移出集合,对应位设为0
int FD_ISSET(int fd, fd_set *set); / /检查fd是否在集合中(对应位是否为1),返回非0表示存在
void FD_SET(int fd, fd_set *set); / /将指定文件描述符加入集合,对应位设为1
void FD_ZERO(fd_set *set); / /清空集合,将位图中所有位设为0
select底层是会把集合给修改的,例如设置进可读文件描述符集合的是0011 0000,当6号文件描述符可读然后函数返回后,可读文件描述符集合被修改成了0010 0000,没有事件发生的fd = 5被清空。如果我们下次调用还要关心fd = 5的话,需要再次设置进可读文件描述符集合中,需要频繁的重复设置,所以实践中干脆用一个辅助数组来存放这些下次还要关心的fd(避免丢失),每次在调用select之前都遍历一遍辅助数组来设置这些fd,在返回时亦是如此,需要遍历整个位图才知道哪些fd就绪了,这是导致效率比较低的一个原因。
3.样例代码
#include <iostream>
#include <sys/select.h>
#include <unistd.h>int main()
{fd_set read_fds;FD_ZERO(&read_fds);FD_SET(0, &read_fds);//检测标准输入while(true){struct timeval timeout;timeout.tv_sec = 2;//设置超时时间为2秒int ret = select(1, &read_fds, nullptr, nullptr, &timeout);if(ret < 0){perror("select");continue;}if(ret == 0){std::cout << "timeout" << std::endl;continue;}if(FD_ISSET(0, &read_fds)){char buffer[1024] = { 0 };int n = read(0, buffer, sizeof(buffer) - 1);if(n > 0){std::cout << buffer << std::endl;}}//内核会修改位图,需要重新设置进去FD_ZERO(&read_fds);FD_SET(0, &read_fds);}return 0;
}
4.优缺点总结
先说优点,select的跨平台兼容性比较好,几乎主流的操作系统都是兼容的,这相对于epoll来说是个优点(epoll只能在Linux上用)。
缺点有:
①文件描述符数量的上限低,通常是1024个,这对于动辄上万个连接的高并发场景来说是远远不够的。
②其次,select需要做不少的遍历工作,用户需要遍历返回后的位图,查找哪些是已经就绪的,然而有时候,遍历一千个文件描述符才有屈指可数的几个就绪也是有的。这就导致了很多无效的遍历。在内核层面,每次调用select函数时亦是如此,需要遍历所有的监听描述符判断是否就绪。这样,随着监听描述符的增多,效率就会不断下降。
③同时,在每一次调用select函数时,需要将fd集合从用户态拷贝到内核态,返回时内核在将结果拷贝回用户态。
④除了拷贝上的时间开销,还有内核在将处理结果拷贝到用户态是,会覆盖原有的数据,如果下次还想监听的描述符因此被覆盖的话,下次还要再重新设置。
三、poll
1.函数介绍
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
1)fds是poll函数监听的结构列表,每一个元素中包含三个成员变量:文件描述符、监听事件的集合、返回的事件集合。pollfd结构体如下:
// pollfd 结构
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
2)nfds表示fds的长度。
3)timeout表示超时时间,单位是毫秒。
4)返回值含义和select一样。
2.样例代码
#include <iostream>
#include <poll.h>
#include <unistd.h>int main()
{struct pollfd poll_fd;poll_fd.fd = 0; //监听标准输入poll_fd.events = POLLIN;//可读事件while(true){int ret = poll(&poll_fd, 1, 3000);if(ret < 0){perror("poll");continue;}if(ret == 0){std::cout << "timeout" << std::endl;continue;}if(poll_fd.revents == POLLIN)//判断返回的事件{char buffer[1024] = { 0 };int n = read(0, buffer, sizeof(buffer) - 1);if(n > 0){std::cout << buffer << std::endl;}}}return 0;
}
3.优缺点总结
先说优点,首先值得一提的是,poll突破了文件描述符数量的限制。使用结构体数组(struct polled[ ])而非位图(fd_set),可监听任意数量的文件描述符,仅受系统资源的限制。第二点,相对于select来说,poll做到了事件和返回结果的解耦(events和revents),不会再像select那样,从内核拷贝到用户时会覆盖原有数据,进而需要再次设置。
缺点是,调用poll时还是需要将数据拷贝到内核,返回时从内核拷贝到用户。而且也需要遍历整个struct polled[ ]数组,检查哪些fd已就绪。
四、epoll
epoll是 Linux 特有的高效 I/O 多路复用机制,专为解决select和poll的性能瓶颈而设计,尤其适合处理高并发问题。按照man手册的说法,epoll是为了处理大量句柄而做了改进的poll。所谓句柄,就是epoll_create函数返回的文件描述符。
1.函数介绍
int epoll_create(int size);
这个函数的作用就是创建一个epoll的句柄。size参数已被忽略,可任意传值。这个句柄(也就是文件描述符)用完以后,需要调用close函数关闭。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
● epoll的事件注册函数。
● 第一个参数是epoll的句柄,即epoll_create函数的返回值。
● 第二个参数表示动作,用三个宏来表示。
EPOLL_CTL_ADD:注册新的fd
EPOLL_CTL_MOD:修改已经注册fd的监听事件
EPOLL_CTL_DEL:删除fd
● 第三个参数是要监听的fd。
● 第四个参数是告诉内核要监听什么事件。
至此,可能比较迷的就是struct epoll_event是什么鬼?下面是它的结构:
struct epoll_event {uint32_t events; // 监控的事件类型(如 EPOLLIN、EPOLLOUT)epoll_data_t data; // 用户数据(常保存 fd 或回调函数指针)
};typedef union epoll_data {void *ptr;int fd; // 常用:与事件关联的 fduint32_t u32;uint64_t u64;
} epoll_data_t;
下面看看epoll_ctl函数是怎么用的。
struct epoll_event ev;
ev.data.fd = fd;
ev.events = EPOLLIN;
int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);//epoll_fd为句柄
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
● 等待事件触发。
● 第一个参数是epoll句柄。
● 输出参数,存放就绪事件的结构体数组
● maxevents,events数组大小(单次返回的最多事件数)
● 超时时间(ms),-1 表示阻塞直到事件触发
● 返回值:>0:触发的事件数量 0:超时 -1:错误
函数使用样例:
epoll_event events[1000];
int nfds = epoll_wait(epoll_fd, events, sizeof(events) / sizeof(events[0], -1);
2.原理
epoll原理围绕红黑树、就绪列表、回调函数机制这三者展开。当调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这两个结构体中有两个关键的成员——红黑树和就绪列表。
struct eventpoll{
....
/*红黑树的根节点,这颗树中存储着所有添加到 epoll 中的需要监控的事件*/
struct rb_root rbr;
/*双链表中则存放着将要通过 epoll_wait 返回给用户的满足条件的事件。就绪列表*/
struct list_head rdlist;
....
};
通过epoll_ctl方法添加的事件,最终就会被挂到红黑树中。而所有添加到epoll中的事件都会与设备驱动程序建立回调关系,然后在监控的事件发生时调用这个回调方法。这个回调方法在内核中叫做ep_poll_callback,它会把发生的事件添加到就绪列表rdlist中。然后,当调用epoll_wait方法时,只需要检查eventpoll中的就绪列表rdlist是否有元素即可。如果就绪列表不为空,那么就将发生的事件拷贝给用户,同时将事件的数量返回给用户。 与select和poll相比,epoll就不用去遍历所有的事件,导致一些无效的遍历和判断。下面继续深入到一些细节问题。

红黑树中的每个结点,都是一个epitem结构体。下面看看这个结构体长啥样。
struct epitem{
struct rb_node rbn; //红黑树节点
struct list_head rdllink; //双向链表节点
struct epoll_filefd ffd; //事件句柄信息
struct eventpoll *ep; //指向其所属的 eventpoll 对象
struct epoll_event event; //期待发生的事件类型
}
总结一下:
红黑树的作用就是存储所有需要监控的文件描述符(fd)及其关联的事件(
epoll_event) 。就绪列表的作用就是临时存储已触发的fd(就绪事件)。当事件触发时,由回调函数ep_poll_callback将对应的结点添加到就绪列表中。当调用epoll_wait方法时,将就绪事件拷贝给用户然后清空就绪列表。
重谈epoll_create、epoll_ctl、epoll_wait三个函数。
1)当调用epoll_create时,创建eventpoll结构体,然后初始化红黑树的根节点和就绪列表表头。
2)当调用epoll_ctl(epfd, EPOLL_CTL_ADD, fd, event)时,内核首先会检查红黑树中是否已经存在对应的fd结点。如果不存在,那就新 建epitem结点并入树。然后设置该fd对应的回调函数ep_poll_callback,绑定到其底层设备驱动。
3)当调用epoll_wait时,就去检查就绪列表,如果不为空,则拷贝到用户态,然后清空就绪列表。
3.LT和ET两种工作模式
LT(Level-Triggered,水平触发),只要文件描述符fd满足可读/可写的条件,epoll_wait会持续通知应用层,直到数据被完全处理。
当fd的接收缓冲区有数据时,会触发EPOLLIN事件,只要数据未读完,每次调用epoll_wait方法都会重复报告此fd的可读事件。同理,如果发送缓冲区未满(可写入),则持续触发EPOLLOUT。
优点:容错性强,未处理的事件不会被遗漏,LT模式下会持续通知上层应用。而且编程比较简单。
缺点:若应用层未及时处理完数据,频繁触发事件会增加epoll_wait的调用次数。一个高负载的fd可能会导致epoll_wait长期处于忙碌状态。
ET(Edge-Triggered,边缘触发),仅在fd状态变化时触发通知(比如从不可读变为可读),后续如果数据未读完也不在通知,直到下一次状态变化(比如数据由少变多)。
当接收缓冲区从空变为非空时,触发一次EPOLLIN,之后即使仍有数据未读完也不在重复通知。因此应用层必须在一次通知中处理完所有数据,否则可能永远无法收到下一个状态变化的通知,进而丢失数据。
优点:仅在状态变化时触发一次,减少了epoll_wait的调用频率。同时一次性把接收缓冲区中的数据全部读完,这样发送方可以收到更大的窗口大小通知,进而可以发送更多的数据,提高网络的吞吐量。
缺点:编程复杂,如果处理数据不当,可能丢失数据。
| 特性 | LT(水平触发) | ET(边缘触发) |
|---|---|---|
| 触发条件 | 数据未处理完则持续触发 | 仅当状态变化时触发一次 |
| 系统调用次数 | 可能较多(重复触发) | 较少(严格依赖状态变化) |
| 编程复杂度 | 低 | 高(需非阻塞 IO + 循环处理) |
| 容错性 | 高(未处理完会再次触发) | 低(漏处理可能导致事件丢失) |
| 适用场景 | 简单场景、对性能不敏感的延迟容忍型 | 高并发、延迟敏感型(如高频交易系统) |
4.优缺点总结
优点:
1)epoll有着高效的事件通知机制(基于回调函数),epoll_wait直接返回给用户就绪事件列表,无需遍历所有的文件描述符,性能与活跃连接数无关,对比select和poll的O(n)遍历,epoll在高并发场景下性能卓越。
2)支持大并发连接,文件描述符数量仅受系统资源限制。相比与select,这是一个优点。
3)事件触发模式灵活,根据不同的场景,有LT(默认)和ET两种模式可选。
4)无重复数据拷贝。select和poll在每次调用时,都需要将文件描述符集从用户空间拷贝到内核空间,而epoll在添加对应文件描述符和它对应的事件后,再调用epoll_wait时,无需再传入监听的文件描述符集,进而没有重复拷贝。
缺点:仅限Linux平台,代码可移植性差。
五、核心机制对比:select vs poll vs epoll
| 指标 |
|
|
|
|---|---|---|---|
| 最大并发连接数 | 1024 | 10k+(受内存限制) | 100k+(内存依赖) |
| 时间复杂度 | O(n) | O(n) | O(1) |
| 内存拷贝开销 | 每次调用传递全量 fd | 同 select | 注册后无需重复传递 |
| 触发模式 | 仅 LT | 仅 LT | LT 和 ET |
| 适用平台 | 跨平台 | 类 Unix | Linux |
六、高频面试题解析
epoll 为什么用红黑树不用哈希表?
1)红黑树性能稳定可靠。哈希表查找的平均时间复杂度为O(1),但是在有大量哈希冲突,可能退化到O(N)。内核需要保证稳定的响应时间,无法接受性能波动,这对于高并发场景非常关键。红黑树作为自平衡的二叉搜素树,插入、查找、删除操作均严格保证O(logN)的时间复杂度,性能可预测且稳定。
2)红黑树具有更高效的动态操作。哈希表可能需要扩容,扩容就需要重新计算哈希值并迁移数据,导致短暂的性能抖动。红黑树天然支持动态的增删结点,无需全局调整结构,可以平滑的支持扩展。
3)红黑树的内存利用率比较高而且不存在冲突。在稀疏连接的场景下,哈希表中可能存在不少空桶,导致浪费。红黑树是按需分配结点内存的,不存在额外的浪费。而且红黑树是以fd来作为键值的,天然就避免了冲突。
ET 模式必须配非阻塞 socket 吗?为什么?
在ET(边缘触发)模式下使用非阻塞socket是强烈建议且几乎必须的。
1)ET模式下,要求上层一次读完数据,为了确保一次读完数据,需要通过循环读取,比如循环的调用read。但是,在接收缓冲区中的数据读完以后,再次调用read会导致阻塞,进而线程被挂起,直到下一波数据的到达。线程一旦被挂起,就无法去处理其他事件,严重影响并发性能。
2)非阻塞可以维持事件循环的高效性。非阻塞socket在每次IO操作后立即返回,使得应用可以快速处理完当前事件后,继续通过epoll_wait监听其他就绪事件,保持事件循环的高吞吐。如果是阻塞式的socket,单次处理可能长时间占用线程,导致其他就绪时间延迟处理,降低系统整体的响应速度。
如果 fd 频繁增减,epoll 是否仍高效?
依然高效,理由如下:
1)epoll使用红黑树作为管理fd的底层结构,其增删查操作均严格保证O(logN)的时间复杂度(n是当前监听的fd数量)。即使fd频繁增删,单次操作的性能仍被控制在可控范围内。对比select和poll这种基于线性扫描的模型,性能随fd数量的增长线性下降。而epoll的 O(log n) 操作显著优于这种线性开销。
2)epoll事件通知机制的天然优势——无轮询开销。epoll通过事件驱动的方式通知就绪的fd(基于回调机制),而非遍历整个集合。fd的频繁增删不会直接关联事件通知的开销。epoll_wait只返回就绪的fd。
七、结语
多路转接在面试中还是比较常见的,还请诸位慎重。
路漫漫其修远兮,吾将上下而求索!如有错误,请不吝指出,感谢支持!
完结~
相关文章:
从 select 到 epoll:拆解 I/O 多路复用的演进与实战
目录 一、引言:为什么需要 I/O 多路复用? 二、select 1.函数介绍 2.原理 3.样例代码 4.优缺点总结 三、poll 1.函数介绍 2.样例代码 3.优缺点总结 四、epoll 1.函数介绍 2.原理 3.LT和ET两种工作模式 4.优缺点总结 五、核心机制对比&…...
Go后端架构探索:从 MVC 到 DDD 的演进之路
Go语言 MVC 与 DDD 分层架构详细对比 MVC和DDD是后台开发两种流行的分层架构思想,MVC(Model-View-Controller)是一种设计模式,主要用于分离用户界面、业务逻辑和数据模型,便于分层解耦,而DDD(领…...
【力扣hot100题】(017)矩阵置零
还是挺简单的,使用哈希表记录需要置换的行列即可,这样就可以避免重复节省时间。 class Solution { public:void setZeroes(vector<vector<int>>& matrix) {unordered_set<int> row;unordered_set<int> line;for(int i0;i&l…...
One Commander 3,文件管理新体验
One Commander 3 是一款集多功能于一体 Windows 10/11的文件管理工具,其设计目的在于为用户带来多元化的操作体验。这款工具通过支持多栏界面布局,让用户能够迅速且高效地组织和管理文件。此外,它还提供了多主题选项和多种图标集,…...
Ubuntu 下 nginx-1.24.0 源码分析
main 函数在 src\core\nginx.c int ngx_cdecl main(int argc, char *const *argv) {ngx_buf_t *b;ngx_log_t *log;ngx_uint_t i;ngx_cycle_t *cycle, init_cycle;ngx_conf_dump_t *cd;ngx_core_conf_t *ccf;ngx_debug_init();if (ngx_strerror_in…...
c# ftp上传下载 帮助类
工作中FTP的上传和下载还是很常用的。如下载打标数据,上传打标结果等。 这个类常用方法都有了:上传,下载,判断文件夹是否存在,创建文件夹,获取当前目录下文件列表(不包括文件夹) ,获取当前目录下文件列表(不包括文件夹) ,获取FTP文件列表(包括文件夹), 获取当前目…...
Java进阶——静态代理与动态代理
代理模式是一种常用的设计模式,为其他对象提供一种代理以控制对这个对象的访问。代理模式就像是一个中间人,客户端通过代理来间接访问目标对象,可以在不修改目标对象的基础上,对目标对象的功能进行增强或扩展。代理模式主要分为静…...
VS Code 中 .history`文件的来源与 .gitignore`的正确使用
引言 在使用 VS Code 进行 Git 版本控制时,有时会发现项目中多出一个 .history 目录,并被 Git 识别为未跟踪文件。本文将解释 .history 的来源,并提供 .gitignore 的正确配置方法,确保开发环境的整洁性。 1. .history 文件的来源…...
非手性分子发光有妙招:借液晶之力,实现高不对称圆偏振发光
*本文只做阅读笔记分享* 一、圆偏振发光研究背景与挑战 圆偏振发光(CPL)材料在3D显示、光电器件等领域大有用处,衡量它的一个重要指标是不对称发光因子(glum)。早期CPL材料的glum值低,限制了实际应用。为…...
解释器模式_行为型_GOF23
解释器模式 解释器模式(Interpreter Pattern)是一种行为型设计模式,核心思想是定义语言的文法规则,并构建一个解释器来解析和执行该语言中的表达式。它类似于“翻译器”——将符合特定语法规则的文本(如数学公式、脚本…...
OTN(Optical Transport Network)详解
OTN(光传送网)是一种基于**波分复用(WDM)**的大容量光传输技术,结合了SDH的运维管理优势和WDM的高带宽特性,广泛应用于骨干网、城域核心层及数据中心互联(DCI)。 1. OTN 的基本概念 …...
YOLOv8+ Deepsort+Pyqt5车速检测系统
该系统通过YOLOv8进行高效的目标检测与分割,结合DeepSORT算法完成目标的实时跟踪,并利用GPU加速技术提升处理速度。系统支持模块化设计,可导入其他权重文件以适应不同场景需求,同时提供自定义配置选项,如显示标签和保存…...
【干货】前端实现文件保存总结
⚠️⚠️文前推荐一下👉 前端必备工具推荐网站(图床、API和ChatAI、智能AI简历、AI思维导图神器等实用工具): 站点入口:http://luckycola.com.cn/ 前端实现文件保存实现总结 在Web开发中,文件下载是常见的交互需求。本文将系统总结前端实现文…...
并发编程之FutureTask.get()阻塞陷阱:深度解析线程池CPU飚高问题排查与解决方案
FutureTask.get方法阻塞陷阱:深度解析线程池CPU飚高问题排查与解决方法 FutureTask.get()方法阻塞陷阱:深度解析线程池CPU飚高问题排查与解决方法1、情景复现1.1 线程池工作原理1.2 业务场景模拟1.3 运行结果1.4 发现问题:线程池没有被关闭1.…...
DGNN-YOLO:面向遮挡小目标的动态图神经网络检测与追踪方法解析
一、算法结构与核心贡献 1.1 文章结构 采用经典五段式结构: 引言:分析智能交通系统(ITS)中小目标检测与追踪的挑战,提出研究动机。相关工作:综述小目标检测(YOLO系列、Faster R-CNN)、目标追踪(SORT、Transformer)和图神经网络(GNN)的进展。方法论:提出DG…...
在Ubuntu中固定USB设备的串口号
获取设备信息 lsusb # 记录设备的Vendor ID和Product ID(例如:ID 0403:6001)# 获取详细属性(替换X和Y为实际设备号) udevadm info -a /dev/ttyUSBX 结果一般如下 创建udev规则文件 sudo gedit /etc/udev/rules.d/us…...
javaSE————文件IO(2)、
文件内容的读写——数据流 我们对于文件操作使用流对象Stream来操作,什么是流对象呢,水流是什么样的,想象一下,水流的流量是多种的,可以流100ml,也可以流1ml,流对象就和水流很像,我…...
前端常问的宏观“大”问题详解(二)
JS与TS选型 一、为什么选择 TypeScript 而不是 JavaScript? 1. 静态类型系统:核心优势 TypeScript 的静态类型检查能在 编译阶段 捕获类型错误(如变量类型不匹配、未定义属性等),显著减少运行时错误风险。例如&…...
[创业之路-343]:创业:一场认知重构与组织进化的双向奔赴
目录 前言:关键词: 一、重构企业认知框架: 1、认知框架的顶层设计——六大维度生态模型 2、认知重构的精密设计——五层结构化模型 第一层:战略层(脑) 第二层:运营层(躯干&…...
智慧电力:点亮未来能源世界的钥匙
在科技日新月异的今天,电力行业正经历着前所未有的变革。智慧电力,作为这一变革的核心驱动力,正逐步改变着我们对电力的认知和使用方式。它不仅是电力行业的一次技术革新,更是推动社会可持续发展、实现能源高效利用的重要途径。 智…...
架构师面试(二十三):负载均衡
问题 今天我们聊微服务相关的话题。 大中型微服务系统中,【负载均衡】是一个非常核心的组件;在微服务系统的不同位置对【负载均衡】进行了实现,下面说法正确的有哪几项? A. LVS 的负载均衡一般通过前置 F5 或是通过 VIP keepa…...
CSS3学习教程,从入门到精通, CSS3 列表控制详解语法知识点及案例代码(24)
CSS3 列表控制详解 CSS 列表控制的语法知识点及案例代码的详细说明,包括 list-style-type、list-style-image、list-style-position 和 list-style 的用法。 1. list-style-type 属性 list-style-type 属性用于设置列表项标记的类型。 语法 list-style-type: v…...
NSSCTF(MISC)—[justCTF 2020]pdf
相应的做题地址:https://www.nssctf.cn/problem/920 binwalk分离 解压文件2AE59A.zip mutool 得到一张图片 B5F31内容 B5FFD内容 转换成图片 justCTF{BytesAreNotRealWakeUpSheeple}...
坚持“大客户战略”,昂瑞微深耕全球射频市场
北京昂瑞微电子技术股份有限公司(简称“昂瑞微”)是一家聚焦射频与模拟芯片设计的高新技术企业。随着5G时代的全面到来,智能手机、智能汽车等终端设备对射频前端器件在通信频率、多频段支持、信道带宽及载波聚合等方面提出了更高需求…...
LiteDB 数据库优缺点分析与C#代码示例
LiteDB 是一个轻量级的 .NET NoSQL 嵌入式数据库,完全用 C# 开发,支持跨平台(Windows、Linux、MacOS),并提供类似于 MongoDB 的简单 API。它以单文件形式存储数据,类似于 SQLite,支持事务和 ACID 特性,确保数据的一致性和可靠性。 优缺点分析 优点: 轻量级与嵌入式:…...
上海SMT贴片技术解析与行业趋势
内容概要 随着长三角地区电子制造产业集群的快速发展,上海作为核心城市正引领着SMT贴片技术的革新浪潮。本文聚焦表面组装技术在高密度互连、微间距贴装等领域的突破性进展,通过解析焊膏印刷精度控制、元件定位算法优化等核心工艺,展现上海企…...
HTML5和CSS3的一些特性
HTML5 和 CSS3 是现代网页设计的基础技术,它们引入了许多新特性和功能,极大地丰富了网页的表现力和交互能力。 HTML5 的一些重要特性包括: 新的语义化标签: HTML5 引入了一些重要的语义化标签如 <header>, <footer>, <articl…...
Linux系统中快速安装docker
1 查看是否安装docker 要检查Ubuntu是否安装了Docker,可以使用以下几种方法: 方法1:使用 docker --version 命令 docker --version如果Docker已安装,输出会显示Docker的版本信息,例如: Docker version …...
每日c/c++题 备战蓝桥杯(最长上升子序列)
点击题目链接 题目描述 给出一个由 n(n≤5000) 个不超过 1e6 的正整数组成的序列。请输出这个序列的最长上升子序列的长度。 最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。 输入格式 第一行,一个整数…...
蓝桥杯—质数
质数 质数是一个只有1和它本身2个因数 代码实现 //求质数 #include<bits/stdc.h> using namespace std; bool zhishu(int n) {if(n1){cout<<"1不是质数";return false;}else if(n>1){for(int i2;i<sqrt(n);i){if(n%i0){cout<<n<<&q…...
