Linux TCP与Socket与IO多路复用(Epoll)
目录
一、背景
二、交互流程
2.1 数据流动
2.2 对象之间的关系
三、TCP
3.1 为什么需要三次握手
3.2 三次握手流程
3.3 三次握手后的产物
3.4 TCB
四、Socket
4.1 Java Socket和C++ Socket
4.2 Socket的本质
4.3 Socket和TCB的关系
4.4 通过文件描述符调用Socket的流程
五、Epoll
5.1 Epoll 结构
5.2 Epoll简要工作流程
5.3 Epoll代码
5.4 epoll_ctl过程
5.5 epoll_wait过程
5.6 水平触发(LT)与边缘触发(ET)
5.7 与Java NIO关系
一、背景
网络传输无处不在,正确理解网络传输的步骤有助于我们写出高性能的程序,也有助于我们解决程序中出现的问题。
二、交互流程
下面的不理解可以跳过 二、交互流程
2.1 数据流动
网卡 → DMA缓冲区 → 协议栈处理(IP/TCP) → TCB接收缓冲区 → recv() → 用户空间缓冲区
应用程序 → send() → 用户空间缓冲区 → 内核发送缓冲区 → 协议栈处理 → 网卡队列 → 网络
2.2 对象之间的关系
epoll
→fd
→ struct file
→ struct socket
→ struct sock
(TCB 的核心数据结构)
三、TCP
老生常谈的东西了,基本就是三次握手,但这次我会从操作系统角度,谈谈还干了什么,还包括三次握手的生成对象TCB
3.1 为什么需要三次握手
-
第一次握手(
SYN
):服务端确认客户端的发送能力正常。 -
第二次握手(
SYN-ACK
):客户端确认服务端的接收和发送能力正常。 -
第三次握手(
ACK
):服务端确认客户端的接收能力正常。 -
只有三次握手后,双方才能确保彼此能正常收发数据。
TCP三次握手发生在网络协议的传输层
3.2 三次握手流程
-
客户端发起连接
-
用户调用
connect()
,内核发送SYN报文(设置初始序列号ISN
)。 -
创建TCB(传输控制块):内核为连接分配资源(如
struct tcp_sock
),初始化序列号(ISN)、窗口大小等参数,TCB状态变为SYN_SENT
-
-
服务端响应
-
DMA写入内存:网卡通过DMA直接将报文数据(包括TCP头、IP头、以太网帧等)写入内核预分配的接收缓冲区(如
sk_buff
结构)。 -
触发软中断:随后网卡触发软中断(如Linux中的
NET_RX_SOFTIRQ
),通知内核有新的数据包需要处理。 -
创建半连接:
内核协议栈解析SYN包,创建传输控制块(TCB),初始化连接状态(如序列号、窗口大小),并将连接状态设为SYN_RCVD
(半开连接)。 -
加入半连接队列(SYN Queue):
该连接(TCB)被存入半连接队列,等待客户端确认。 -
构造SYN-ACK包:
内核生成SYN-ACK响应(设置SYN和ACK标志,分配服务器初始序列号,确认号为客户端的序列号+1)。 -
DMA发送数据:
报文通过内核协议栈封装(TCP头→IP头→MAC头),存入网卡发送缓冲区,网卡通过DMA读取并发送。、 -
启动重传定时器:
为防止丢包,内核启动定时器(默认约1秒),若未收到客户端的ACK,将重传SYN-ACK
-
-
最终确认
-
客户端收到SYN-ACK后,状态变为
ESTABLISHED
,发送ACK。 -
服务端收到ACK后,做3-6步骤的操作
- DMA与软中断:客户端的ACK包由网卡通过DMA写入内存,再次触发
NET_RX_SOFTIRQ
软中断。 - 验证ACK:内核检查ACK的合法性(确认号是否为服务器序列号+1)。
- 连接状态迁移:若验证通过,连接状态转为
ESTABLISHED
,并从半连接队列移至全连接队列(Accept Queue)。 - 通知应用层:应用通过
accept()
系统调用从全连接队列中获取新连接,开始数据传输。
-
3.3 三次握手后的产物
主要是TCB和全连接队列
全连接队列:存放已建立连接但未被accept()
取出的TCB
3.4 TCB
1. 存储连接状态信息
-
连接状态:记录 TCP 状态机的当前阶段(如
ESTABLISHED
、TIME_WAIT
、SYN_RECEIVED
等)。 -
端点信息:保存本地和远端的 IP 地址及端口号,唯一标识一个 TCP 连接。
-
序列号和确认号:维护发送和接收数据的序列号(
SEQ
)和确认号(ACK
),保证数据有序性和可靠性。
2. 完成数据传输
-
每个 TCP 连接由 四元组 唯一标识:
<本地 IP, 本地端口, 远端 IP, 远端端口>
只要四元组中任意一个元素不同,内核就会视为 不同的 TCP 连接,并为其分配独立的 TCB(传输控制块)。当应用程序通过socket fd执行
read()/write()
时,内核会通过关联的TCB完成实际的数据传输
3. 缓冲区和数据管理
-
发送缓冲区:暂存应用层待发送的数据,直到收到对方的确认。
-
接收缓冲区:存储已接收但尚未被应用层读取的数据。
4. 流量控制与窗口管理
-
滑动窗口:记录接收方的可用缓冲区大小(窗口大小),控制发送速率以避免接收方溢出。
-
发送和接收窗口:跟踪当前允许发送的数据范围和已确认的数据范围。
5. 连接生命周期管理
-
三次握手:跟踪
SYN
、SYN-ACK
、ACK
的交换过程,完成连接建立。 -
四次挥手:管理
FIN
包的交换,确保连接正常关闭或终止。
四、Socket
4.1 Java Socket和C++ Socket
Java Socket 和 C++ Socket 在 Linux 上的本质是相同的,它们的底层实现均基于 Linux 内核提供的同一套 Socket 接口。无论是 Java 的 java.net.Socket
还是 C++ 的 sys/socket.h
,最终都会通过系统调用(如 socket()
, bind()
, connect()
等)与内核交互。区别仅在于语言层面的封装和 API 设计。
既然是一样的,我们后面主要是在操作系统层级进行分析。
4.2 Socket的本质
Socket是文件描述符的一种类型。文件描述符可以表示多种资源(文件、管道、Socket等),Socket是其中用于网络通信的一种。
通过文件描述符操作Socket:Socket的读写、关闭等操作均可通过其关联的文件描述符完成:
使用通用I/O函数:如 read()
、write()
、close()
。
使用Socket专用函数:如 send()
、recv()
、bind()
、connect()
等,这些函数需要文件描述符作为参数。
4.3 Socket和TCB的关系
一一对应关系
-
每个 TCP Socket 对应一个 TCB:
-
当应用调用
socket()
创建一个 TCP Socket 后,内核会为这个 Socket 分配一个 TCB。 -
TCB 的生命周期与 Socket 绑定:Socket 被创建时 TCB 初始化,Socket 关闭时 TCB 释放。
-
-
Socket 是 TCB 的“用户态句柄”:
-
应用通过 Socket 文件描述符操作连接(如发送数据、接收数据、关闭连接),内核根据 Socket 找到对应的 TCB,修改其状态或触发协议行为(如重传、流量控制)。
-
-
发送数据:
-
应用通过
send()
写入 Socket 的数据会暂存到 TCB 的发送缓冲区,TCP 协议根据 TCB 中的窗口和拥塞控制参数决定何时发送。
-
-
接收数据:
-
内核将收到的数据存入 TCB 的接收缓冲区,应用通过
recv()
从 Socket 读取时,数据从接收缓冲区复制到用户空间。
-
每个 Socket 文件描述符(fd
)在内核中关联到一个 struct socket
结构,该结构指向对应的 struct sock
(即 TCB 的核心数据结构)。
关系链:
fd
→ struct file
→ struct socket
→ struct sock
(含接收缓冲区)。
4.4 通过文件描述符调用Socket的流程
对象级调用流程:
用户调用 read(sockfd, buf, len)↓ 通过 sockfd 找到进程文件描述符表中的 struct file↓ struct file 的 f_op->read() 调用 Socket 的具体实现(如 sock_read())↓ 内核通过 struct file 的 private_data 找到 struct socket↓ 最终操作 struct sock 的接收缓冲区(sk_receive_queue)读取数据
接收流程:
-
数据到达内核:
数据包经过网卡接收、协议栈解析(IP/TCP 层处理)后,最终存入对应 TCP 连接的 TCB 接收缓冲区。该缓冲区位于内核空间,由内核管理。 -
应用程序调用
recv()
:
当应用程序调用recv(fd, buf, len, flags)
时:-
fd
(文件描述符):关联到特定的 Socket,而该 Socket 绑定到唯一的 TCB。 -
内核操作:
内核从该 TCB 的接收缓冲区中,复制数据到用户提供的缓冲区buf
(位于用户空间)。 -
数据移除:
被成功复制的数据会从 TCB 的接收缓冲区中移除,释放空间,接收窗口(rwnd
)随之扩大。
-
-
返回结果:
-
若接收缓冲区中有数据,
recv()
立即返回实际读取的字节数。 -
若接收缓冲区为空:
-
阻塞模式:进程休眠,直到新数据到达或连接关闭。
-
非阻塞模式:立即返回错误码(如
EAGAIN
或EWOULDBLOCK
)。
-
-
Q:TCB接收缓冲区的作用是什么?
(1)数据暂存与排序
-
TCB 的接收缓冲区存储已通过 TCP 协议验证的、按序排列的数据。例如:
-
若数据包乱序到达,内核会在缓冲区中等待缺失的序列号填补后,再通知应用层读取。
-
若数据重复(如重传包),内核直接丢弃冗余数据。
-
(2)流量控制的基础
-
接收缓冲区的剩余空间决定了 TCP 的 接收窗口(
rwnd
)。此窗口通过 ACK 报文通告给发送方,控制其发送速率。 -
若缓冲区满,
rwnd=0
,发送方暂停发送,避免数据被丢弃。
(3)内核与用户空间的桥梁
-
数据从内核的 TCB 接收缓冲区到用户空间的
buf
,必须通过 拷贝(如copy_to_user()
)。
(注:零拷贝技术如splice()
或sendfile()
可绕过此步骤,但常规recv()
需要拷贝。)
发送流程:
1. 应用程序提交数据
-
send()
/write()
系统调用:
应用程序将数据写入用户空间缓冲区,调用send()
触发系统调用。 -
数据拷贝到内核:
数据从用户空间拷贝到 TCB 的发送缓冲区(内核空间)。
2. 发送缓冲区管理
-
分片与封装:
内核根据 MSS 将数据分片,添加 TCP/IP 头部,生成 TCP 段。 -
发送窗口约束:
仅允许发送窗口内的数据(已发送未确认数据 + 可发送未发送数据 ≤ 发送窗口大小)。
3. 协议栈处理
-
滑动窗口与序列号:
每个 TCP 段携带序列号(seq
),接收方据此确认数据顺序。 -
重传机制:
已发送但未确认的 TCP 段保留在发送缓冲区的重传队列中,超时(RTO)或收到重复 ACK 时触发重传。
4. 网络层与网卡发送
-
IP 层处理:
添加 IP 头部,路由选择,分片(若超过 MTU)。 -
网卡队列:
TCP 段交给网卡驱动,存入发送队列(如tx_ring
),通过 DMA 发送到网络。
五、Epoll
经过上面的介绍,我们对文件描述符、Socket、TCP有相应的了解,也明白了文件描述符是如何操作网络连接的。
那假如有大量的网络连接时,我们该如何管理呢?
目前主流的方法是I/O 多路复用,即单线程/少量线程监听多个socket事件
Epoll是Linux下的一种I/O多路复用机制,用于高效地处理大量文件描述符
5.1 Epoll 结构
epoll
的实现依赖于两个核心数据结构:红黑树(Red-Black Tree) 和 就绪队列(Ready List)。它们共同协作,使得 epoll
能够高效地管理海量文件描述符(FD)的 I/O 事件。
红黑树是 epoll
实例中用于 存储所有被监控的文件描述符(FD) 的数据结构。每个通过 epoll_ctl
添加的 FD(如套接字、管道等)都会在红黑树中注册为一个节点。
设计原因
-
高效动态操作:红黑树是一种自平衡二叉搜索树,插入、删除、查找的时间复杂度均为
O(log N)
,适合频繁增删 FD 的场景(例如 Web 服务器处理大量短连接)。 -
快速定位 FD:当某个 FD 发生事件(如可读、可写)时,内核需要快速找到该 FD 的监控信息(例如用户关注的事件类型),红黑树的特性确保了这一过程的效率。
-
避免重复注册:红黑树的唯一性保证同一个 FD 不会被重复添加,避免资源浪费。
就绪队列是 epoll
实例中用于 临时存储已触发事件的 FD 的链表结构。当某个被监控的 FD 发生事件(例如套接字收到数据),内核会将该 FD 添加到就绪队列中。
设计原因
-
快速事件通知:用户调用
epoll_wait
时,内核无需遍历所有被监控的 FD,而是直接检查就绪队列,时间复杂度接近O(1)
。 -
事件去重与合并:如果同一 FD 的多个事件连续触发(如多次可读),就绪队列会合并这些事件,避免重复通知。
-
支持边缘触发(Edge-Triggered, ET)模式:在 ET 模式下,事件仅在状态变化时触发一次,就绪队列确保事件不会被遗漏。
工作流程
-
事件触发:当某个 FD 发生事件(如数据到达),内核调用与该 FD 关联的回调函数。
-
加入就绪队列:回调函数将 FD 插入就绪队列,并标记触发的事件类型。
-
用户获取事件:用户调用
epoll_wait
时,内核将就绪队列中的事件拷贝到用户空间,并清空队列(取决于触发模式)。
5.2 Epoll简要工作流程
-
创建epoll实例
-
使用
epoll_create1()
创建epoll文件描述符。
-
-
创建并配置监听Socket
-
创建TCP Socket,设置为非阻塞模式,绑定地址并开始监听。
-
-
注册监听Socket到epoll
-
通过
epoll_ctl()
将监听Socket加入epoll监控,关注EPOLLIN
事件(新连接事件)。
-
-
事件循环
-
使用
epoll_wait()
阻塞等待事件发生。 -
遍历就绪事件列表,处理不同类型的事件:
-
新连接:接受连接,将新Socket加入epoll监控。
-
数据可读:读取数据并处理,必要时注册
EPOLLOUT
事件准备写入。 -
数据可写:发送数据,完成后取消
EPOLLOUT
监控。 -
错误/关闭:移除并关闭Socket。
-
-
-
清理资源
-
关闭所有Socket和epoll实例。
-
5.3 Epoll代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <fcntl.h>#define MAX_EVENTS 10
#define PORT 8080
#define BUFFER_SIZE 1024// 设置文件描述符为非阻塞模式
void set_nonblocking(int fd) {int flags = fcntl(fd, F_GETFL, 0);fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}int main() {int listen_fd, epoll_fd, nfds;struct epoll_event ev, events[MAX_EVENTS];struct sockaddr_in addr;// 1. 创建监听Socketlisten_fd = socket(AF_INET, SOCK_STREAM, 0);if (listen_fd == -1) {perror("socket");exit(EXIT_FAILURE);}// 绑定并监听memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_addr.s_addr = htonl(INADDR_ANY);addr.sin_port = htons(PORT);if (bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr)) {perror("bind");close(listen_fd);exit(EXIT_FAILURE);}if (listen(listen_fd, SOMAXCONN)) {perror("listen");close(listen_fd);exit(EXIT_FAILURE);}set_nonblocking(listen_fd); // 非阻塞模式// 2. 创建epoll实例epoll_fd = epoll_create1(0);if (epoll_fd == -1) {perror("epoll_create1");close(listen_fd);exit(EXIT_FAILURE);}// 3. 注册监听Socket到epollev.events = EPOLLIN; // 关注可读事件ev.data.fd = listen_fd;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &ev)) {perror("epoll_ctl: listen_fd");close(listen_fd);exit(EXIT_FAILURE);}printf("Server listening on port %d...\n", PORT);// 4. 事件循环while (1) {nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);if (nfds == -1) {perror("epoll_wait");break;}for (int i = 0; i < nfds; i++) {int fd = events[i].data.fd;// 处理新连接if (fd == listen_fd) {struct sockaddr_in client_addr;socklen_t addrlen = sizeof(client_addr);int client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &addrlen);if (client_fd == -1) {perror("accept");continue;}set_nonblocking(client_fd); // 新Socket设为非阻塞ev.events = EPOLLIN | EPOLLET; // 边缘触发模式(可选)ev.data.fd = client_fd;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) == -1) {perror("epoll_ctl: client_fd");close(client_fd);}printf("New connection: fd %d\n", client_fd);// 处理数据可读} else if (events[i].events & EPOLLIN) {char buffer[BUFFER_SIZE];ssize_t nread = recv(fd, buffer, BUFFER_SIZE, 0);if (nread > 0) {printf("Received from fd %d: %.*s\n", fd, (int)nread, buffer);// 回显数据(示例)send(fd, buffer, nread, 0);} else if (nread == 0 || (nread == -1 && errno != EAGAIN)) {// 关闭连接printf("Closing fd %d\n", fd);epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);close(fd);}}// 处理其他事件(如EPOLLOUT、EPOLLERR等)}}// 清理资源close(listen_fd);close(epoll_fd);return 0;
}
5.4 epoll_ctl过程
当通过 epoll_ctl(EPOLL_CTL_ADD)
将一个fd(如Socket)添加到epoll实例时,内核会执行以下操作:
(1) 注册回调函数到fd的等待队列
-
为fd创建epoll条目(
epitem
):包含fd的信息和关注的事件(如EPOLLIN
)。 -
将回调函数
ep_poll_callback
注册到fd的等待队列:-
调用fd的
poll
方法(如Socket的sock_poll
)。 -
poll
方法将ep_poll_callback
添加到fd的等待队列中。
-
(2) 事件触发时的回调流程
-
数据到达触发硬件中断:网卡接收数据后,内核协议栈处理数据并标记Socket为可读。
-
唤醒等待队列:
-
内核调用Socket等待队列中的回调函数
ep_poll_callback
。 -
ep_poll_callback
将对应的fd(封装为epitem
)加入epoll的就绪队列(rdllist
)。
-
-
通知用户程序:
-
如果用户程序阻塞在
epoll_wait
,内核唤醒该线程,使其从epoll_wait
返回并处理就绪事件。
-
5.5 epoll_wait过程
epoll_wait
是 epoll
机制的核心函数,它负责 等待并获取已就绪的事件。
步骤 1:进入内核态
-
用户程序调用
epoll_wait
时,会从用户态切换到内核态。 -
内核访问
epoll
实例的数据结构(包括 红黑树 和 就绪队列)。
步骤 2:检查就绪队列
-
epoll
实例维护一个 就绪队列(ready list),其中保存所有已触发事件的fd。 -
如果就绪队列非空,内核直接从中取出事件,填充到用户空间的
events
数组。 -
如果队列为空,且
timeout=-1
,线程阻塞在此处,直到有新事件到来或信号中断。
步骤 3:监控fd状态(若就绪队列为空)
-
若就绪队列为空,内核通过 回调机制 监控所有注册的fd:
-
当某个fd发生事件(如socket接收数据),内核会将该fd添加到就绪队列。
-
这一过程由内核的 事件驱动机制 实现,无需轮询所有fd,效率极高。
-
步骤 4:返回就绪事件
-
将就绪队列中的事件复制到用户空间的
events
数组。 -
返回就绪事件的数量
nfds
,用户程序通过遍历events[0..nfds-1]
处理事件。 -
如果此时有线程阻塞在
epoll_wait
,内核会唤醒该线程
5.6 水平触发(LT)与边缘触发(ET)
行为 | 水平触发(LT) | 边缘触发(ET) |
---|---|---|
触发时机 | 只要缓冲区有数据/可写,持续触发 | 仅在缓冲区状态变化时触发一次(如新数据到达) |
数据未读尽的后果 | 下次 epoll_wait 继续报告事件 | 不再触发事件,可能导致数据滞留 |
编程复杂度 | 较低(无需一次性处理所有数据) | 较高(需循环读写至 EAGAIN ) |
适用场景 | 简单场景、小数据量 | 高性能场景、需精细化控制 |
5.7 与Java NIO关系
Java NIO 在不同的操作系统和 JDK 版本中确实会使用不同的底层实现,其中在 Linux 系统上,Java NIO 的 Selector
(多路复用机制)默认是基于 epoll
实现的
相关文章:
Linux TCP与Socket与IO多路复用(Epoll)
目录 一、背景 二、交互流程 2.1 数据流动 2.2 对象之间的关系 三、TCP 3.1 为什么需要三次握手 3.2 三次握手流程 3.3 三次握手后的产物 3.4 TCB 四、Socket 4.1 Java Socket和C Socket 4.2 Socket的本质 4.3 Socket和TCB的关系 4.4 通过文件描述符调用Socket的…...
LINUX安装运行jeelowcode后端项目(命令行)
环境准备 运行环境:JDK1.8开发工具: Idea、Maven默认已启动中间件:(推荐使用宝塔)Mysql8.0、Redis、Minio第一步:下载JeelowCode项目并导入IDEA中 第二步:导入数据库文件到mysql中,…...

