epoll的LT模式(水平触发)和ET模式(边沿触发)
前言
epoll的触发模式是个引发讨论非常多的话题,网络上这方面总结的文章也很多,首先从名字上就不是很统一,LT模式常被称为水平触发、电平触发、条件触发,而ET模式常被称为边缘触发、边沿触发等,这些都是从英文翻译过来的,只不过翻译的时候有些差异,LT全称 level-triggered,ET全称 edge-triggered。
虽然这个知识点热度很高,但很多人对于它的理解总是差那么一点,特别是在面试的时候,很多面试者总是处于一种回忆和背诵的状态,其实这两种模式真的不需要去死记硬背,下面说说我个人对这两种模式的理解和记忆方法。
名称的记忆
每次提到ET(边沿触发)首先映入我脑海的是大学里《数字逻辑电路》这门课程,里面会提到低电平、高电平,当电平从低到高时会有一个上升沿,而电平从高到低时会有一个下降沿,这个“沿”就是边沿触发时提到的“边沿”,跟马路边的马路牙子是同一种概念,也就是指状态变化的时候。提起上升沿和下降沿我还是印象很深的,当时我可是占用了好几节课的时间用Verilog语言写了一个显示“HELLO WORLD”的仿真波形,依靠的就是电平变化中的“沿”。
状态变化
LT模式和ET模式可以类比电平变化来学习,但是在实际应用中概念却不是完全一样的,在epoll的应用中涉及到关于IO的读写,而读写的状态变化有哪些呢?可读、不可读、可写、不可写,其实就是这四种状态而已,以socket为例。
可读:socket上有数据
不可读:socket上没有数据了
可写:socket上有空间可写
不可写:socket上无空间可写
对于水平触发模式,一个事件只要有,就会一直触发。
对于边缘触发模式,只有一个事件从无到有才会触发。
LT模式
对于读事件 EPOLLIN
,只要socket上有未读完的数据,EPOLLIN
就会一直触发;对于写事件 EPOLLOUT
,只要socket可写(一说指的是 TCP 窗口一直不饱和,我觉得是TCP缓冲区未满时,这一点还需验证),EPOLLOUT
就会一直触发。
在这种模式下,大家会认为读数据会简单一些,因为即使数据没有读完,那么下次调用epoll_wait()时,它还会通知你在上没读完的文件描述符上继续读,也就是人们常说的这种模式不用担心会丢失数据。
而写数据时,因为使用 LT 模式会一直触发 EPOLLOUT 事件,那么如果代码实现依赖于可写事件触发去发送数据,一定要在数据发送完之后移除检测可写事件,避免没有数据发送时无意义的触发。
ET模式
对于读事件 EPOLLIN
,只有socket上的数据从无到有,EPOLLIN
才会触发;对于写事件 EPOLLOUT
,只有在socket写缓冲区从不可写变为可写,EPOLLOUT
才会触发(刚刚添加事件完成调用epoll_wait时或者缓冲区从满到不满)
这种模式听起来清爽了很多,只有状态变化时才会通知,通知的次数少了自然也会引发一些问题,比如触发读事件后必须把数据收取干净,因为你不一定有下一次机会再收取数据了,即使不采用一次读取干净的方式,也要把这个激活状态记下来,后续接着处理,否则如果数据残留到下一次消息来到时就会造成延迟现象。
这种模式下写事件触发后,后续就不会再触发了,如果还需要下一次的写事件触发来驱动发送数据,就需要再次注册一次检测可写事件。
数据的读取和发送
关于数据的读比较好理解,无论是LT模式还是ET模式,监听到读事件从socket开始读数据就好了,只不过读的逻辑有些差异,LT模式下,读事件触发后,可以按需收取想要的字节数,不用把本次接收到的数据收取干净,ET模式下,读事件触发后通常需要数据一次性收取干净。
而数据的写不太容易理解,因为数据的读是对端发来数据导致的,而数据的写其实是自己的逻辑层触发的,所以在通过网络发数据时通常都不会去注册监可写事件,一般都是调用 send 或者 write 函数直接发送,如果发送过程中, 函数返回 -1,并且错误码是 EWOULDBLOCK 表明发送失败,此时才会注册监听可写事件,并将剩余的服务存入自定义的发送缓冲区中,等可写事件触发后再接着将发送缓冲区中剩余的数据发送出去。
相关视频推荐
全网最详细epoll讲解,6种epoll的设计,让你吊打面试官
epoll实战揭秘-支撑亿级IO的底层基石
epoll的具体实现与epoll线程安全,互斥锁、自旋锁、原子操作、CAS
免费学习地址:c/c++ linux服务器开发/后台架构师
需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享
代码实践
基础代码
以下为一个epoll触发模式测试的基础代码,也不算太长,直接拿来就可以测试:
#include <sys/socket.h> //for socket
#include <arpa/inet.h> //for htonl htons
#include <sys/epoll.h> //for epoll_ctl
#include <unistd.h> //for close
#include <fcntl.h> //for fcntl
#include <errno.h> //for errno
#include <iostream> //for coutclass fd_object
{
public:fd_object(int fd) { listen_fd = fd; }~fd_object() { close(listen_fd); }
private:int listen_fd;
};/*
./epoll for lt mode
and
./epoll 1 for et mode
*/
int main(int argc, char* argv[])
{//create a socket fdint listen_fd = socket(AF_INET, SOCK_STREAM, 0);if (listen_fd == -1){std::cout << "create listen socket fd error." << std::endl;return -1;}fd_object obj(listen_fd);//set socket to non-blockint socket_flag = fcntl(listen_fd, F_GETFL, 0);socket_flag |= O_NONBLOCK;if (fcntl(listen_fd, F_SETFL, socket_flag) == -1){std::cout << "set listen fd to nonblock error." << std::endl;return -1;}//init server bind infoint port = 51741;struct sockaddr_in bind_addr;bind_addr.sin_family = AF_INET;bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);bind_addr.sin_port = htons(port);if (bind(listen_fd, (struct sockaddr *)&bind_addr, sizeof(bind_addr)) == -1){std::cout << "bind listen socket fd error." << std::endl;return -1;}//start listenif (listen(listen_fd, SOMAXCONN) == -1){std::cout << "listen error." << std::endl;return -1;}elsestd::cout << "start server at port [" << port << "] with [" << (argc <= 1 ? "LT" : "ET") << "] mode." << std::endl;//create a epoll fdint epoll_fd = epoll_create(88);if (epoll_fd == -1){std::cout << "create a epoll fd error." << std::endl;return -1;}epoll_event listen_fd_event;listen_fd_event.data.fd = listen_fd;listen_fd_event.events = EPOLLIN;if (argc > 1) listen_fd_event.events |= EPOLLET;//add epoll event for listen fdif (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &listen_fd_event) == -1){std::cout << "epoll ctl error." << std::endl;return -1;}while (true){epoll_event epoll_events[1024];int n = epoll_wait(epoll_fd, epoll_events, 1024, 1000);if (n < 0)break;else if (n == 0) //timeoutcontinue;for (int i = 0; i < n; ++i){if (epoll_events[i].events & EPOLLIN)//trigger read event{if (epoll_events[i].data.fd == listen_fd){//accept a new connectionstruct sockaddr_in client_addr;socklen_t client_addr_len = sizeof(client_addr);int client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_addr_len);if (client_fd == -1)continue;socket_flag = fcntl(client_fd, F_GETFL, 0);socket_flag |= O_NONBLOCK;if (fcntl(client_fd, F_SETFL, socket_flag) == -1){close(client_fd);std::cout << "set client fd to non-block error." << std::endl;continue;}epoll_event client_fd_event;client_fd_event.data.fd = client_fd;client_fd_event.events = EPOLLIN | EPOLLOUT;if (argc > 1) client_fd_event.events |= EPOLLET;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &client_fd_event) == -1){std::cout << "add client fd to epoll fd error." << std::endl;close(client_fd);continue;}std::cout << "accept a new client fd [" << client_fd << "]." << std::endl;}else{std::cout << "EPOLLIN event triggered for client fd [" << epoll_events[i].data.fd << "]." << std::endl;char recvbuf[1024] = { 0 };int m = recv(epoll_events[i].data.fd, recvbuf, 1, 0); // only read 1 bytes when read event triggeredif (m == 0 || (m < 0 && errno != EWOULDBLOCK && errno != EINTR)){if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)std::cout << "the client fd [" << epoll_events[i].data.fd << "] disconnected." << std::endl;close(epoll_events[i].data.fd);}std::cout << "recv data from client fd [" << epoll_events[i].data.fd << "] and data is [" << recvbuf << "]." << std::endl;}}else if (epoll_events[i].events & EPOLLOUT){if (epoll_events[i].data.fd == listen_fd) //trigger write eventcontinue;std::cout << "EPOLLOUT event triggered for client fd [" << epoll_events[i].data.fd << "]." << std::endl;}}}return 0;
}
简单说下这段代码的测试方法,可以使用 g++ testepoll.cpp -o epoll
进行编译,编译后通过 ./epoll
运行为LT模式,通过 ./epoll et
模式运行为ET模式,我们用编译好的epoll程序作为服务器,使用nc命令来模拟一个客户端。
测试分类
1.编译后直接./epoll,然后在另一个命令行窗口用 nc -v 127.0.0.1 51741 命令模拟一次连接,此时 ./epoll 会产生大量的 EPOLLOUT event triggered for client fd ...,那是因为在LT模式下,EPOLLOUT会被一直触发。
albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll
start server at port [51741] with [LT] mode.
accept a new client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLOUT event triggered for client fd [5].
...
2.注释包含 EPOLLOUT event triggered for client fd 输出内容的第152行代码,编译后 ./epoll运行,然后在另一个命令行窗口用 nc -v 127.0.0.1 51741 模拟一次连接后,输入abcd回车,可以看到服务器./epoll输出内容,EPOLLIN被触发多次,每次读取一个字节。
albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll
start server at port [51741] with [LT] mode.
accept a new client fd [5].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [a].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [b].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [c].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [d].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
3.还原刚才注释的那行代码,编译后执行 ./epoll et
启动服务器,然后在另一个命令行窗口用 nc -v 127.0.0.1 51741
模拟一次连接后,然后在另一个命令行窗口用 nc -v 127.0.0.1 51741
模拟一次连接,服务器窗口显示触发了EPOLLOUT
事件
albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll et
start server at port [51741] with [ET] mode.
accept a new client fd [5].
EPOLLOUT event triggered for client fd [5].
在此基础上,从刚刚运行nc
命令的窗口中输入回车、输入回车、输出回车,那么epoll服务器窗口看到的是触发了三次EPOLLIN
事件,每次收到一个回车:
albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll et
start server at port [51741] with [ET] mode.
accept a new client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
但是如果在nc模拟的客户端里输出abcd回车,那么在epoll服务器窗口触发一次EPOLLIN事件接收到一个a之后便再也不会触发EPOLLIN了,即使你在nc客户端在此输入也没有用,那是因为在接受的缓冲区中一直还有数据,新数据来时没有出现缓冲区从空到有数据的情况,所以在ET模式下也注意这种情况。
albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll et
start server at port [51741] with [ET] mode.
accept a new client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [a].
怎么解决ET触发了一次就不再触发了
改代码呗,ET模式在连接后触发一次EPOLLOUT
,接收到数据时触发一次EPOLLIN
,如果数据没收完,以后这两个事件就再也不会被触发了,要想改变这种情况可以再次注册一下这两个事件,时机可以选择接收到数据的时候,所以可以修改这部分代码:
else
{std::cout << "EPOLLIN event triggered for client fd [" << epoll_events[i].data.fd << "]." << std::endl;char recvbuf[1024] = { 0 };int m = recv(epoll_events[i].data.fd, recvbuf, 1, 0); // only read 1 bytes when read event triggeredif (m == 0 || (m < 0 && errno != EWOULDBLOCK && errno != EINTR)){if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)std::cout << "the client fd [" << epoll_events[i].data.fd << "] disconnected." << std::endl;close(epoll_events[i].data.fd);}std::cout << "recv data from client fd [" << epoll_events[i].data.fd << "] and data is [" << recvbuf << "]." << std::endl;
}
添加再次注册的逻辑:
else
{std::cout << "EPOLLIN event triggered for client fd [" << epoll_events[i].data.fd << "]." << std::endl;char recvbuf[1024] = { 0 };int m = recv(epoll_events[i].data.fd, recvbuf, 1, 0); // only read 1 bytes when read event triggeredif (m == 0 || (m < 0 && errno != EWOULDBLOCK && errno != EINTR)){if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, epoll_events[i].data.fd, NULL) != -1)std::cout << "the client fd [" << epoll_events[i].data.fd << "] disconnected." << std::endl;close(epoll_events[i].data.fd);}epoll_event client_fd_event;client_fd_event.data.fd = epoll_events[i].data.fd;client_fd_event.events = EPOLLIN | EPOLLOUT;if (argc > 1) client_fd_event.events |= EPOLLET;epoll_ctl(epoll_fd, EPOLL_CTL_MOD, epoll_events[i].data.fd, &client_fd_event);std::cout << "recv data from client fd [" << epoll_events[i].data.fd << "] and data is [" << recvbuf << "]." << std::endl;
}
这次以./epoll et
方式启动服务器,使用nc -v 127.0.0.1 51741
模拟客户端,输入abc回车发现,epoll服务器输出显示触发的事件变了:
albert@home-pc:/mnt/d/data/cpp/testepoll$ ./epoll et
start server at port [51741] with [ET] mode.
accept a new client fd [5].
EPOLLOUT event triggered for client fd [5].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [a].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [b].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [c].
EPOLLIN event triggered for client fd [5].
recv data from client fd [5] and data is [
].
EPOLLOUT event triggered for client fd [5].
总结
- LT模式会一直触发EPOLLOUT,当缓冲区有数据时会一直触发EPOLLIN
- ET模式会在连接建立后触发一次EPOLLOUT,当收到数据时会触发一次EPOLLIN
- LT模式触发EPOLLIN时可以按需读取数据,残留了数据还会再次通知读取
- ET模式触发EPOLLIN时必须把数据读取完,否则即使来了新的数据也不会再次通知了
- LT模式的EPOLLOUT会一直触发,所以发送完数据记得删除,否则会产生大量不必要的通知
- ET模式的EPOLLOUT事件若数据未发送完需再次注册,否则不会再有发送的机会
- 通常发送网络数据时不会依赖EPOLLOUT事件,只有在缓冲区满发送失败时会注册这个事件,期待被通知后再次发送
相关文章:

