当前位置: 首页 > news >正文

I/O多路复用:poll与epoll

一、select/poll与epoll对比

核心区别

特性select/pollepoll
内核数据结构数组(线性结构)红黑树(存储监听的fd)
内存拷贝每次调用需将fd列表从用户态拷贝到内核态仅在注册fd时拷贝一次(epoll_ctl)
内核事件检测轮询所有fd(O(n))回调机制(事件触发时主动通知,O(1))
用户态处理遍历所有fd查找就绪事件(O(n))直接获取就绪事件列表(O(1))
最大连接数select受FD_SETSIZE限制(默认1024)无限制(受系统资源限制)
触发模式仅水平触发(LT)支持水平触发(LT)和边缘触发(ET)

触发模式详解

  1. 水平触发(LT,默认)

    • 只要fd就绪(如可读),就会一直触发事件(类似“持续提醒”)。
    • 适用于数据分批到达的场景,可多次读取数据。
  2. 边缘触发(ET)

    • 仅在fd状态变化时触发一次事件(如从不可读变为可读)。
    • 必须一次性读完所有数据,否则可能丢失后续事件。
    • 需配合非阻塞I/O使用(如设置fd为O_NONBLOCK)。

二、poll服务器代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>#define MAX_FDS 10   // 最大监听fd数
#define SERVER_PORT 6000// 初始化pollfd数组
void init_pollfd(struct pollfd fds[]) {for (int i = 0; i < MAX_FDS; i++) {fds[i].fd = -1;        // 标记未使用的fdfds[i].events = 0;     // 初始无监听事件fds[i].revents = 0;    // 初始无就绪事件}
}// 向pollfd数组添加新fd
void add_fd(struct pollfd fds[], int fd) {for (int i = 0; i < MAX_FDS; i++) {if (fds[i].fd == -1) {fds[i].fd = fd;fds[i].events = POLLIN;  // 监听读事件break;}}
}// 从pollfd数组删除fd
void del_fd(struct pollfd fds[], int fd) {for (int i = 0; i < MAX_FDS; i++) {if (fds[i].fd == fd) {fds[i].fd = -1;fds[i].events = 0;fds[i].revents = 0;break;}}
}// 处理客户端连接
void handle_accept(int listen_fd, struct pollfd fds[]) {int client_fd = accept(listen_fd, NULL, NULL);if (client_fd == -1) {perror("accept failed");return;}printf("Client %d connected\n", client_fd);add_fd(fds, client_fd);  // 将客户端fd加入poll监听
}// 处理客户端数据接收
void handle_recv(int client_fd, struct pollfd fds[]) {char buf[128] = {0};int n = recv(client_fd, buf, sizeof(buf), 0);if (n < 0) {perror("recv failed");close(client_fd);del_fd(fds, client_fd);return;}if (n == 0) {  // 客户端关闭连接printf("Client %d disconnected\n", client_fd);close(client_fd);del_fd(fds, client_fd);return;}printf("Received from %d: %s\n", client_fd, buf);send(client_fd, "OK", 2, 0);  // 简单响应
}// 初始化服务器套接字
int init_server() {int listen_fd = socket(AF_INET, SOCK_STREAM, 0);if (listen_fd == -1) {perror("socket failed");return -1;}struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);server_addr.sin_addr.s_addr = INADDR_ANY;if (bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {perror("bind failed");close(listen_fd);return -1;}if (listen(listen_fd, 5) == -1) {perror("listen failed");close(listen_fd);return -1;}return listen_fd;
}int main() {int listen_fd = init_server();if (listen_fd == -1) exit(1);struct pollfd fds[MAX_FDS];init_pollfd(fds);add_fd(fds, listen_fd);  // 监听服务器套接字while (1) {int n = poll(fds, MAX_FDS, 5000);  // 超时5秒if (n < 0) {perror("poll failed");continue;} else if (n == 0) {printf("Poll timeout\n");continue;}// 遍历所有fd,处理就绪事件for (int i = 0; i < MAX_FDS; i++) {if (fds[i].fd == -1) continue;  // 跳过未使用的fdif (fds[i].revents & POLLIN) {  // 读事件就绪if (fds[i].fd == listen_fd) {handle_accept(listen_fd, fds);  // 处理新连接} else {handle_recv(fds[i].fd, fds);  // 处理客户端数据}}}}close(listen_fd);return 0;
}

三、epoll服务器代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>#define MAX_EVENTS 10  // 最大就绪事件数
#define SERVER_PORT 6000// 向epoll实例添加fd
void add_epoll_event(int epfd, int fd, int events) {struct epoll_event event;event.data.fd = fd;event.events = events;if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event) == -1) {perror("epoll_ctl add failed");}
}// 从epoll实例删除fd
void del_epoll_event(int epfd, int fd) {if (epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL) == -1) {perror("epoll_ctl del failed");}
}// 处理客户端连接
void handle_accept(int listen_fd, int epfd) {int client_fd = accept(listen_fd, NULL, NULL);if (client_fd == -1) {perror("accept failed");return;}printf("Client %d connected\n", client_fd);// 设置为非阻塞模式(配合ET模式使用)// int flags = fcntl(client_fd, F_GETFL);// fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);add_epoll_event(epfd, client_fd, EPOLLIN);  // 监听读事件
}// 处理客户端数据接收
void handle_recv(int client_fd, int epfd) {char buf[128] = {0};int n = recv(client_fd, buf, sizeof(buf), 0);if (n < 0) {perror("recv failed");close(client_fd);del_epoll_event(epfd, client_fd);return;}if (n == 0) {  // 客户端关闭连接printf("Client %d disconnected\n", client_fd);close(client_fd);del_epoll_event(epfd, client_fd);return;}printf("Received from %d: %s\n", client_fd, buf);send(client_fd, "OK", 2, 0);  // 简单响应
}// 初始化服务器套接字
int init_server() {int listen_fd = socket(AF_INET, SOCK_STREAM, 0);if (listen_fd == -1) {perror("socket failed");return -1;}struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);server_addr.sin_addr.s_addr = INADDR_ANY;if (bind(listen_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {perror("bind failed");close(listen_fd);return -1;}if (listen(listen_fd, 5) == -1) {perror("listen failed");close(listen_fd);return -1;}return listen_fd;
}int main() {int listen_fd = init_server();if (listen_fd == -1) exit(1);int epfd = epoll_create(1);  // 参数无实际意义,仅需>0if (epfd == -1) {perror("epoll_create failed");exit(1);}add_epoll_event(epfd, listen_fd, EPOLLIN);  // 监听服务器套接字struct epoll_event events[MAX_EVENTS];while (1) {int n = epoll_wait(epfd, events, MAX_EVENTS, 5000);  // 超时5秒if (n < 0) {perror("epoll_wait failed");continue;} else if (n == 0) {printf("Epoll timeout\n");continue;}// 处理就绪事件for (int i = 0; i < n; i++) {if (events[i].data.fd == listen_fd) {handle_accept(listen_fd, epfd);  // 处理新连接} else {handle_recv(events[i].data.fd, epfd);  // 处理客户端数据}}}close(listen_fd);close(epfd);return 0;
}

四、epoll核心系统调用详解

1. epoll_create

int epoll_create(int size);  // Linux 2.6.8后size被忽略,传入>0即可
  • 作用:创建epoll实例,返回文件描述符epfd
  • 注意:需调用close(epfd)释放资源。

2. epoll_ctl

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
  • op取值
    • EPOLL_CTL_ADD:注册fd
    • EPOLL_CTL_MOD:修改事件
    • EPOLL_CTL_DEL:删除fd(event可为NULL)
  • event.events常用值
    • EPOLLIN:可读事件
    • EPOLLOUT:可写事件
    • EPOLLET:边缘触发模式(默认水平触发)

3. epoll_wait

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
  • 返回值
    • >0:就绪事件数
    • 0:超时
    • -1:错误(如被信号中断)
  • timeout
    • -1:永久阻塞
    • 0:立即返回
    • >0:超时时间(毫秒)

五、内核实现原理

  1. 红黑树存储监听fd

    • 内核使用红黑树管理所有注册的fd,插入/删除/查询时间复杂度均为O(log n)。
  2. 事件驱动机制

    • 每个fd关联一个回调函数,当fd状态变化(如可读)时,内核主动将事件加入就绪队列。
    • epoll_wait直接从就绪队列获取事件,无需轮询所有fd。
  3. 内存优化

    • 仅在epoll_ctl时拷贝fd和事件到内核,后续epoll_wait无需重复拷贝。

在这里插入图片描述

六、适用场景

  • 高并发场景:处理上万级连接时性能优势显著。
  • 长连接管理:适合连接活跃但数据交互较少的场景(如IM、聊天服务器)。
  • 边缘触发模式:需配合非阻塞I/O,适用于数据一次性读取的场景(如日志服务器)。

以下是关于设置非阻塞模式并配合**边缘触发(ET)**模式的详细补充笔记,包含原理、代码示例和注意事项:

七、ET模式与非阻塞I/O的绑定关系

为什么ET模式必须搭配非阻塞I/O?

  1. ET模式特性

    • 仅在文件描述符状态变化时触发一次事件(如从不可读→可读)。
    • 若未一次性读完数据,后续数据到达时不会再次触发事件(除非再次发生状态变化)。
  2. 阻塞I/O的风险

    • 若使用阻塞I/O,read()/write()可能因数据未完全到达而阻塞,导致:
      • 错过后续事件(ET模式仅触发一次)。
      • 阻塞整个事件循环,影响其他连接。
  3. 非阻塞I/O的作用

    • 通过O_NONBLOCK标志使read()/write()立即返回,允许循环读取/写入直至数据处理完毕(通过EAGAIN错误判断)。
    • 确保在ET模式下一次性处理完所有就绪数据,避免事件丢失。

八、设置非阻塞模式的方法

1. 使用fcntl函数

#include <fcntl.h>// 将fd设置为非阻塞模式
int set_nonblocking(int fd) {int flags = fcntl(fd, F_GETFL);  // 获取当前文件状态标志if (flags == -1) {perror("fcntl F_GETFL failed");return -1;}flags |= O_NONBLOCK;  // 添加非阻塞标志if (fcntl(fd, F_SETFL, flags) == -1) {perror("fcntl F_SETFL failed");return -1;}return 0;
}

2. 调用时机

  • 在通过epoll_ctl注册fd时设置(如客户端连接建立后)。
  • 示例代码(在handle_accept中设置):
    void handle_accept(int listen_fd, int epfd) {int client_fd = accept(listen_fd, NULL, NULL);if (client_fd == -1) {perror("accept failed");return;}printf("Client %d connected\n", client_fd);set_nonblocking(client_fd);  // 设置非阻塞模式add_epoll_event(epfd, client_fd, EPOLLIN | EPOLLET);  // 注册ET模式
    }
    

九、ET模式下的数据读写处理

1. 读取数据(以EPOLLIN为例)

void handle_recv(int client_fd, int epfd) {char buf[128];ssize_t n;// 循环读取直至EAGAIN(非阻塞I/O返回-1且errno=EAGAIN)while ((n = recv(client_fd, buf, sizeof(buf), 0)) > 0) {printf("Received from %d: %s\n", client_fd, buf);memset(buf, 0, sizeof(buf));  // 清空缓冲区}if (n == -1) {if (errno == EAGAIN) {  // 数据读完,正常退出send(client_fd, "OK", 2, 0);  // 响应客户端return;}perror("recv failed");close(client_fd);del_epoll_event(epfd, client_fd);return;}if (n == 0) {  // 客户端关闭连接printf("Client %d disconnected\n", client_fd);close(client_fd);del_epoll_event(epfd, client_fd);}
}

2. 写入数据(以EPOLLOUT为例)

void handle_send(int client_fd, int epfd, const char* data, size_t len) {ssize_t sent = 0;while (sent < len) {ssize_t n = send(client_fd, data + sent, len - sent, 0);if (n == -1) {if (errno == EAGAIN) {  // 缓冲区满,等待下次可写事件// 重新注册EPOLLOUT事件(需先删除再添加)struct epoll_event event;event.data.fd = client_fd;event.events = EPOLLOUT | EPOLLET;epoll_ctl(epfd, EPOLL_CTL_MOD, client_fd, &event);return;}perror("send failed");close(client_fd);del_epoll_event(epfd, client_fd);return;}sent += n;}// 数据发送完毕,取消可写事件监听(按需)struct epoll_event event;event.data.fd = client_fd;event.events = EPOLLIN | EPOLLET;  // 恢复监听读事件epoll_ctl(epfd, EPOLL_CTL_MOD, client_fd, &event);
}

十、完整ET模式代码示例(基于epoll)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>#define MAX_EVENTS 10
#define SERVER_PORT 6000
#define BUF_SIZE 1024// 设置非阻塞模式
int set_nonblocking(int fd) {int flags = fcntl(fd, F_GETFL);if (flags == -1) return -1;flags |= O_NONBLOCK;return fcntl(fd, F_SETFL, flags);
}// 向epoll添加fd(ET模式)
void add_epoll_et(int epfd, int fd) {struct epoll_event event;event.data.fd = fd;event.events = EPOLLIN | EPOLLET;  // 注册读事件+ET模式if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event) == -1) {perror("epoll_ctl add ET failed");}
}// 处理客户端连接
void handle_accept(int listen_fd, int epfd) {int client_fd = accept(listen_fd, NULL, NULL);if (client_fd == -1) {perror("accept failed");return;}printf("Client %d connected (ET mode)\n", client_fd);if (set_nonblocking(client_fd) == -1) {  // 设置非阻塞close(client_fd);return;}add_epoll_et(epfd, client_fd);  // 注册ET模式
}// 处理客户端数据(ET模式下的非阻塞读取)
void handle_recv_et(int client_fd, int epfd) {char buf[BUF_SIZE] = {0};  // 初始化缓冲区ssize_t n;while (1) {  // 大循环,持续读取直到遇到 EAGAIN/EWOULDBLOCK 或错误n = recv(client_fd, buf, sizeof(buf), 0);  // 非阻塞读取if (n == -1) {  // 读取错误或无数据可读if (errno == EAGAIN || errno == EWOULDBLOCK) {// 缓冲区无数据,正常情况,发送响应并退出循环send(client_fd, "ok", 2, 0);break;} else {// 其他错误(如连接重置)perror("recv error");close(client_fd);epoll_ctl(epfd, EPOLL_CTL_DEL, client_fd, NULL);break;}} else if (n == 0) {  // 对端关闭连接printf("Client %d disconnected\n", client_fd);close(client_fd);epoll_ctl(epfd, EPOLL_CTL_DEL, client_fd, NULL);break;} else {  // n > 0,读取到有效数据printf("ET Recv from %d: %s\n", client_fd, buf);// 处理数据(这里简单回发 "ok")send(client_fd, "ok", 2, 0);memset(buf, 0, sizeof(buf));  // 清空缓冲区// 注意:这里不 break,继续循环读取剩余数据(ET 模式需要一次性读完)}}
}int main() {int listen_fd = socket(AF_INET, SOCK_STREAM, 0);if (listen_fd == -1) { perror("socket failed"); exit(1); }struct sockaddr_in addr = {0};addr.sin_family = AF_INET;addr.sin_port = htons(SERVER_PORT);addr.sin_addr.s_addr = INADDR_ANY;if (bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {perror("bind failed"); exit(1);}if (listen(listen_fd, 5) == -1) { perror("listen failed"); exit(1); }int epfd = epoll_create(1);if (epfd == -1) { perror("epoll_create failed"); exit(1); }// 注册服务器套接字(LT模式,因为accept是阻塞操作)struct epoll_event event;event.data.fd = listen_fd;event.events = EPOLLIN;  // 水平触发模式epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &event);struct epoll_event events[MAX_EVENTS];while (1) {int n = epoll_wait(epfd, events, MAX_EVENTS, -1);if (n < 0) { perror("epoll_wait failed"); continue; }for (int i = 0; i < n; i++) {if (events[i].data.fd == listen_fd) {handle_accept(listen_fd, epfd);  // 处理连接(LT模式)} else {if (events[i].events & EPOLLIN) {handle_recv_et(events[i].data.fd, epfd);  // 处理ET读事件}}}}close(listen_fd);close(epfd);return 0;
}

在这里插入图片描述

十一、LT模式与ET模式对比

特性水平触发(LT,默认)边缘触发(ET)
触发条件fd就绪时持续触发fd状态变化时触发一次
I/O模式阻塞/非阻塞均可必须使用非阻塞I/O
数据处理可分批读取(每次触发时处理部分数据)必须一次性读完(循环读取至EAGAIN)
适用场景通用场景(如数据分批到达)高性能场景(如日志、消息队列)
代码复杂度较低(无需循环读取)较高(需处理EAGAIN错误)

十二、注意事项

  1. 避免阻塞操作

    • ET模式下,任何阻塞操作(如不带O_NONBLOCKread)都会导致事件循环阻塞,引发严重性能问题。
  2. 数据完整性

    • 单次recv可能无法读取完整数据,必须通过循环读取确保数据全部处理(依赖EAGAIN判断)。
  3. 事件注册

    • 若修改事件(如从读改为写),需通过epoll_ctl(EPOLL_CTL_MOD)重新注册,否则可能丢失事件。
  4. 缓冲区管理

    • 写操作时需处理EAGAIN(缓冲区满),通常需要维护待发送数据缓冲区,并在下次EPOLLOUT事件时继续发送。

相关文章:

I/O多路复用:poll与epoll

一、select/poll与epoll对比 核心区别 特性select/pollepoll内核数据结构数组&#xff08;线性结构&#xff09;红黑树&#xff08;存储监听的fd&#xff09;内存拷贝每次调用需将fd列表从用户态拷贝到内核态仅在注册fd时拷贝一次&#xff08;epoll_ctl&#xff09;内核事件检…...

【调制识别】PGD攻击中参数的含义

在PGD&#xff08;Projected Gradient Descent&#xff09;对抗攻击中&#xff0c;代码如下&#xff1a; # 定义PGD对抗样本生成类 class AttackPGD(nn.Module): def __init__(self, model, config):super(AttackPGD, self).__init__()self.model model …...

设备数据看板助力自动化工厂实现生产智能精细化管理

工厂数字化转型需要实现自动化设备生产现场可视化、设备系统间的互联互通&#xff0c;以及数据的智能决策。然而&#xff0c;当前许多制造企业仍面临着传统单机设备同质化严重、数字化服务能力不足、售后成本高企、系统集成效率低下等挑战。企业如何通过自动化装备看板和实时数…...

单点登录是是什么?具体流程是什么?

SSO⼀般都需要⼀个独⽴的认证中⼼&#xff08;passport&#xff09;&#xff0c;⼦系统的登录均得通过passport&#xff0c;⼦系统本⾝将不参与登录操作&#xff0c;当⼀个系统成功登录以后&#xff0c;passport将会颁发⼀个令牌给各个⼦系统&#xff0c;⼦系统可以拿着令牌会获…...

什么业务需要用到waf

Web应用防火墙&#xff08;Web Application Firewall&#xff0c;简称WAF&#xff09;主要用于保护Web应用程序免受各种网络攻击&#xff0c;以下是需要用到WAF的业务类型及具体场景&#xff1a; 一、电子商务业务 业务特点&#xff1a;涉及用户注册、登录、支付等敏感操作&a…...

java中的Servlet4.x详解

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 Servlet 4.x 是 Java EE 8&#xff08;现 Jakarta EE&#xff09;规范的一部分&#xff0c;于 2017 年正式发布。作为 Servlet 技术的重大更…...

## Docker 中 Elasticsearch 启动失败:日志文件权限问题排查与解决

好的&#xff0c;这是一份关于你遇到的 Docker Elasticsearch 启动报错问题的笔记&#xff0c;包含问题描述、我的分析判断以及最终的解决方案&#xff0c;适合用于整理成文章。 Docker 中 Elasticsearch 启动失败&#xff1a;日志文件权限问题排查与解决 在使用 Docker部署 E…...

JavaScript:PC端特效--缓动动画

一、缓动效果原理 缓动动画就是让元素运动速度有所变化&#xff0c;最常见的就是让元素慢慢停下来 思路&#xff1a; 让盒子每次移动的距离慢慢变小&#xff0c;速度就会慢慢降下来核心算法&#xff1a;&#xff08;目标值-现在位置&#xff09;/10作为每次移动距离的步长停…...

【深度学习新浪潮】什么是多模态大模型?

多模态大模型是人工智能领域的前沿技术方向,它融合了多种数据模态(如文本、图像、语音、视频、传感器数据等),并通过大规模参数模型实现跨模态的联合理解与生成。简单来说,这类模型就像人类一样,能同时“看”“听”“读”“说”,并将不同信息关联起来,完成复杂任务。 …...

C++23 std::mdspan:多维数组处理新利器

文章目录 引言C23简介std::mdspan的定义与特点定义特点 std::mdspan的优势零成本抽象的多维数据访问减少内存开销提高代码灵活性 std::mdspan的应用场景科学计算图形学 相关提案示例代码使用动态扩展使用静态和动态扩展 总结 引言 在C的发展历程中&#xff0c;每一个新版本都带…...

09、底层注解-@Import导入组件

09、底层注解-Import导入组件 Import是Spring框架中的一个注解&#xff0c;用于将组件导入到Spring的应用上下文中。以下是Import注解的详细介绍&#xff1a; #### 基本用法 - **导入配置类** java Configuration public class MainConfig { // 配置内容 } Configuration Impo…...

码蹄集——N是什么、棋盘

MT1223 N是什么 给定一系列数字3、10、21、36…&#xff0c;输入正整数N&#xff0c;输出上述序列的第N个值。从N1开始计数。 格式 输入格式&#xff1a;输入正整数N 输出格式&#xff1a;输出整型 样例 1 输入&#xff1a;5 输出&#xff1a;55 备注&#xff1a;N小于…...

C++中聚合类(Aggregate Class)知识详解和注意事项

一、聚合类&#xff08;Aggregate Class&#xff09;概念 聚合&#xff08;Aggregate&#xff09; 是 C 中一类特殊的类类型&#xff0c;无用户自定义构造函数、无私有或受保护非静态数据成员、无虚函数、无基类&#xff08;C11 起基类必须也是聚合且无私有/受保护成员&#x…...

python打卡day30

模块和库的导入 知识点回顾&#xff1a; 导入官方库的三种手段导入自定义库/模块的方式导入库/模块的核心逻辑&#xff1a;找到根目录&#xff08;python解释器的目录和终端的目录不一致&#xff09; 作业&#xff1a;自己新建几个不同路径文件尝试下如何导入 python的学习就像…...

PostGIS实现栅格数据导出图片标准格式【ST_AsGDALRaster】

ST_AsGDALRaster 函数应用详解&#xff1a;将栅格数据导出为标准图片格式 [文章目录] 一、函数概述 二、函数参数详解 三、关键功能示例 四、GDAL 选项配置指南 五、性能优化建议 六、注意事项与限制 七、应用场景 八、总结 一、函数概述 ST_AsGDALRaster是PostGIS中…...

4.6 sys模块

sys --- 仅作了解 面试之前冲击一下 python的垃圾回收机制 import sys # 1. api_version : 获取python的内部版本号 print(sys.api_version) #1013 # 2. copyright: 获取cpython的版本 print(sys.copyright) #3.getfilesystemencoding() getdefaultencoding():获…...

「HHT(希尔伯特黄变换)——ECG信号处理-第十三课」2025年5月19日

一、引言 心电信号&#xff08;ECG&#xff09;是反映心脏电活动的重要生理信号&#xff0c;其特征提取对于心脏疾病的诊断和监测具有关键意义。Hilbert - Huang Transform&#xff08;HHT&#xff09;作为一种强大的信号处理工具&#xff0c;在心电信号特征提取领域得到了广泛…...

枪机定焦系统的自动控制装置

枪机定焦系统&#xff0c;作为一种监控设备&#xff0c;通常被广泛应用于各种需要高清、远距离监控的场合。该系统的主要特点是其镜头焦距固定&#xff0c;不能手动或自动调节&#xff0c;从而确保了监控画面的稳定性和清晰度。当提到枪机定焦系统采用自动功能时&#xff0c;可…...

【Unity】Unity中将字典序列化

Unity中将字典序列化&#xff0c;在预制体上能够看到字典的内容&#xff0c;也可以直接在预制体上拖拽给字典赋值 直接上代码 using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine;public class SerializableD…...

VTK|箱体切割器

文章目录 效果实现类头文件实现类源文件如何调用项目git链接 效果 实现类头文件 /*** file BoxClipperController.h* brief 该头文件定义了 BoxClipperController 类&#xff0c;用于管理基于盒子的网格数据裁剪操作。* details 该类提供了使用 vtkBoxWidget 对网格数据进行裁…...

CS50x 01 c

1 getchar() 在 C 语言里&#xff0c;getchar()是一个十分常用的函数&#xff0c;其功能是从标准输入&#xff08;一般指键盘&#xff09;读取单个字符。下面为你详细介绍它的用法。 基本语法 getchar()函数的原型定义在<stdio.h>头文件中&#xff0c;语法形式如下&am…...

确保高质量的音视频通话,如何最大化利用视频带宽

在当今数字时代&#xff0c;音视频内容随处可见&#xff0c;对于开发者来说&#xff0c;理解互联网带宽变得至关重要。我们的在线体验质量&#xff0c;无论是观看高清电影还是演唱会直播&#xff0c;都严重依赖于互联网带宽的概念。在本文中&#xff0c;我们将揭示视频带宽的复…...

@RequestParam 和 @RequestBody、HttpServletrequest 与HttpServletResponse

在Java Web开发中&#xff0c;RequestParam、RequestBody、HttpServletRequest 和 HttpServletResponse 是常用的组件&#xff0c;它们用于处理HTTP请求和响应。下面分别介绍它们的使用场景和使用方法&#xff1a; 1. RequestParam RequestParam 是Spring MVC框架中的注解&am…...

HashMap 的特点及应用场景

一、HashMap 核心特点 1. 基本特性 键值对存储&#xff1a;基于 Map 接口实现&#xff0c;存储 Key-Value 对 允许 null 键/值&#xff1a;可以有一个 null 键和多个 null 值 非线程安全&#xff1a;多线程环境下需要额外的同步措施 无序存储&#xff1a;不保证元素的插入顺…...

day30 python 模块、包与库的高效使用指南

目录 一、Python库的分类与适用场景 表格 1.1 基础工具库 1.2 科学计算库 1.3 数据分析库 1.4 Web开发库 1.5 机器学习库 1.6 自动化脚本库 1.7 网络爬虫库 二、模块与包的导入方式 2.1 标准导入 2.2 从模块中导入特定项 2.3 非标准导入&#xff08;不推荐&#x…...

JVM核心配置参数详解与调优指南

精心整理了最新的面试资料和简历模板&#xff0c;有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 引言 Java虚拟机&#xff08;JVM&#xff09;的配置参数直接影响应用程序的性能、稳定性和资源利用率。合理配置参数能够显著提升吞吐量、降低延迟并避免内存…...

python打卡第30天

知识点回顾&#xff1a; 一&#xff0c;导入官方库的三种手段。 使用 import 直接导入整个模块 import module_name 使用 from ... import ... 导入特定功能 from module_name import function_name 使用 as 关键字重命名模块或功能 import module_name as alias # 或 from mod…...

数据要素及征信公司数据要素实践

数据要素及征信公司数据要素实践 1.数据要素的定义与核心特征2.征信公司应用数据要素的实践路径3.总结1.数据要素的定义与核心特征 数据要素是数字经济时代的新型生产要素,指以电子形式存在、通过计算方式参与生产经营活动并创造价值的数据资源。 其核心特征包括: 新型生产…...

耗时十分钟,做了一个 uniapp 灵感泡泡机

最近&#xff0c;我用 UniApp 搭配 CodeBuddy 实现了一个充满童话感的小应用&#xff0c;名叫 IdeaBubbles&#xff08;灵感泡泡机&#xff09;。它是一个单页 WebApp&#xff0c;用来记录那些转瞬即逝的灵感时刻。整个界面以梦幻气泡和彩虹玻璃拟态为主题&#xff0c;视觉效果…...

《Head First 设计模式》第二章 - 笔记

本书是本人写的设计模式的笔记&#xff0c;写下核心要点&#xff0c;如果你掌握过设计模式&#xff0c;想快速阅读本书内容&#xff0c;这个笔记适合你阅读。如果你是新手&#xff0c;有 java 基础和 oo 设计原则基础&#xff0c;你适合跟我一样从零阅读本书。 第一章 观察者模…...

matlab绘制光学传递函数mtf曲线

在 MATLAB 中绘制光学系统的光学传递函数&#xff08;Modulation Transfer Function&#xff0c;MTF&#xff09;曲线可以通过以下步骤实现。MTF 是描述光学系统对物体细节的传递能力的函数&#xff0c;通常用于分析成像系统的性能。 假设我们有一个理想的光学系统&#xff0c…...

贵州某建筑物挡墙自动化监测

1. 项目简介 某建筑物位于贵州省某县城区内&#xff0c;靠近县城主干道&#xff0c;周边配套学校、医院、商贸城。建筑物临近凤凰湖、芙蓉江等水系&#xff0c;主打“湖景生态宜居”。改建筑物总占地面积&#xff1a;约5.3万平方米&#xff1b;总建筑面积&#xff1a;约15万平…...

自定义协议与序列化

前言 书接上回&#xff0c;我们上一篇提到了协议并且我们草率的写了一个协议&#xff0c;然后又对TCP的R和W留了一个伏笔&#xff0c;我们今天彻底做个了断。 UDP是面向数据报的&#xff0c;它要么不读&#xff0c;要么就是一次读完的&#xff0c;所以不存在数据断断续续的问…...

MySQL中的Change Buffer是什么,它有什么作用?

MySQL 中的 Change Buffer&#xff08;更改缓冲区&#xff09;是 InnoDB 存储引擎使用的一种特殊数据结构&#xff0c;主要用于优化对二级索引&#xff08;secondary indexes&#xff09;的写操作性能。 它的核心作用是&#xff1a; 当对表进行 INSERT、UPDATE 或 DELETE 操作…...

Ubuntu 20.04之Docker安装ES7.17.14和Kibana7.17.14

你需要已经安装如下运行环境: Ubuntu 20.04 docker 28 docker-compose 1.25 一、手动拉取镜像 docker pull docker.elastic.co/kibana/kibana:7.17.14docker pull docker.elastic.co/elasticsearch/elasticsearch:7.17.14 或者手动导入镜像 docker load -i es7.17.14.ta…...

ThreadLocal作一个缓存工具类

1、工具类 import java.util.HashMap; import java.util.Map;public class ThreadLocalUtil {// 使用Map存储多类型数据private static final ThreadLocal<Map<String, Object>> CONTEXT_HOLDER new ThreadLocal<>();// 存储数据public static void set(Str…...

DeepSeek在旅游行业的智能化革命

2025年,从贵州全域智慧旅游平台的行程规划革命,到黄山景区"AI旅行助手"的实时服务升级,再到宁夏"游宁AI"的全域智能导览,DeepSeek通过技术创新与行业场景的深度融合,正在重新定义"智慧旅游"的内涵。这场变革不仅体现在效率提升层面,更通过…...

说一下响应状态码有哪些?

HTTP响应状态码分类(RFC 7231标准) 1. 1xx(信息类) 临时响应,表示请求已被接收,需要继续处理 100 Continue:客户端应继续发送请求体 101 Switching Protocols:服务器同意升级协议(如WebSocket) 102 Processing(WebDAV):服务器正在处理但未完成 2. 2xx(成功类)…...

ABAP实战案例--获取当前数据由哪个用户锁住

1、业务场景&#xff1a; A用户正在打开订单&#xff0c;订单已上锁&#xff1b;B用户打开时只允许查看并提醒由哪个用户正在操作该笔订单。 2、函数使用&#xff1a;ENQUEUE_READ 代码示例&#xff1a; DATA:LV_MESSAGE TYPE SY-MSGV1,LV_UNAME TYPE UNAME.DATA:LV_GARG …...

CSS 选择器入门

一、CSS 选择器基础&#xff1a;快速掌握核心概念 什么是选择器&#xff1f; CSS 选择器就像 “网页元素的遥控器”&#xff0c;用于定位 HTML 中的特定元素并应用样式。 /* 结构&#xff1a;选择器 { 属性: 值; } */ p { color: red; } /* 选择所有<p>元素&#xff0c;…...

【深度学习新浪潮】如何入门人工智能?

入门人工智能(AI)需要结合数学基础、编程技能、机器学习理论和实践项目,逐步深入。以下是一个系统的学习路径,适合零基础或初学者参考: 一、打好基础:数学与编程 1. 数学基础(关键) AI的核心依赖数学,尤其是以下领域: 线性代数:向量、矩阵运算、特征分解等(用于…...

软件工程第六章-详细设计

文章目录 程序流程图PAD图N-S图(盒图)流图根据PDL创建流图把程序流程图映射到流图 程序流程图 PAD图 N-S图(盒图) 流图 根据PDL创建流图 把程序流程图映射到流图...

Profinet转Modbus TCP协议转换技术,打通能耗监控‘最后一公里’

在工业自动化领域&#xff0c;Profinet与Modbus TCP是两种广泛使用的通讯协议。Profinet通常用于实时性要求较高的工业控制系统&#xff0c;而Modbus TCP则因其简单、开放的特性广泛应用于各类设备的通信。然而&#xff0c;由于两者在技术规范上的差异&#xff0c;直接的互联互…...

C++:因子问题

【描述】 任给两个正整数N、M&#xff0c;求一个最小的正整数a&#xff0c;使得a和(M-a)都是N的因子。 【输入】 包括两个整数N、M。N不超过1,000,000。 【输出】 输出一个整数a&#xff0c;表示结果。如果某个案例中满足条件的正整数不存在&#xff0c;则在对应行输出-1 【样例…...

SAP系统的委外业务是什么?委外采购(标准委外)与工序外协的区别有哪些?

【SAP系统研究】 #SAP #委外 #外包 #代工 委外业务是很常见的业务类型。 企业生产过程中,会在自制生产之外,产生委外加工业务,也称之为外包或者代工。还有一些企业,自己只负责设计、市场等业务,而将生产加工环节交给其他公司。 一、委外产生的原因 有的企业由于环评、…...

小乌龟git中的推送账户、作者账户信息修改

文章目录 修改git文档作者信息修改git推送用户信息参考文献 修改git文档作者信息 小乌龟中的用户信息为&#xff1a;作者信息&#xff0c;并非推送用户。 上边用户信息&#xff0c;修改的是文件的作者信息。如果想要修改git服务中记录的推送用户信息需要修改推送用户信息。 …...

vue2.0 组件

个人简介 &#x1f468;‍&#x1f4bb;‍个人主页&#xff1a; 魔术师 &#x1f4d6;学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全栈发展 &#x1f6b4;个人状态&#xff1a; 研发工程师&#xff0c;现效力于政务服务网事业 &#x1f1e8;&#x1f1f3;人生格言&…...

5月19日笔记

BGP的路由聚合 BGP&#xff08;Border Gateway Protocol&#xff0c;边界网关协议&#xff09;是互联网中用于在不同自治系统&#xff08;AS&#xff09;之间交换路由信息的一种协议。在BGP中&#xff0c;路由聚合是一种技术&#xff0c;它允许网络管理员通过减少路由表中冗余的…...

【SPIN】PROMELA并发编程(SPIN学习系列--3)

一、active与run&#xff1a;Promela的进程创建基石 在Promela语言中&#xff0c;**active和run**是构建并发模型的核心关键字&#xff0c;分别用于定义主动进程和动态创建被动进程&#xff1a; active proctype <进程名>() 作用&#xff1a;声明主动进程类型&#xff0…...

深入理解 Redisson 看门狗机制:保障分布式锁自动续期

在分布式系统的开发中&#xff0c;分布式锁是解决资源竞争、数据一致性问题的关键手段。Redisson 作为一个在 Java 领域广泛使用的 Redis 客户端框架&#xff0c;为我们提供了功能强大且易用的分布式锁实现。其中&#xff0c;看门狗&#xff08;watchDog&#xff09;机制更是 R…...