嵌入式高级工程师面试全解:从 malloc 到 kernel panic 的系统知识梳理
在嵌入式和操作系统方向的技术面试中,常常会涉及一系列关于内存管理、虚拟化、系统权限、调试工具、外设通信等方面的问题。本文将基于一次真实的高级嵌入式工程师岗位面试问题,整理并详解所有相关技术点,作为一份结构清晰、知识全面的学习资…...
机器学习第二十七讲:Kaggle → 参加机器学习界的奥林匹克
机器学习第二十七讲:Kaggle → 参加机器学习界的奥林匹克 资料取自《零基础学机器学习》。 查看总目录:学习大纲 关于DeepSeek本地部署指南可以看下我之前写的文章:DeepSeek R1本地与线上满血版部署:超详细手把手指南 Kaggle详解…...

C++(初阶)(二十)——封装实现set和map
二十,封装实现set和map 二十,封装实现set和map1,参数类型2,比较方式3,迭代器3.1,普通迭代器3.2,const迭代器3.3,set_map的迭代器实现 4,插入和查找5,特别的&a…...

【MySQL】06.内置函数
1. 聚合函数 -- 统计表中的人数 -- 使用 * 做统计,不受 NULL 影响 mysql> select count(*) 人数 from exam_result; -------- | 人数 | -------- | 5 | -------- 1 row in set (0.01 sec)-- 使用表达式做统计 mysql> select count(name) 人数 from ex…...