epoll的LT模式(水平触发)和ET模式(边沿触发)
前言 epoll的触发模式是个引发讨论非常多的话题,网络上这方面总结的文章也很多,首先从名字上就不是很统一,LT模式常被称为水平触发、电平触发、条件触发,而ET模式常被称为边缘触发、边沿触发等,这些都是从英文翻译过来…...
Java基础面试20题
Java的八大基本数据类型 答:可以分4种类型:布尔类型(boolean),字符类型(char),浮点类型(double,float),整型(byte,short,int, long)。 String,StringBuffer与StringBuilder的区别? …...
Java面向对象封装
目录 封装的概念 封装具体实现步骤 示例代码如下 封装具体语法 封装的概念 Java封装是一种面向对象的编程方法,它的主要思想是将类的内部细节(包括数据和方法)隐藏起来,对外只提供公共的访问接口,从而保证了程序的安全性和稳定性。 封装具体实现步骤 1、声明类的成…...

队列实现图书信息管理(C语言)
文章目录Queue.hmain.cQueue.c用队列实现一个图书信息管理,这里放一下有哪些文件。(ps:我之前写的是学生信息管理,但是有人说我们的作业是写图书,就该了下内容,没有改文件名)队列是用链表实现的…...

Java开发 - 读写分离初体验
前言 上一篇中,我们介绍了主从复制,相信学过的小伙伴已经能够很好的掌握主从复制的技术,实际上也并没有那么难,虽然没有讲一主多从,多主多从的配置,但是从一主一从的配置中也很容易联想到该怎么配置&#…...

