c++高级篇(四) ——Linux下IO多路复用之epoll模型
IO多路复用 —— epoll
前言
在之前我们就已经介绍过了select和poll,在作为io多路复用的最后一个的epoll,我们来总结一下它们之间的区别:
a
select
实现原理
select 通过一个文件描述符集合(fd_set)来工作,该集合可以包含需要监控的文件描述符。调用者会指定一个超时时间,如果在这个时间内没有任何描述符准备好,则函数返回。select 可以同时监听读、写和异常三种类型的事件。
优点
select 函数调用和实现比较简单,同时它支持跨平台
缺点
-
select是基于位图这一数据结构来存储与遍历文件描述符相关的信息,由于位图数据结构的限制,select最多能同时监听1024个文件描述符,如果超过1024个文件描述符,无法实现大规模的并发处理。
-
每次调用select,需要拷贝位图,而且select属于用户态,网络通信属于内核态,需要拷贝两次,会影响select的性能。
-
select的每次监听都需要遍历一整个位图,随着需要监听的socket增加,性能会大大下降。
poll
实现原理
poll 通过一个链表来存储需要监控的文件描述符,当文件描述符就绪时,链表中的节点会被移动到就绪链表中,当链表为空时,poll会阻塞。poll与select类似,也是通过一个文件描述符集合来工作,但是poll所使用的数据结构是一个结构体数组,它的结构如下:
struct pollfd
{int fd; //存储的socketshort events; // socket触发的事件short revents; // 返回的事件
}
优点
poll 函数调用和实现简单,同时它支持跨平台,同时相对于select,poll没有了1024的限制,可以实现对更多文件描述符的监听。
缺点
poll监视的连接数没有1024的限制,但是随着socket的增多,poll的效率会降低,无法处理超大规模并发。
epoll
epoll的原理
epoll 全名是 eventpoll,是Linux内核2.5.44 版本之后才出现的一个事件通知机制。它属于IO多路复用技术的一种形式,IO多路复用技术指的是一个操作里同时监听多个输入输出源,在其中一个或多个输入输出源可用的时候返回,然后对其进行读写操作。上面的select和poll都具有一个通病,那就是都有性能瓶颈,而epoll可以承受百万级别的连接,属于select和poll模式的升级版。
epoll的优点
-
select和poll监听都是线性检测的,而epoll是基于红黑树来管理文件描述符,对于事件的发生,它不像select和poll那样需要遍历整个文件描述符集合,而是通过回调函数来通知,所以epoll的效率更高.
-
select和poll在工作中需要对集合进行判断来看哪些文件描述符已经就绪,而epoll则不需要,epoll通过回调函数来通知,所以epoll的效率更高。
当我们需要监听大量的文件描述符时,epoll的效率会更高,所以epoll是当前最常用的IO多路复用技术。
epoll的操作函数
在Linux内核中,主要给我们提供了一下三个函数来操作epoll:
#include <sys/epoll.h>
//创建一个epoll实例,通过红黑树来管理文件描述符
int epoll_create(int size);
//管理红黑树中的文件描述符(添加,修改,删除)
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
//等待文件描述符就绪
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
epoll_create
epoll_create函数用于创建一个epoll实例,参数size表示要监听的文件描述符的数量,但是这个参数在Linux 2.6.8之后已经没有意义了,因为epoll的红黑树可以动态扩展,所以大于0即可。
int epfd = epoll_create(0);
返回值:
- 成功:返回epoll实例的文件描述符
- 失败:返回-1,并设置errno
epoll_ctl
epoll_ctl函数用于管理红黑树中的文件描述符,参数epfd表示epoll实例的文件描述符,参数op表示要进行的操作,参数fd表示要监听的文件描述符,参数event表示要监听的事件。
在epoll中有以下和事件相关的数据结构
typedef union epoll_data {void *ptr;int fd; // 通常情况下使用这个成员, 和epoll_ctl的第三个参数相同即可uint32_t u32;uint64_t u64;
} epoll_data_t;struct epoll_event {uint32_t events; /* Epoll events */epoll_data_t data; /* User data variable */
};
而在epool_ctl函数中主要要注意下面几个参数;
int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);
epfd
:epoll实例的文件描述符op
:要进行的操作EPOLL_CTL_ADD
:添加文件描述符EPOLL_CTL_MOD
:修改文件描述符EPOLL_CTL_DEL
:删除文件描述符
fd
:要监听的文件描述符event
:epoll事件,用来修饰fd,指定检测什么事件events
:事件类型EPOLLIN
:读事件EPOLLOUT
: 写事件EPOLLERR
: 错误事件
-
data
:用户数据,通常情况下使用fd即可
epoll_wait
epoll_wait函数用于等待事件的发生,参数epfd表示epoll实例的文件描述符,参数events表示要监听的事件,参数maxevents表示最多监听多少个事件,参数timeout表示等待时间。
int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout);
epfd
:epoll实例的文件描述符events
:要监听的事件maxevents
:最多监听多少个事件timeout
:等待时间0
:函数不阻塞,不管epoll实例中有没有就绪的文件描述符,函数被调用后都直接返回大于0
:如果epoll实例中没有已就绪的文件描述符,函数阻塞对应的毫秒数再返回-1
:函数一直阻塞,直到epoll实例中有已就绪的文件描述符之后才解除阻塞
epoll的使用
这里为实现了一个简单的基于epoll实现的服务端与客户端通讯,大家可以自己测试一下:
//server.cpp
#include <iostream>
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <string.h>int main(int argc,char* argv[])
{if(argc!=3){std::cout<<"命令行参数数量不对"<<std::endl;std::cout<<"./demo port timeout"<<std::endl;exit(-1);}// 创建套接字int lfd=socket(AF_INET,SOCK_STREAM,0);if(lfd<0){perror("create socket");return -1;}//绑定struct sockaddr_in server_addr;memset(&server_addr,0,sizeof(server_addr));server_addr.sin_port=htons(atoi(argv[1]));server_addr.sin_family=AF_INET;server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//设置端口复用int opt=1;setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));//绑定端口int ret=bind(lfd,(struct sockaddr*)&server_addr,sizeof(server_addr));if(ret<0){perror("bind");exit(-1);}//监听ret=listen(lfd,10);if(ret<0){perror("listen");}//创建epoll实例int epfd=epoll_create(100);if(epfd<0){perror("epfd");exit(-1);}struct epoll_event ev;ev.data.fd=lfd;ev.events=EPOLLIN;ret=epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);if(ret<0){perror("epoll_ctl");}struct epoll_event evs[1024];int size=(sizeof(evs)/sizeof(struct epoll_event));while(1){int num=epoll_wait(epfd,evs,size,atoi(argv[2]));for(int i=0;i<num;i++){int fd=evs[i].data.fd;if(fd==lfd) //如果是监听的socket{int cfd=accept(fd,0,0); //接收客户端的连接ev.events=EPOLLIN;ev.data.fd=cfd;int ret=epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);if(ret<0){perror("epoll_ctl");exit(-1);}}else //不是监听的说明要接收客户端的消息{char buffer[1024];memset(buffer,0,sizeof(buffer));int len=recv(fd,buffer,sizeof(buffer)-1,0);std::cout<<len<<std::endl;if(len==0) //客户端已经断开连接{int ret=epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);if(ret<0){perror("epoll_ctl_del");exit(-1);}close(fd);}else if(len>0){std::cout<<333<<std::endl;std::cout<<"client:"<<buffer<<std::endl;char* recebuf="ok";send(fd,recebuf,sizeof(recebuf),0);}else{perror("recv");exit(-1);}}}}return 0;
}
//client.cpp
#include <iostream>
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>int main(int argc, char* argv[])
{if (argc != 3) {std::cout << "命令行参数数量不对" << std::endl;std::cout << "./client server_ip port" << std::endl;return -1;}// 创建套接字int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {perror("socket");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(atoi(argv[2]));if (inet_pton(AF_INET, argv[1], &server_addr.sin_addr) <= 0) {perror("inet_pton");close(sockfd);return -1;}// 连接到服务器if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {perror("connect");close(sockfd);return -1;}// 发送数据给服务器const char* sendbuf = "Hello from client!";ssize_t send_len = send(sockfd, sendbuf, strlen(sendbuf), 0);if (send_len < 0) {perror("send");close(sockfd);return -1;}std::cout << "Sent: " << sendbuf << std::endl;// 接收服务器的数据char buffer[1024];memset(buffer, 0, sizeof(buffer));ssize_t recv_len = recv(sockfd, buffer, sizeof(buffer)-1, 0);if (recv_len > 0) {std::cout << "Received from server: " << buffer << std::endl;} else if (recv_len == 0) {std::cout << "Server closed the connection." << std::endl;} else {perror("recv");}// 关闭套接字close(sockfd);return 0;
}
# makefile
all: client serverserver: server.cppg++ -g -o server server.cppclient: client.cppg++ -g -o client client.cpp
epoll的工作模式
epoll有两种工作模式:LT(Level Triggered,水平触发)和ET(Edge Triggered,边缘触发)。
LT模式
LT又叫水平模式,是epoll的默认工作模式。在这种模式下,内核会不断地通知你文件描述符是否就绪,即使你已经读取了数据。也就是说,即使你读取了数据,文件描述符仍然被认为是就绪的,内核会继续通知你。这种模式适用于需要不断地检查文件描述符是否就绪的场景。
水平模式主要有以下特点:
- 这是epoll的默认工作模式。
- 在LT模式下,当一个文件描述符准备好进行读写操作时,epoll会通知应用程序。
- 如果应用程序没有立即处理该事件,或者在处理过程中没有完全读取或写入所有数据,那么只要文件描述符仍然处于就绪状态,epoll将继续通知应用程序。
其实对于大多数应用来说,LT模式足够使用,并且它的行为与传统的poll和select相似
ET模式
ET又叫边缘模式,是epoll的高级模式。在这种模式下,内核只会通知你文件描述符从非就绪状态变为就绪状态一次,即使你读取了数据,文件描述符仍然被认为是非就绪的,内核不会继续通知你。这种模式适用于需要高效处理大量并发连接的场景。
边缘模式主要有以下特点:
- ET模式是一种低延迟、高性能的工作模式。
- 在ET模式下,epoll只会在状态发生变化时通知应用程序一次。例如,如果一个文件描述符从非就绪变为就绪,epoll将通知应用程序;但是,如果应用程序未能在第一次通知后立即处理完所有可用的数据,那么即使该文件描述符仍然是就绪状态,epoll也不会再次发送通知,直到该文件描述符的状态再次发生变化(即从就绪变回非就绪,再由非就绪变成就绪)。
因此,在ET模式下,应用程序必须确保每次收到通知时都尽可能多地读取或写入数据,以避免错过后续的通知。这通常意味着在循环中尽可能多地尝试读写操作,直到遇到EAGAIN或EWOULDBLOCK错误为止,这表明当前没有更多可读写的就绪数据。
PS:
- LT模式会不断通知应用程序,即使应用程序已经开始读取数据,这样可以保证应用程序能够及时处理数据,但是 频繁的通知会带来性能的损耗,而ET模式只会在状态发生变化时通知应用程序一次,因此应用程序需要确保每次收到通知时都尽可能多地读取或写入数据,以避免错过后续的通知。所以ET模式要求应用程序在每次收到通知时都尽可能多地读取或写入数据,否则可能会错过后续的通知。因此,ET模式通常需要更复杂的编程逻辑,并且对应用程序的设计和实现有更高的要求。
2.我们在使用ET模式要设置EPOLLET标志如下:
if(fd==lfd) //如果是监听的socket
{int cfd=accept(fd,0,0); //接收客户端的连接ev.events=EPOLLIN|EPOLLET; //设置边沿触发ev.data.fd=cfd;int ret=epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev);if(ret<0){perror("epoll_ctl");exit(-1);}
}
3.LT模式下支持阻塞和非阻塞,而ET模式下只支持非阻塞。
相关文章:
c++高级篇(四) ——Linux下IO多路复用之epoll模型
IO多路复用 —— epoll 前言 在之前我们就已经介绍过了select和poll,在作为io多路复用的最后一个的epoll,我们来总结一下它们之间的区别: a select 实现原理 select 通过一个文件描述符集合(fd_set)来工作,该集合可以包含需要监控的文件…...
基于Java Springboot环境保护生活App且微信小程序
一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术:Html、Css、Js、Vue、Element-ui 数据库:MySQL 后端技术:Java、Spring Boot、MyBatis 三、运行环境 开发工具:IDEA/eclipse 微信…...
.NET 9 中 LINQ 新增功能实现过程
本文介绍了.NET 9中LINQ新增功能,包括CountBy、AggregateBy和Index方法,并提供了相关代码示例和输出结果,感兴趣的朋友跟随我一起看看吧 LINQ 介绍 语言集成查询 (LINQ) 是一系列直接将查询功能集成到 C# 语言的技术统称。 数据查询历来都表示为简单的…...
【Vue3中Router使用】
Vue3中Router使用 1. 安装vue-router组件2. 建两个测试页面2.1 测试页面Home.vue2.2 测试页面Category.vue 3. 创建路由对象4. 在入口main.js中引入router把App.vue改成路由页面5. 测试5.1 关闭检查解决ESlint报错5.2 改文件名解决ESlint检查报错测试WebHashHistory 和WebHisto…...
性能测试攻略(一):需求分析
性能测试成为软件开发和运维过程中不可或缺的一环。性能测试不仅能够帮助我们了解系统在特定条件下的表现,还能帮助我们发现并解决潜在的性能问题。那么我们怎么做一次完整的性能测试呢?首先,我们需要进行需求分析,来明确我们的测…...
android WebRtc 无法推流以及拉流有视频无声音问题
最近在开发使用WebRtc进行视频通话和语音通话,我使用的设备是MTK的手机,期间后台的技术人员几乎没法提供任何帮助,只有接口和测试的web端,有遇到不能推流。推流成功网页端有画面有声音,但是安卓端有画面,没…...
Socket编程TCP
【Linux】TCP编程 实验:通过TCP通信—在客户端输入要执行的指令,接收执行结果,另服务端接收指令并执行,向客户端发送执行结果 //主函数 #include<iostream> #include<string> #include"log.hpp" #include…...
《以 C++为笔,绘就手势识别人机交互新画卷》
在科技浪潮汹涌澎湃的当下,人机交互领域正处于深刻变革的前沿阵地。从古老的命令行输入到图形化界面的鼠标点击,再到如今风靡全球的触摸操控,每一次交互方式的革新都重塑了我们与电子设备的沟通模式。而近年来,手势识别技术作为一…...
【CSS】小球旋转loading加载动画
效果 css小球旋转loading动画 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>Document<…...
Leetcode经典题6--买卖股票的最佳时机
买卖股票的最佳时机 题目描述: 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。…...
BA是什么?
目录 1.EKF的步骤 一、问题定义与模型建立 二、线性化处理 三、应用卡尔曼滤波 四、迭代与收敛 五、结果评估与优化 注意事项 2.BA问题的步骤 一、问题定义与数据准备 二、构建优化模型 三、选择优化算法 四、执行优化过程 五、结果评估与优化 六、应用与验证 1.…...
【IDEA】报错:Try to run Maven import with -U flag (force update snapshots)
问题 IDEA运行项目报错:Try to run Maven import with -U flag (force update snapshots) 原因 IDEA 的项目运行绑定的maven有问题, 解决问题 检查项目绑定的maven配置...
MATLAB提供的窗函数
加窗法 为什么使用加窗法? 在数字滤波器设计和频谱估计中,加窗函数的选择对于整体结果的质量有重大影响。加窗的主要作用是减弱因无穷级数截断而产生的吉布斯现象的影响。 windowDesigner 六种常见的窗函数 根据离散时间傅里叶变换的乘法性质&a…...
git 使用配置
新拿到机器想配置git 获取代码权限,需要的配置方法 1. git 配置用户名和邮箱 git config --global user.name xxxgit config --global user.email xxemail.com 2. 生成ssh key ssh-keygen -t rsa -C "xxemail.com" 3. 获取ssh key cat ~/.ssh/id_rsa.…...
【深度学习】深入解析长短期记忆网络(LSTMs)
长短期记忆网络(Long Short-Term Memory networks, LSTMs)是一种特殊的递归神经网络(RNN),专门设计用来解决标准 RNN 在处理长序列数据时的梯度消失和梯度爆炸问题。LSTMs 在许多序列数据任务中表现出色,如…...
vue watch和computed的区别,computed和method的区别
发现宝藏 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【宝藏入口】。 在 Vue 中,watch、computed 和 methods 都是常用的响应式功能,它们的用途和工作方式有所不同。下面分别解…...
搭建高可用负载均衡系统:Nginx 与云服务的最佳实践
搭建高可用负载均衡系统:Nginx 与云服务的最佳实践 引言 在项目开发过程中,我们通常在开发和测试阶段采用单机架构进行开发和测试。这是因为在这个阶段,系统的主要目的是功能实现和验证,单机架构足以满足开发人员的日常需求&…...
FFmpeg 4.3 音视频-多路H265监控录放C++开发十九,ffmpeg复用
封装就是将 一个h264,和一个aac文件重新封装成一个mp4文件。 这里我们的h264 和 aac都是来源于另一个mp4文件,也就是说,我们会将 in.mp4文件解封装成一路videoavstream 和 一路 audioavstream,然后 将这两路的 avstream 合并成一…...
Node.js JWT认证教程
Node.js JWT认证教程 1. 项目介绍 JSON Web Token (JWT) 是一种安全的跨域身份验证解决方案,在现代Web应用中广泛使用。本教程将详细讲解如何在Node.js中实现JWT认证。 2. 项目准备 2.1 初始化项目 # 创建项目目录 mkdir nodejs-jwt-auth cd nodejs-jwt-auth# …...
nn.utils.clip_grad_value_
nn.utils.clip_grad_value_ 是 PyTorch 中的一个函数,用于在训练过程中对模型的梯度进行裁剪,以防止梯度爆炸(gradient explosion)问题。该函数对梯度的每个元素进行裁剪,将其限制在一个指定的最大绝对值范围内。裁剪后…...
Java后端面试模板(技术面)
1、自我介绍模板 面试官您好!我是来自----大学计算机学院的一名大三学生,我的名字叫—。 在大学期间,我主要自学了一些主流的Java技术栈,其中主要包括:Java主流的框架:Spring MVC Spring Boot Spring Clou…...
【大语言模型】ACL2024论文-24 图像化歧义:Winograd Schema 挑战的视觉转变
【大语言模型】ACL2024论文-24 图像化歧义:Winograd Schema 挑战的视觉转变 目录 文章目录 【大语言模型】ACL2024论文-24 图像化歧义:Winograd Schema 挑战的视觉转变目录摘要研究背景问题与挑战如何解决核心创新点算法模型实验效果(包含重要…...
Docker 安装和使用
#Docker 安装和使用 文章目录 1. 安装2. 干掉讨厌的 sudo3. 使用镜像源3.1. 使用 upstart 的系统3.2. 使用 systemd 的系统 4. 基本使用4.1. 容器操作4.2. 镜像操作 5. 网络模式说明5.1. bridge 模式5.2. host 模式5.3. container 模式5.4. none 模式 6. 查看 Docker run 启动参…...
nginx网站服务
nginx介绍: 1、高并发,轻量级的web服务软件 2、稳定性高,系统资源消耗率低 对http的高并发处理能力高,单台物理服务器可以支持30000-50000个并发。 一般来说在工作中,单台的并发一般在20000. nginx的功能介绍&…...
MATLAB 手写判断点在多边形内外的2种方法(87)
MATLAB 手写判断点在多边形内外-方法1(87) 一、算法介绍二、算法实现1.方法1(代码+测试)2.方法2(代码+测试)三、结果一、算法介绍 手动实现两种方法,判断点在多边形的内部还是外部, 具体实现和测试代码如下,使用前请自行验证。(代码复制粘贴即可使用) 二、算法实现…...
Android SurfaceFlinger layer层级
壁纸作为显示的最底层窗口它是怎么显示的 1. SurfaceFlinger layer层级 锁屏状态dump SurfaceFlinger ,adb shell dumpsys SurfaceFlinger Display 0 (active) HWC layers: -----------------------------------------------------------------------------------…...
零基础快速掌握——【c语言基础】数组的操作,冒泡排序,选择排序
1.数组 内存空间连续: 2.定义格式 数组的定义格式: 数组分为一维数组、二维数组、以及多维数组,不同类型的数组定义格式时不一样 2.1 一维数组的定义 数据类型 数组名 [数组长度]; 解释: 数据类型࿱…...
个人IP建设:简易指南
许多个体创业者面临的一个关键挑战是如何为其企业创造稳定的需求。 作为个体创业者,您无法使用营销团队,因此许多人通过推荐和他们的网络来产生需求。因此,扩大您的网络是发展您的业务和产生持续需求的最佳策略。 这就是个人IP和品牌发挥作…...
【Unity高级】如何获取着色器(Shader)的关键词
在动态设置Shader时,会需要通过EnableKeyword, DisableKeyword来完成。但一个Shader有哪些关键词呢?Unity的文档中并没有列出来,但我们可以通过遍历Shader的KeywordSpace来查看。 1. 代码如下 using UnityEngine;public class KeywordExamp…...
OSS文件上传
1、我们这个系统对接的阿里云OSS需要先对接小鹏OSS系统获取accessKeyId、accessKeySecret,这个可以忽略 aliyun:oss:endpoint: https://oss-cn-hangzhou.aliyuncs.combucketName: xp-xpd-experiencedomain: https://xp-xpd-experience.oss-cn-hangzhou.aliyuncs.co…...
时序预测算法TimeXer代码解析
在时序预测领域,如何有效地利用外部变量(exogenous variables)来提升内部变量(endogenous variables)的预测性能一直是一个挑战。 在上一篇文章中,我结合论文为大家解读了TimeXer框架,今天&…...
【无标题】建议用坚果云直接同步zotero,其他方法已经过时,容易出现bug
created: 2024-12-06T16:07:45 (UTC 08:00) tags: [] source: https://zotero-chinese.com/user-guide/sync author: 数据与文件的同步 | Zotero 中文社区 Excerpt Zotero 中文社区,Zotero 中文维护小组,Zotero 插件,Zotero 中文 CSL 样式 数…...
Hive 分桶表的创建与填充操作详解
Hive 分桶表的创建与填充操作详解 在 Hive 数据处理中,分桶表是一个极具实用价值的功能,它相较于非分桶表能够实现更高效的采样,并且后续还可能支持诸如 Map 端连接等节省时间的操作。不过,值得注意的是,在向表写入数…...
docker怎么commit tag push?
在 Docker 中,commit、tag 和 push 是用于创建和推送自定义镜像到仓库的三个不同步骤。以下是每个命令的详细说明和使用方法: ### 1. docker commit 当你对一个运行中的容器做了修改,并希望将这些修改保存为一个新的镜像时,可以使…...
全面替换VMware,南昌大学一卡通的硬核智慧
将一昼夜分为十二时辰 是古人的博大智慧 晨光熹微,门扉轻启,负笈而行 智慧校园的“十二时辰”启幕新章 一、数字南大:一卡通打卡校园十二时辰 时辰轮转,一时有一时的使命师生们是如何高效、便捷地度过每个时辰?一张充…...
SpringMVC ,ioc和aop
IOC和AOP IOC 控制反转,将应用程序的控制权交给spring容器管理,而不是应用程序本身 1.创建一个mapper,测试用, 就写个普通方法 public class UserMapper {public void addUser(){System.out.println("dao层新增");} …...
3GPP R18 LTM(L1/L2 Triggered Mobility)是什么鬼?(三) RACH-less LTM cell switch
这篇看下RACH-less LTM cell switch。 相比于RACH-based LTM,RACH-less LTM在进行LTM cell switch之前就要先知道target cell的TA信息,进而才能进行RACH-less过程,这里一般可以通过UE自行测量或者通过RA过程获取,而这里的RA一般是通过PDCCH order过程触发。根据38.300中的描…...
Ansys Maxwell:Qi 无线充电组件
Qi 无线充电采用感应充电技术,无需物理连接器或电缆,即可将电力从充电站传输到兼容设备。由 WPC 管理的 Qi 标准确保了不同无线充电产品之间的互操作性。以下是 Qi v1.3 标准的核心功能: Qi v1.3 标准的主要特点 身份验证:确保充…...
Neo4j 图数据库安装与操作指南(以mac为例)
目录 一、安装前提条件 1.1 Java环境 1.2 Homebrew(可选) 二、下载并安装Neo4j 2.1 从官方网站下载 2.1.1 访问Neo4j的官方网站 2.1.2 使用Homebrew安装 三、配置Neo4j 3.1 设置环境变量(可选) 3.2 打开配置文件(bash_profile) 3.2.1 打开终端…...
基于MFC绘制门电路
MFC绘制门电路 1. 设计内容、方法与难点 本课题设计的内容包括了基本门电路中与门和非门的绘制、选中以及它们之间的连接。具体采用的方法是在OnDraw函数里面进行绘制,并设计元器件基类,派生出与门和非门,并组合了一个引脚类,在…...
Gitee上获取renren-fast-vue install并run dev错误处理
目的:获取一个手脚架、越简约越好、越干净越好、于是看上了renren-fast-vue… 前端:vue2 后端:jdk1.8 mysql 5.7 SpringBoot单体架构 一开始只是下载前后端项目到本地,一堆乱七八糟的错误,网上找的资料也参差不齐… …...
sdk项目的git 标记新tag的版本号
在 Git 中,tag 是用来标记某个特定的提交点(通常是发布版本或重要的里程碑)的工具。通过 git tag,你可以为版本号创建标记,帮助团队跟踪不同版本的代码。 如果你想创建一个新的版本号标签,可以按照以下步骤…...
学习日志022 -- python事件机制
作业: 1】思维导图 2】完成闹钟 main.py import sysfrom PySide6.QtCore import QTimerEvent, QTime,Qt from PySide6.QtGui import QMovie,QMouseEvent from PySide6.QtWidgets import QApplication, QWidget from Form import Ui_Formclass MyWidget(Ui_Form,Q…...
JAVA八股文-运行篇-创建项目运行(1)
前置环境搭建:jdk、maven、idea、linux环境 一、创建一个java项目 File->New->Project 二、填写基本信息 三、完成,写了一段代码 四、打包 五、本地运行,运行和debug二选一 六、上传至linux环境 七、linux环境下命令执行 7.1 指定Main方法类 …...
Vue Web开发(二)
1. 项目搭建 1.1. 首页架子搭建 使用Element ui中的Container布局容器,选择倒数第二个样式,将代码复制到Home.vue。 1.1.1.下载less (1)下载less样式 npm i less (2)下载less编辑解析器 npm i less…...
Midjourney Describe API 的对接和使用
Midjourney Describe API 的对接和使用 Midjourney Describe API 的主要功能是通过上传图片,获取对图片的描述。使用该 API,只需要传递图片文件地址,API 会返回图片的详细描述。无需繁琐的参数设置,即可获得高质量的图片描述。 …...
MySQL是怎么加锁的
1. 全局锁 1.1 什么是全局锁? 全局锁是一种一次性锁住整个数据库的锁定机制。一旦加上全局锁,整个数据库的所有表都会处于只读状态,这意味着所有修改操作(如INSERT、UPDATE、DELETE)都会被阻塞。 常用的SQL命令&…...
burp suite 5
声明! 学习视频来自B站up主 **泷羽sec** 有兴趣的师傅可以关注一下,如涉及侵权马上删除文章,笔记只是方便各位师傅的学习和探讨,文章所提到的网站以及内容,只做学习交流,其他均与本人以及泷羽sec团队无关&a…...
硬件和自驾功能
1 硬件 指令集架构 (ISA): ARM v6, v7, v8:这些是 ARM 公司设计的不同版本的指令集架构 (ISA)。ARM v6 和 v7 属于 32 位架构,而 ARM v8 则引入了 64 位支持(即 ARMv8-A)和 32 位向后兼容模式。需要强调的是ÿ…...
uviewplus中的时间单选框up-datetime-picker的在uni-app+vue3的使用方法
uviewplus中的时间单选框up-datetime-picker的使用方法 前言 在实际开发中,我们经常需要使用时间选择器来让用户选择特定的时间。本文将详细介绍uviewplus中up-datetime-picker组件的使用方法,特别是在处理年月选择时的一些关键实现,因为官方有很多相关的功能和方法…...