企业微信内部网页开发流程笔记
背景 基于ai实现企微侧边栏和工作台快速问答小助,需要h5开发,因为流程不清楚摸索半天,所以记录一下 一、网页授权登录 1. 配置步骤 1.1 设置可信域名 登录企业微信管理后台 进入"应用管理" > 选择开发的具体应用 > “网…...

智慧在线判题OJ系统项目总体,包含功能开发思路,内部中间件,已经部分知识点
目录 回顾一下xml文件怎么写 哪个地方使用了哪个技术 MyBatis-Plus-oj的表结构设计, 管理员登录功能 Swagger Apifox编辑 BCrypt 日志框架引入(slf4jlogback) nacos Swagger无法被所有微服务获取到修改的原因 身份认证三种方式: JWT(Json Web Json,一…...

【MySQL】2-MySQL索引P2-执行计划
欢迎来到啾啾的博客🐱。 记录学习点滴。分享工作思考和实用技巧,偶尔也分享一些杂谈💬。 有很多很多不足的地方,欢迎评论交流,感谢您的阅读和评论😄。 目录 EXPLAINexplain output 执行计划输出解释重点typ…...

云电脑显卡性能终极对决:ToDesk云电脑/顺网云/海马云,谁才是4K游戏之王?
一、引言 1.1 云电脑的算力革命 云电脑与传统PC的算力供给差异 传统PC的算力构建依赖用户一次性配置本地硬件,特别是CPU与显卡(GPU)。而在高性能计算和游戏图形渲染等任务中,GPU的能力往往成为决定体验上限的核心因素。随着游戏分…...
11 接口自动化-框架封装之统一请求封装和接口关联封装
文章目录 一、框架封装1、统一请求封装和路径处理2、接口关联封装 二、简单封装代码实现config.yml - 放入一些配置数据yaml_util.py - 处理 yaml 数据requests_util.py - 将请求封装在同一个方法中test_tag.py - 测试用例执行conftest.py - 会话之前清除数据 一、框架封装 1、…...

influxdb时序数据库
以下概念及操作均来自influxdb2 官方文档 InfluxDB2 is the platform purpose-built to collect, store, process and visualize time series data. Time series data is a sequence of data points indexed in time order. Data points typically consist of successive meas…...

OpenCV CUDA模块图像处理------颜色空间处理之用于执行伽马校正(Gamma Correction)函数gammaCorrection()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::cuda::gammaCorrection 是 OpenCV 的 CUDA 模块中用于执行伽马校正(Gamma Correction)的一个函数。伽马校正通常用于…...
机器学习10-随机森林
随机森林学习笔记 一、随机森林简介 随机森林(Random Forest)是一种集成学习算法,基于决策树构建模型。它通过组合多个决策树的结果来提高模型的准确性和稳定性。随机森林的核心思想是利用“集成”的方式,将多个弱学习器组合成一…...

商品条形码查询接口如何用C#进行调用?
一、什么是商品条码查询接口? 1974年6月26日,美国俄亥俄州的一家超市首次使用商品条码完成结算,标志着商品条码正式进入商业应用领域。这项技术通过自动识别和数据采集,极大提升了零售行业的作业效率,减少了人工录入错…...
编译pg_duckdb步骤
1. 要求cmake的版本要高于3.17,可以通过下载最新的cmake的程序,然后设置.bash_profile的PATH环境变量,将最新的cmake的bin目录放到PATH环境变量的最前面 2. g的版本要支持c17标准,否则会报 error ‘invoke_result in namespace ‘…...

多模态大语言模型arxiv论文略读(九十一)
FineCLIPER: Multi-modal Fine-grained CLIP for Dynamic Facial Expression Recognition with AdaptERs ➡️ 论文标题:FineCLIPER: Multi-modal Fine-grained CLIP for Dynamic Facial Expression Recognition with AdaptERs ➡️ 论文作者:Haodong C…...

攻防世界 - MISCall
下载得到一个没有后缀的文件,把文件放到kali里面用file命令查看 发现是bzip2文件 解压 变成了.out文件 查看发现了一个压缩包 将其解压 发现存在.git目录和一个flag.txt,flag.txt是假的 恢复git隐藏文件 查看发现是将flag.txt中内容读取出来然后进行s…...
数据结构测试模拟题(2)
1、选择排序(输出过程) #include <iostream> using namespace std;int main() {int a[11]; // 用a[1]到a[10]来存储输入// 读取10个整数for(int i 1; i < 10; i) {cin >> a[i];}// 选择排序过程(只需9轮)for(int…...

在PyTorch中,对于一个张量,如何快速为多个元素赋值相同的值
我们以“a torch.arange(12).reshape((3, -1))”为例,a里面现在是: 如果我们想让a的右下角的2行3列的元素都为10的话,可以如何快速实现呢? 我们可以用到索引和切片技术,执行如下的指令即可达到目标: a[1…...

苍穹外卖--Redis
1.Redis入门 1.1Redis简介 Redis是一个基于内存的key-value结果数据库 基于内存存储,读写性能高 适合存储热点数据(热点商品、资讯、新闻) 企业应用广泛 Redis的Windows版属于绿色软件,直接解压即可使用,解压后目录结构如下:…...
C++ 条件变量虚假唤醒问题的解决
在 C 中,std::condition_variable 的 wait 和 wait_for 方法除了可以传入一个锁(std::unique_lock),还可以传入一个谓词函数(函数或可调用对象)。这个谓词的作用是让条件变量在特定的条件满足时才退出等待。…...

深度学习————注意力机制模块
关于注意力机制我自己的一点理解:建立各个维度数据之间的关系,就是对已经处理为特征图的数据,将其他影响因素去除(比如通道注意力,就将空间部分的影响因素消除或者减到极小)再对特征图进行以此特征提取 以此…...
openssl 使用生成key pem
好的,以下是完整的步骤,帮助你在 Windows 系统中使用 OpenSSL 生成私钥(key)和 PEM 文件。假设你的 openssl.cnf 配置文件位于桌面。 步骤 1:打开命令提示符 按 Win R 键,打开“运行”对话框。输入 cmd&…...

python:基础爬虫、搭建简易网站
一、基础爬虫代码: # 导包 import requests # 从指定网址爬取数据 response requests.get("http://192.168.34.57:8080") print(response) # 获取数据 print(response.text)二、使用FastAPI快速搭建网站: # TODO FastAPI 是一个现代化、快速…...

好坏质检分类实战(异常数据检测、降维、KNN模型分类、混淆矩阵进行模型评估)
任务 好坏质检分类实战 task: 1、基于 data_class_raw.csv 数据,根据高斯分布概率密度函数,寻找异常点并剔除 2、基于 data_class_processed.csv 数据,进行 PCA 处理,确定重要数据维度及成分 3、完成数据分离,数据分离…...
链表:数据结构的灵动舞者
在数据结构的舞台之上,链表以它灵动的身姿演绎着数据的精彩故事。与顺序表的规整有序不同,链表展现出了别样的灵活性与独特魅力。今天,就让我们一同走进链表的世界,去领略它的定义、结构、操作,对比它与顺序表的优缺点…...

YOLOv4:目标检测的新标杆
引言 YOLO(You Only Look Once)系列作为目标检测领域的经典算法,以其高效的检测速度和良好的准确率闻名。2020年推出的YOLOv4在保持YOLO系列高速检测特点的同时,通过引入多项创新技术,将检测性能提升到了新高度。本文将详细介绍YOLOv4的核心…...
PyTorch 2.1新特性:TorchDynamo如何实现30%训练加速(原理+自定义编译器开发)
一、PyTorch 2.1动态编译架构演进 PyTorch 2.1的发布标志着深度学习框架进入动态编译新纪元。其核心创新点TorchDynamo通过字节码即时重写技术,将Python动态性与静态图优化完美结合。相较于传统JIT方案,TorchDynamo实现了零侵入式加速——开发者只需添加…...

LabVIEW通用测控平台设计
基于 LabVIEW 图形化编程环境,设计了一套适用于工业自动化、科研测试领域的通用测控平台。通过整合研华、NI等品牌硬件,实现多类型数据采集、实时控制及可视化管理。平台采用模块化架构,支持硬件灵活扩展,解决了传统测控系统开发周…...