图文详解CAN Log文件 - ASC文件格式
目录 1 CAN Log文件 -- ASC文件格式 1.1 Header 1.2 版本编号 1.3 经典CAN网络中的描述 1.3.1 经典CAN Standard标准帧的描述 1.3.2 经典CAN Extended扩展帧的描述 1.3.3 CAN Remote远程帧的描述 1.3.4 CAN Error错误帧的描述 1.4 CANFD网络中的描述 1.4.1 经典CAN S…...

网络编程套接字(一)
学习任务: 我们先来认识端口号,区分好主机IP和端口号的区别,以及涉及到进程PID和端口号的区别。 然后简单认识一下TCP协议和UDP协议,这两个协议都是传输层的。接着了解什么是网络字节序,它有什么作用。然后是网络编程的…...
Mysql数据库存储过程
1、参数分类 存储过程的参数类型可以是IN、OUT和INOUT。根据这点分类如下: 1、没有参数(无参数无返回) 2、仅仅带 IN 类型(有参数无返回) 3、仅仅带 OUT 类型(无参数有返回) 4、既带 IN 又带 O…...
当我开始学习人工智能:人工智能的学派及研究目标
上课真是不认真啊,现在都写不来了作业了,真的会谢 一、人工智能的学派及其争论 1.1 对人工智能方法的争论 三个学派 符号主义 认为人的认知基元是符号,认知过程即符号操作过程。 认为人是一个物理符号系统,计算机也是一个物理符…...

