Node.js技术原理分析系列——如何在Node.js中新增一个内置模块
本文由体验技术团队曹杨毅原创。
Node.js 是一个开源的、跨平台的JavaScript运行时环境,它允许开发者在服务器端运行JavaScript代码。Node.js 是基于Chrome V8引擎构建的,专为高性能、高并发的网络应用而设计,广泛应用于构建服务器端应用程序、网络应用、命令行工具等。
本系列将分为9篇文章为大家介绍 Node.js 技术原理:从调试能力分析到内置模块新增,从性能分析工具 perf_hooks 的用法到 Chrome DevTools 的性能问题剖析,再到 ABI 稳定的理解、基于 V8 封装 JavaScript 运行时、模块加载方式探究、内置模块外置以及 Node.js addon 的全面解读等主题,每一篇都干货满满。
本文内容为本系列第2篇,以下为正文内容。
前言
作为 Node.js 的使用者,想必同学们对“内置模块”这个概念并不陌生:Node.js 内置模块也叫核心模块,预置在 Node.js 运行时中,这些内置模块不需要额外下载安装,可以在 js 代码中通过 require 引入。
常用的内置模块包括 fs、http 等
const fs = require("fs");
const http = require("http");
const url = require("url");
对于这些天天使用的功能,大家有没有好奇过它们是怎么开发出来的呢?
为了更好地了解 Node.js 的底层实现,我把这套流程自己走了一遍。这里可以跟大家分享一下:如何基于 Node.js 开源代码定制开发,添加一组新的内置模块。
所有复杂的功能都是建立在简单的基础之上的,因此我们用作演示的这组内置模块不需要太复杂。我们就实现一个往标准输出流输出信息的功能吧,类似于 console.log() 功能。
我们要实现的效果是这样的:我们的内置模块开发完成之后,我们可以通过这样的一段 js 代码对它进行调用
const my_console = require("my_console");
class a {constructor() {this.a = 1;}
}
let b = new a();
arr = [1, 2, 3, 4, 5];
my_console.log("Hello World!", 111111, b, arr);
调用的结果是这样的
实现步骤
1、编写核心源码
我们选用 Node.js 22.7.0 源码进行改造,在 Node.js 的 src 文件夹下新增一个源码文件 my_console.cc,在这个文件中编写我们的 C++ 业务代码,实现我们上面提到的功能。
首先引入我们所需要使用的头文件。
为了让我们的程序具备一定的跨平台能力,我们在这里还写了一些条件编译宏,根据平台的不同而使用不同的头文件。
#include "env-inl.h"
#include "node_external_reference.h"
#include "string_bytes.h"#ifdef __MINGW32__
# include <io.h>
#endif // __MINGW32__#ifdef __POSIX__
# include <climits> // PATH_MAX on Solaris.
#endif // __POSIX__#include <array>
#include <cerrno>
#include <cstring>
在编写业务代码之前,我们需要先定义一个命名空间(嵌套在 node 命名空间下,Node.js 里面现有的内置模块普遍遵循这个习惯)
namespace node {
namespace my_console {
由于我们要使用一些来自其他命名空间的数据类型,所以我们将它们引入进来
using v8::Array;
usingv8::ArrayBuffer;
usingv8::Boolean;
usingv8::Context;
usingv8::Float64Array;
usingv8::FunctionCallbackInfo;
usingv8::Int32;
usingv8::Integer;
usingv8::Isolate;
usingv8::Local;
定义输出内容到控制台的函数 Log ,并且在函数中初始化我们要输出到控制台的字符串 str
static void Log(const FunctionCallbackInfo<Value>& args) {Isolate* isolate = args.GetIsolate();Local<Context> context = isolate->GetCurrentContext();std::string str = "";
接下来是实现函数的内容:根据入参类型的不同,进行相应的处理,最后将其格式化输出到控制台
for(int i = 0; i < args.Length(); i++) {Local<Value> arg = args[i];if(arg.IsEmpty()) {str += "undefined";} elseif(arg->IsNull()) {str += "null";} elseif(arg->IsTrue()) {str += "true";} elseif(arg->IsFalse()) {str += "false";} elseif(arg->IsInt32()) {str += std::to_string(arg->Int32Value(context).ToChecked());} elseif(arg->IsNumber()) {str += std::to_string(arg->NumberValue(context).ToChecked());} elseif(arg->IsString()) {String::Utf8Valuevalue(isolate, arg);str += *value;} elseif(arg->IsArray()) {Local<Array> array = Local<Array>::Cast(arg);str += "[";for(int i = 0; i < array->Length(); i++) {if(i > 0) {str += ", ";}Local<Value> element = array->Get(context, i).ToLocalChecked();if(element->IsInt32()) {str += std::to_string(element->Int32Value(context).ToChecked());} elseif(element->IsNumber()) {str += std::to_string(element->NumberValue(context).ToChecked());} elseif(element->IsString()) {String::Utf8Valuevalue(isolate, element);str += *value;}}str += "]";
} elseif(arg->IsObject()) {v8::Local<v8::String> tmp = v8::JSON::Stringify(context, arg).ToLocalChecked();v8::String::Utf8Valuevalue(isolate, tmp);str += *value;
}
}
接下来就是我们的输出语句,这里我们多打印一些东西,把参数个数和内容都给打印出来
int length = args.Length();printf("Number of arguments: %d\n", length);printf("content: %s\n", str.c_str());
}
最后我们把 Initialize
和 RegisterExternalReferences
这 2 个函数实现出来,要注册一个模块必须要有这 2 个函数。
都是一些模板化的代码了,照着原有的模块复制一份进行修改就行
void Initialize(Local<Object> target, Local<Value> unused, Local<Context> context, void* priv) {
Environment* env = Environment::GetCurrent(context);
SetMethod(context, target, "log", Log);target->Set(context,FIXED_ONE_BYTE_STRING(env->isolate(), "isBigEndian"),Boolean::New(env->isolate(), IsBigEndian())).Check();
}voidRegisterExternalReferences(ExternalReferenceRegistry* registry) {registry->Register(Log);
}} // namespace my_console
} // namespace nodeNODE_BINDING_CONTEXT_AWARE_INTERNAL(my_console, node::my_console::Initialize)
NODE_BINDING_EXTERNAL_REFERENCE(my_console, node::my_console::RegisterExternalReferences)
现在核心的源码文件已经处理完成,接下来处理一些外围的文件。
2、处理外围文件
为了让模块能正确注册,我们需要改 NODE_BUILTIN_STANDARD_BINDINGS 这个宏,把我们新增的模块也填进去
src/node_binding.cc
#define NODE_BUILTIN_STANDARD_BINDINGS(V) \V(async_context_frame) \// ...... 为节省篇幅,省略中间内容V(worker) \V(zlib) \V(my_console) // 新增 my_console
所有的内置模块都会在 C++ 的基础上加一个 js 的封装层(哪怕你不需要封装任何操作),这样它才能被用户以 require 语句导入,这个步骤我们也不能省略。
lib/my_console.js
'use strict';
const my_console = internalBinding('my_console');
module.exports = {log: my_console.log
};
改完代码文件之后,最后还要修改构建配置文件。在 node_sources
字段中添加一项:'src/my_console.cc'
。只有这样做,我们新增的这个源码才会被编到 node 里面。
node.gyp
'src/tty_wrap.cc',
'src/udp_wrap.cc',
'src/util.cc',
'src/uv.cc',
'src/my_console.cc', # 新增这一项
到这里,涉及的 4 个文件均已完成新增/修改。
编译
编译环境: ubuntu 22.04 x64
编译命令:
./configure
make
make install
验证结果
执行测试文件
node mytest/test.js
测试成功
完整代码
完整的修改内容可以查看这个 commit: https://github.com/caoyangyicn/my_console/commit/17b418a4daedd3639116fcc9ea1afcdadc1964d5
下一节,我们将讲解 Node.js 的 perf_hooks 模块作用和用法,请大家持续关注本系列内容~学习完本系列,你将获得:
- 提升调试与性能优化能力
- 深入理解模块化与扩展机制
- 探索底层技术与定制化能力
关于OpenTiny
欢迎加入 OpenTiny 开源社区。添加微信小助手:opentiny-official 一起参与交流前端技术~
OpenTiny 官网:https://opentiny.design
OpenTiny 代码仓库:https://github.com/opentiny
TinyVue 源码:https://github.com/opentiny/tiny-vue
TinyEngine 源码: https://github.com/opentiny/tiny-engine
欢迎进入代码仓库 Star🌟TinyEngine、TinyVue、TinyNG、TinyCLI~ 如果你也想要共建,可以进入代码仓库,找到 good first issue标签,一起参与开源贡献~
相关文章:
Node.js技术原理分析系列——如何在Node.js中新增一个内置模块
本文由体验技术团队曹杨毅原创。 Node.js 是一个开源的、跨平台的JavaScript运行时环境,它允许开发者在服务器端运行JavaScript代码。Node.js 是基于Chrome V8引擎构建的,专为高性能、高并发的网络应用而设计,广泛应用于构建服务器端应用程序…...
从低清到4K的魔法:FlashVideo突破高分辨率视频生成计算瓶颈(港大港中文字节)
论文链接:https://arxiv.org/pdf/2502.05179 项目链接:https://github.com/FoundationVision/FlashVideo 亮点直击 提出了 FlashVideo,一种将视频生成解耦为两个目标的方法:提示匹配度和视觉质量。通过在两个阶段分别调整模型规模…...
康耐视CAM-CIC-10MR-10-GC工业相机
康耐视(COGNEX)的工业相机CAM-CIC-10MR-10-GC是CAM-CIC-10MR系列中的一款型号,主要应用于工业自动化检测和高精度视觉系统 基本参数与特性 分辨率与帧率: CAM-CIC-10MR-10-GC属于康耐视CIC系列,具备10MP(1000万像素)的分辨能力,帧率为10fps。该系列相机支持卷帘快门(R…...
解惑Python:一文解决osgeo库安装失败问题
Osgeo(Open Source Geospatial Foundation)是一个支持开源地理空间数据处理的基金会,我们可以在python中使用“osgeo”库来访问其提供的高效地理空间数据。例如,我们使用该模块提供的GDAL处理栅格数据,使用OGR处理矢量…...
3、树莓派5 安装VNC查看器 开启VNC服务器
在前序文章中( 2、树莓派5第一次开机),可以使用三种方式开机,其中使用网线及wifi的方式均需要使用到VNC查看器进行远程桌面控制,本文将介绍如何下载安装并配置及使用VNC查看器及服务器,对前序文章做一些补充…...
Django 创建表时 “__str__ ”方法的使用
在 Django 模型中,__str__ 方法是一个 Python 特殊方法(也称为“魔术方法”),用于定义对象的字符串表示形式。它的作用是控制当对象被转换为字符串时,应该返回什么样的内容。 示例: 我在初学ModelForm时尝…...
STM32 CAN过滤器配置和应用方法介绍
目录 概述 一、CAN过滤器核心概念 二、过滤器配置步骤(以标准ID为例) 三、不同模式的配置示例 四、高级配置技巧 五、调试与问题排查 六、关键计算公式 总结 概述 在STM32微控制器中,CAN过滤器可以配置为标识符屏蔽模式和标识符列表模…...
【第1章:深度学习概览——1.3 深度学习的核心组件与概念解析之神经网络基础】
大家好!今天咱们一头扎进深度学习的神秘领域,好好探索一下其最重要的基石 —— 神经网络。不管你是深度学习的新手小白,还是已经接触过一些基础概念,这篇文章都能助力你更透彻地理解神经网络的原理和运作机制。咱们从最基础的知识入手,一步步揭开神经网络的神秘面纱! 一、…...
Python中如何进行数据库连接?
在 Python 中进行数据库连接,不同的数据库需要使用不同的库。下面分别介绍几种常见数据库(SQLite、MySQL、PostgreSQL)的连接方法。 1. 连接 SQLite 数据库 SQLite 是一种轻量级的嵌入式数据库,Python 标准库中自带了sqlite3模块…...
解析 WebGPU 中 device.createBuffer 的参数意义
在 WebGPU 开发里,device.createBuffer 方法扮演着至关重要的角色,它用于创建一个 GPU 缓冲区对象,这个对象能够存储顶点数据、索引数据、统一数据等。下面我们就来详细剖析该方法各个参数的意义。 1. size:决定缓冲区容量 size …...
PLC的集成RAM,存储器卡,用户程序存储空间,数据存储容量分别指的什么,有什么关联?
1. 集成RAM 定义:集成RAM(随机存取存储器)是PLC内部的高速易失性存储器,用于临时存储运行时的数据(如输入/输出状态、中间变量、计数器/定时器的当前值等)。 特点: 易失性:断电后数…...
BFS-FloodFill 算法 解决最短路问题 多源 解决拓扑排序
文章目录 一、FloodFill 算法[733. 图像渲染](https://leetcode.cn/problems/flood-fill/description/)2.思路3.代码 [200. 岛屿数量](https://leetcode.cn/problems/number-of-islands/description/)2.思路3.代码 [LCR 105. 岛屿的最大面积](https://leetcode.cn/problems/ZL6…...
USB2.03.0接口区分usb top工具使用
一. USB2.0 & 3.0接口支持区分 1.1. 颜色判断 USB接口的颜色并不是判断版本的可靠标准,但根据行业常见规范分析如下: USB接口颜色与版本对照表: 接口颜色常见版本内部触点数量传输速度黑色USB2.04触点480 Mbps (60 MB/s)白色USB2.0(多…...
2025百度快排技术分析:模拟点击与发包算法的背后原理
一晃做SEO已经15年了,2025年还有人问我如何做百度快速排名,我能给出的答案就是:做好内容的前提下,多刷刷吧!百度的SEO排名算法一直是众多SEO从业者研究的重点,模拟算法、点击算法和发包算法是百度快速排名的…...
idea 2019.3常用插件
idea 2019.3常用插件 文档 idea 2019.3常用插件idea 2023.3.7常用插件 idea 2019.3常用插件 插件名称插件版本说明1AceJump3.5.9AceJump允许您快速将插入符号导航到编辑器中可见的任何位置。只需按“ctrl;”,键入一个字符,然后在Ace Jump…...
【Python 学习 / 5】函数详解(定义、参数、作用域、lambda、内置函数)
文章目录 一、函数1. 定义函数1.1 基本函数定义1.2 带参数的函数1.3 带返回值的函数 2. 参数传递2.1 位置参数2.2 默认参数2.3 可变参数2.3.1 使用*args2.3.2 使用**kwargs 2.4 参数的混合使用 3. 作用域3.1 局部和全局变量3.2 global 关键字输出: 3.3 nonlocal关键…...
WPF7-数据绑定基础
1. WPF数据绑定试验 1.1. 数据绑定的核心实现1.2. {Binding}语法1.3. 理解 DataContext 1. WPF数据绑定试验 以下是一个简单的 WPF 数据绑定示例,使用两个TextBox控件分别表示Name和Age来进行进行数据绑定试验。 数据模型类 创建一个 Person 类,包含…...
http 与 https 的区别?
HTTP(超文本传输协议)和 HTTPS(安全超文本传输协议)是互联网通信的基础协议。随着网络技术的发展和安全需求的提升,HTTPS变得越来越重要。本文将深入探讨HTTP与HTTPS之间的区别,包括其工作原理、安全性、性能、应用场景及未来发展等。 1. HTTP与HTTPS的基本概念 1.1 HT…...
大数据学习(49) - Flink按键分区状态(Keyed State)
&&大数据学习&& 🔥系列专栏: 👑哲学语录: 承认自己的无知,乃是开启智慧的大门 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一下博主哦ᾑ…...
IP 路由基础 | 路由条目生成 / 路由表内信息获取
注:本文为 “IP 路由” 相关文章合辑。 未整理去重。 IP 路由基础 秦同学学学已于 2022-04-09 18:44:20 修改 一. IP 路由产生背景 我们都知道 IP 地址可以标识网络中的一个节点,并且每个 IP 地址都有自己的网段,各个网段并不相同…...
COBOL语言的移动应用开发
COBOL语言的移动应用开发探讨 引言 在信息技术快速发展的今天,移动应用开发已成为各行各业不可或缺的一部分。许多企业和开发者纷纷转向使用新兴的编程语言和开发工具,以满足不断变化的用户需求。然而,作为一种历史悠久的编程语言ÿ…...
TCP协议(Transmission Control Protocol)
TCP协议,即传输控制协议,其最大的特征就是对传输的数据进行可靠、高效的控制,其段格式如下: 源端口和目的端口号表示数据从哪个进程来,到哪个进程去,四位报头长度表示的是TCP头部有多少个4字节,…...
C语言数组之二维数组
C语言 主要内容 数组 二维数组 数组 二维数组 定义 二维数组本质上是一个行列式的组合,也就是说二维数组由行和列两部分组成,属于多维数组。二维数组数据是通过行列进行解读。二维数组可被视为一个特殊的一维数组,相当于二维数组又是一…...
计算机专业知识【软件开发中的常用图表:E - R图、HIPO、DFD、N - S、PAD详解】
在软件开发过程中,有许多种图表工具被用于不同阶段的设计和分析,帮助开发者更清晰地理解系统结构、数据流程和算法逻辑。下面将详细介绍E - R图、HIPO图、DFD图、N - S图和PAD图,包括它们的样子和用途。 一、E - R图(实体 - 联系…...
多人协同开发 —— Git Aoneflow工作流
一、Aoneflow工作流核心架构 #mermaid-svg-rwTOe9qYwzG3wkdy {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-rwTOe9qYwzG3wkdy .error-icon{fill:#552222;}#mermaid-svg-rwTOe9qYwzG3wkdy .error-text{fill:#552222…...
VSCode运行Go程序报错:Unable to process `evaluate`: debuggee is running
如果使用默认的VSCode的服务器来运行Go程序,那么使用fmt.Scan函数输入数据的时候就会报错,我们需要修改launch.json文件,将Go程序运行在shell终端上。 main.go package mainimport "fmt"func main() {var n intfmt.Scan(&n)v…...
Mybatis高级(动态SQL)
目录 一、动态SQL 1.1 数据准备: 1.2 <if>标签 1.3<trim> 标签 1.4<where>标签 1.5<set>标签 1.6 <foreach>标签 1.7<include> 标签 一、动态SQL 动态SQL是Mybatis的强⼤特性之⼀,能够完成不同条件下不同…...
在 Vue 3 中使用 Lottie 动画:实现一个加载动画
在现代前端开发中,动画是提升用户体验的重要元素之一。Lottie 是一个流行的动画库,它允许我们使用 JSON 文件来渲染高质量的动画。本文将介绍如何在 Vue 3 项目中集成 Lottie 动画,并实现一个加载动画效果。 如果对你有帮助请帮忙点个&#x…...
建筑行业安全技能竞赛流程方案
一、比赛时间: 6月23日8:30分准时到场;9:00-10:00理论考试;10:10-12:00现场隐患答疑;12:00-13:30午餐;下午13:30-15:30现场…...
Mybatisplus自定义sql
文章目录 引言流程 引言 mybatisplus最擅长的将where里面的语句给简便化,而不用我们自己写标签来实现条件查询 但是很多公司规范我们将sql写在mapper层中,不能写在service中 而且一些语句查询的不同select count(*) xxx from xxx 也难以用mp来实现 如何…...
情书网源码 情书大全帝国cms7.5模板
源码介绍 帝国cms7.5仿《情书网》模板源码,同步生成带手机站带采集。适合改改做文学类的网站。 效果预览 源码获取 情书网源码 情书大全帝国cms7.5模板...
基于Unity引擎的网络通信架构深度解析——以NetworkConnectionController为例
一、架构概览与设计理念 本文将以重构后的NetworkConnectionController为核心,深入分析基于Unity引擎的MMO网络通信架构设计。该模块采用分层设计思想,通过连接池管理、流量控制、心跳监测等多维度技术手段,构建了一个高性能、可扩展的网络通…...
C#学习之DataGridView控件
目录 一、DataGridView控件常用属性、方法、事件汇总表 1. 常用方法、属性和事件汇总 二、DataGridView 控件的常用方法调用 1. DataBind() 方法 2. Clear() 方法 3. Refresh() 方法 4. Sort() 方法 5. ClearSelection() 方法 6. BeginEdit() 方法 7. EndEdit() 方法…...
midjourney 一 prompt 提示词
midjourney 不需要自然语言的描述,它只需要关键词即可。 一个完整的Midjourney prompt通常包括三个部分 图片提示(Image Prompts)、文本提示(Text Prompt)和参数(Parameters)。 1、图片提示(…...
谈谈 wait 和 notify
目录 1 wait()方法 2 notify()⽅法 3 wait 和 sleep 的区别 多线程调度是随机的, 很多时候希望多个线程能够按照咱们规定的顺序来执行. 完成线程之间的配合工作. wait和notify就是一个用来协调线程顺序的重要工具. 这俩方法都是 Object 提供的方法. 随便找个对象࿰…...
250214-java类集框架
引言 类集框架本质上相当于是容器,容器装什么东西由程序员指定 1.单列集合 单列集合是list和set,list的实现类有ArrayList和LinkedList,前者是数组实现,后者是链表实现。list和set,前者有序、可重复,后者…...
Python学习心得异常处理
有些代码在操作的过程当中,如果不注意其所限定的条件,可能在输入函数值时引发一些程序的报错,这样为了让代码自己能做到抛除异常操作的情况,就得让代码具有排除异常的能力。下面的一些操作就使得代码具有该功能,处理异…...
【机器学习】线性回归 多项式线性回归
【机器学习系列】 KNN算法 KNN算法原理简介及要点 特征归一化的重要性及方式线性回归算法 线性回归与一元线性回归 线性回归模型的损失函数 多元线性回归 多项式线性回归 多项式线性回归 V1.0多项式回归多项式回归的公式 特征代换超越函数作为特征向量维度 V1.0 多项式回归 …...
链表和list
链表和list 算法题中的经典操作:用空间代替时间 双链表头插顺序: 1.先修改新结点的左右指针 2.然后修改结点y的左指针 3.最后修改哨兵位的右指针 双链表在任意位置(p)之后插入…...
vscode的一些实用操作
1. 焦点切换(比如主要用到使用快捷键在编辑区和终端区进行切换操作) 2. 跳转行号 使用ctrl g,然后输入指定的文件内容,即可跳转到相应位置。 使用ctrl p,然后输入指定的行号,回车即可跳转到相应行号位置。...
sass中@import升级@use的使用区别与案例
在 Sass 中,import 和 use 都用于模块化代码,但二者有显著区别。以下是主要差异和具体案例说明: 核心区别对比 特性 import (旧版) use (新版) 作用域 全局作用域(变量/混合易冲突) 局部作用域(需通过…...
基于单片机ht7038 demo
单片机与ht7038 demo,三相电能表,电量数据包括电流电压功能,采用免校准方法 列表 ht7038模块/CORE/core_cm3.c , 17273 ht7038模块/CORE/core_cm3.h , 85714 ht7038模块/CORE/startup_stm32f10x_hd.s , 15503 ht7038模块/CORE/startup_stm32…...
基于YOLO11深度学习的胃肠道息肉智能检测分割与诊断系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战、目标分割、人工智能
《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…...
CViewState::InitializeColumns函数分析之_hdsaColumnStates的结构
CViewState::InitializeColumns函数分析之_hdsaColumnStates的结构 // Set up saved column state only if the saved state // contains information other than "nothing". if (_hdsaColumnStates) { UINT cStates DSA_GetItemCount(_hdsaColumnS…...
商淘云中英文外贸电商系统,助力传统企业杨帆出海
在全球经济一体化的浪潮下,传统企业纷纷渴望拓展海外市场,寻求新的增长机遇。然而,语言障碍、复杂的跨境交易流程、多元的支付体系以及迥异的消费习惯,如同重重壁垒,阻碍着传统企业扬帆出海的步伐。此时,商…...
--- Mysql事务 ---
什么是事务 因为事务的存在,可以使得多条sql语句一起执行,并且只有全部执行成功或全部执行失败俩种结果,保证了数据的安全,也使得这些sql语句拥有了原子性,隔离性,一致性,持久性(AC…...
FreeRTOS第7篇:内存的“精打细算”——堆管理与内存分配
文/指尖动听知识库-星愿 文章为付费内容,商业行为,禁止私自转载及抄袭,违者必究!!! 文章专栏:深入FreeRTOS内核:从原理到实战的嵌入式开发指南 引言:嵌入式系统的“仓库管理员” 想象你是一家繁忙仓库的管理员:货物(内存块)需要被高效存取,货架(堆空间)必须避免…...
有效市场理论(Efficient Market Hypothesis,简称 EMH):市场真的“有效”吗?中英双语
有效市场理论(EMH):市场真的“有效”吗? 1. 什么是有效市场理论? 📌 有效市场理论(Efficient Market Hypothesis,简称 EMH) 是由美国经济学家 尤金法玛(Eug…...
STM32 HAL库USART串口中断编程:演示数据丢失
目录 一、开发环境 二、配置STM32CubeMX 三、代码实现与部署 四、运行结果: 五、注意事项 上面讨论过,HAL_UART_Receive最容易丢数据了,可以考虑用中断来实现,但是HAL_UART_Receive_IT还不能直接用,容易数据丢失,实际工作中不会这样用,本文介绍STM32F103 HAL库函数…...
MapReduce的工作原理及其在大数据处理中的应用
MapReduce是一种由Google提出的面向大数据并行处理的计算模型、框架和平台,它通过将复杂的数据处理任务分解为两个简单的阶段——Map(映射)和Reduce(归约),实现了分布式并行计算,极大地提高了数…...