Html5钢琴块游戏制作与分享(音游可玩)
当年一款手机节奏音游,相信不少人都玩过或见过。最近也是将其做了出来分享给大家。 游戏的基本玩法:点击下落的黑色方块,弹奏音乐。(下落的速度会越来越快) 可以进行试玩,手机玩起来效果会更好些。 点击…...
MySQL数据库——数据库设计概念和数据库设计步骤
数据库设计就是根据业务系统的具体需求,结合我们所选用的数据库,建立好表结构及表与表之间的管理关系,为这个业务系统构造出最优秀的数据存储模型的过程。使之能有效的对应用的数据进行存储,并高效的对已经存储的数据进行访问。 …...

【云原生】Kubernetes(k8s)之Pod概念和使用
k8s之Pod概念和使用一、Pod简介1.1、Pod的阶段(状态)1.2、容器状态二、Pod的定义2.1、restartPolicy2.2、imagePullPolicy2.3、command2.4、args2.5、resources三、Pod的使用3.1、创建并访问Pod3.2、多个应用容器3.3、Init容器3.3.1、Init容器与普通容器…...

数组(九)-- LC[316][321][402] 去除重复字母
1 移掉 K 位数字 1.1 题目描述 题目链接:https://leetcode.cn/problems/remove-k-digits/ 1.2 思路分析 这道题让我们从一个字符串数字中删除 k 个数字,使得剩下的数最小。也就说,我们要保持原来的数字的相对位置不变。 以题目中的 num1432…...

ubuntu下Thrift安装
thrift是一种常用rpc框架,工作中经常会用到,本文记录一下其安装过程。 目录 1.下载软件包 1.1thrift下载 1.2libevent下载 1.3boost下载 2.安装(注意步骤) 2.1安装libevent 2.2安装boost 2.3安装与Python2.7版本对应的py…...

读懂AUTOSAR :DiagnosticLogAndTrace DLT(四)-- API解析
一、周期调用的函数:Dlt_TxFunction 根据参数DltGeneralTrafficShapingSupport,决定如何去发送DLT消息。如果为TRUE,那需要参考参数DltLogChannelTrafficShapingBandwidth为每个Log通道设置发送带宽;如果为FALSE,那么…...

【LeetCode】剑指 Offer 56. 数组中数字出现的次数 p275 -- Java Version
1. 题目介绍(56. 数组中数字出现的次数) 面试题56.:数组中数字出现的次数, 一共分为两小题: 题目一:数组中只出现一次的两个数字题目二:数组中唯一只出现一次的数字 2. 题目1:数组中…...

Zookeeper集群 + Fafka集群
目录 第一章Zookeeper 概述 1.1.Zookeeper 定义 1.2.Zookeeper 工作机制 1.3.Zookeeper 特点 1.4.Zookeeper 数据结构 1.5.Zookeeper 应用场景 1.6.Zookeeper 原理之选举机制 1.7.部署 Zookeeper 集群 总结 第二章消息队列概述 2.1消息队列需求原因 2.2消息队列的优…...
全国青少年电子信息智能创新大赛(复赛)python·模拟四卷
目录 一、编程题 答案解析如下: 下载文档打印做题: 全国青少年电子信息智能创新大赛(复赛)python模拟四卷 一、编程题 第一题:描述 班上有学生若干名,给出每名学生的年龄《整数),求班上所有学生的平均年龄,保留到小数点后两企 输入 第一行有一个整数n (1<= n...
Redis - 介绍与使用场景
简介 Redis 的全称是 Remote Dictionary Server,是一个使用 C 语言编写的、开源的(BSD 许可)高性能非关系型(NoSQL)的键值对数据库。 Redis 的数据是存储在内存中的,所以读写速度非常快,被广泛…...

Spark SQL实战(07)-Data Sources
1 概述 Spark SQL通过DataFrame接口支持对多种数据源进行操作。 DataFrame可使用关系型变换进行操作,也可用于创建临时视图。将DataFrame注册为临时视图可以让你对其数据运行SQL查询。 本节介绍使用Spark数据源加载和保存数据的一般方法,并进一步介绍…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...

零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...

听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...

【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...

【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
第八部分:阶段项目 6:构建 React 前端应用
现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...