目录

前言

顶层 Makefile

源码简析

版本号

MAKEFLAGS 变量

命令输出

静默输出

设置编译结果输出目录

代码检查

模块编译

设置目标架构和交叉编译器

调用 scripts/Kbuild.include 文件

交叉编译工具变量设置

头文件路径变量

导出变量

make xxx_defconfig 过程

Makefile.build 脚本分析

scripts_basic 目标对应的命令

%config 目标对应的命令


前言

在移植linux内核之前,我们先来学习一下 Linux 内核的顶层 Makefile 文件,因为顶层 Makefile 控制着 Linux 内核的编译流程。

顶层 Makefile

Linux 的顶层 Makefile 和 uboot 的顶层 Makefile 非常相似,感兴趣的可以看下:U-Boot 顶层 Makefile 简析。

源码简析

版本号

顶层 Makefile 一开始就是 Linux 内核的版本号,如下所示:

VERSION = 4
PATCHLEVEL = 1
SUBLEVEL = 15
EXTRAVERSION =

可以看出, Linux 内核版本号为 4.1.15。

MAKEFLAGS 变量

MAKEFLAGS 变量设置如下所示:

MAKEFLAGS += -rR --include-dir=$(CURDIR)

选项

作用

-r

​禁用内置规则​​:取消Make预定义的隐式规则(如.c.o的自动编译规则)

-R

​禁用内置变量​​:忽略Make预定义的隐含变量(如CCCFLAGS等)

--include-dir=$(CURDIR)

​指定头文件搜索路径​​:将当前目录加入头文件搜索路径

命令输出

Linux 编译的时候也可以通过“V=1”来输出完整的命令,这个和 uboot 一样,相关代码如下所示:

# 检查变量V是否来自命令行参数
ifeq ("$(origin V)", "command line")KBUILD_VERBOSE = $(V)  # 如果通过make V=1调用,则继承该值
endif# 设置默认详细级别(未指定时默认为0)
ifndef KBUILD_VERBOSEKBUILD_VERBOSE = 0      # 默认关闭详细输出模式
endif# 根据详细级别设置编译行为
ifeq ($(KBUILD_VERBOSE),1)quiet =                 # 空值表示显示完整命令Q =                     # 空值取消命令隐藏
elsequiet = quiet_          # 前缀用于生成简洁日志Q = @                   # @符号隐藏命令回显
endif

静默输出

Linux 编译的时候使用“make -s”就可实现静默编译,编译的时候就不会打印任何的信息,同 uboot 一样,相关代码如下:

# 检测Make版本是否为4.x系列
ifneq ($(filter 4.%,$(MAKE_VERSION)),)  # make-4.x版本处理逻辑# 检查MAKEFLAGS是否包含-s选项(静默模式)ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)quiet=silent_  # 启用静默模式输出前缀endif
else  # make-3.8x版本处理逻辑# 兼容旧版本make的-s选项检测ifneq ($(filter s% -s%,$(MAKEFLAGS)),)quiet=silent_  # 启用静默模式输出前缀endif
endif# 导出关键变量到子make进程
export quiet Q KBUILD_VERBOSE

设置编译结果输出目录

Linux 编译的时候使用“O=xxx”即可将编译产生的过程文件输出到指定的目录中,相关代码如下:

# 检查是否在源码目录内构建(KBUILD_SRC为空表示是)
ifeq ($(KBUILD_SRC),)# 当前直接在内核源码目录执行make# 检测是否通过命令行参数O指定输出目录ifeq ("$(origin O)", "command line")KBUILD_OUTPUT := $(O)  # 使用用户指定的输出目录路径endif
endif

代码检查

Linux 也支持代码检查:

  • 使用命令“make C=1”使能代码检查,检查那些需要重新编译的文件。
  • make C=2”用于检查所有的源码文件。

顶层 Makefile 中的代码如下:

# 检查变量C是否来自命令行参数(如 make C=1)
ifeq ("$(origin C)", "command line")KBUILD_CHECKSRC = $(C)  # 继承用户指定的值
endif# 设置默认源码检查级别(未指定时默认为0)
ifndef KBUILD_CHECKSRCKBUILD_CHECKSRC = 0      # 默认关闭源码检查
endif

模块编译

Linux 允许单独编译某个模块,使用命令“make M=dir”即可,旧语法“make SUBDIRS=dir”也是支持的。

顶层 Makefile 中的代码如下:

# 处理外部模块构建目录指定方式(两种兼容语法)
# 1. 老式语法:make ... SUBDIRS=$PWD
# 2. 新式语法:make M=dir
# 环境变量 KBUILD_EXTMOD 优先级最高
ifdef SUBDIRSKBUILD_EXTMOD ?= $(SUBDIRS)  # 兼容旧版SUBDIRS参数
endif# 检查命令行是否指定M参数
ifeq ("$(origin M)", "command line")KBUILD_EXTMOD := $(M)  # 使用新式M参数指定模块目录
endif# 根据是否构建外部模块设置默认构建目标
# 内部构建:依赖all目标
# 外部模块构建:依赖modules目标
PHONY += all
ifeq ($(KBUILD_EXTMOD),)_all: all       # 常规内核构建
else_all: modules   # 外部模块构建
endif# 设置源码树路径
ifeq ($(KBUILD_SRC),)# 在源码目录内构建srctree := .    # 源码树设为当前目录
elseifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))# 在源码树的子目录构建srctree := ..  # 源码树设为上级目录else# 完全外部构建srctree := $(KBUILD_SRC)  # 使用指定的源码树路径endif
endif# 设置对象树路径(总是当前目录)
objtree := .
src := $(srctree)    # 源码路径别名
obj := $(objtree)    # 构建路径别名# 设置VPATH(Makefile搜索路径)
# 包含源码树和外部模块目录(如果指定)
VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))# 导出关键路径变量
export srctree objtree VPATH

外部模块编译过程和 uboot 也一样,最终导出 srctree、 objtree 和 VPATH 这三个变量的值,其中 srctree=.,也就是当前目录, objtree 同样为“.”。

设置目标架构和交叉编译器

同 uboot 一样, Linux 编译的时候需要设置目标板架构ARCH 和交叉编译器 CROSS_COMPILE,在顶层 Makefile 中代码如下:

ARCH ?= $(SUBARCH)
CROSS_COMPILE ?= $(CONFIG_CROSS_COMPILE:"%"=%)

为了方便,一般直接修改顶层 Makefile 中的 ARCH 和 CROSS_COMPILE,直接将其设置为对应的架构和编译器,比如本教程将 ARCH 设置为为 arm, CROSS_COMPILE 设置为 armlinux-gnueabihf-,如下所示:

ARCH ?= arm
CROSS_COMPILE ?= arm-linux-gnueabihf-

设置好以后我们就可以使用如下命令编译 Linux 了:

make xxx_defconfig //使用默认配置文件配置 Linux
make menuconfig //启动图形化配置界面
make -j16 //编译 Linux

调用 scripts/Kbuild.include 文件

同 uboot 一样, Linux 顶层 Makefile 也会调用文件 scripts/Kbuild.include,

顶层 Makefile 相应代码如下:

# We need some generic definitions (do not try to remake the file).
scripts/Kbuild.include: ;
include scripts/Kbuild.include

交叉编译工具变量设置

顶层 Makefile 中其他和交叉编译器有关的变量设置如下:

# 汇编器 (Assembler)
AS = $(CROSS_COMPILE)as      # 用于将汇编代码编译为目标文件# 链接器 (Linker)
LD = $(CROSS_COMPILE)ld      # 负责目标文件的链接和重定位# C编译器 (C Compiler)
CC = $(CROSS_COMPILE)gcc     # 主编译工具,处理C语言源文件# 预处理器 (C Preprocessor)
CPP = $(CC) -E               # 只进行预处理不编译(-E选项)# 静态库工具 (Archiver)
AR = $(CROSS_COMPILE)ar      # 创建和管理静态库(.a文件)# 符号表查看器 (Symbol Lister)
NM = $(CROSS_COMPILE)nm      # 列出目标文件的符号表# 二进制精简工具 (Binary Stripper)
STRIP = $(CROSS_COMPILE)strip # 去除调试符号减小文件体积# 二进制转换工具 (Object Copier)
OBJCOPY = $(CROSS_COMPILE)objcopy # 转换目标文件格式# 反汇编工具 (Object Dumper)
OBJDUMP = $(CROSS_COMPILE)objdump # 反汇编和调试信息提取

LA、 LD、 CC 等这些都是交叉编译器所使用的工具。

头文件路径变量

顶层 Makefile 定义了两个变量保存头文件路径: USERINCLUDE LINUXINCLUDE

相关代码如下:

# 用户空间头文件包含路径(UAPI接口)
USERINCLUDE := \-I$(srctree)/arch/$(hdr-arch)/include/uapi \        # 架构特定UAPI头文件-Iarch/$(hdr-arch)/include/generated/uapi \        # 生成的架构UAPI头文件-I$(srctree)/include/uapi \                        # 通用UAPI头文件-Iinclude/generated/uapi \                         # 生成的通用UAPI头文件-include $(srctree)/include/linux/kconfig.h        # 强制包含kconfig头文件# 内核空间头文件包含路径(兼容O=外部构建选项)
LINUXINCLUDE := \-I$(srctree)/arch/$(hdr-arch)/include \            # 架构特定内核头文件-Iarch/$(hdr-arch)/include/generated/uapi \        # 生成的架构UAPI头文件(重复)-Iarch/$(hdr-arch)/include/generated \            # 生成的架构私有头文件$(if $(KBUILD_SRC), -I$(srctree)/include) \       # 外部构建时包含源码树头文件-Iinclude \                                       # 当前构建目录头文件$(USERINCLUDE)                                    # 包含用户空间路径

LINUXINCLUDE变量,其中srctree=., hdr-arch=arm, KBUILD_SRC 为空,因此,将 USERINCLUDE 和 LINUXINCLUDE 展开以后为:

USERINCLUDE := \
-I./arch/arm/include/uapi \
-Iarch/arm/include/generated/uapi \
-I./include/uapi \
-Iinclude/generated/uapi \
-include ./include/linux/kconfig.hLINUXINCLUDE := \
-I./arch/arm/include \
-Iarch/arm/include/generated/uapi \
-Iarch/arm/include/generated \
-Iinclude \
-I./arch/arm/include/uapi \
-Iarch/arm/include/generated/uapi \
-I./include/uapi \
-Iinclude/generated/uapi \
-include ./include/linux/kconfig.h

导出变量

顶层 Makefile 会导出很多变量给子 Makefile 使用,导出的这些变量如下:

# 内核版本信息导出(用于版本标识)
export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION# 基础构建配置导出
export ARCH SRCARCH CONFIG_SHELL  # 架构和shell配置
export HOSTCC HOSTCFLAGS          # 主机工具链(用于构建host程序)
export CROSS_COMPILE              # 交叉编译前缀(如arm-linux-gnueabi-)
export AS LD CC                   # 核心工具链(汇编器、链接器、编译器)# 二进制工具集导出
export CPP AR NM STRIP OBJCOPY OBJDUMP  # 预处理器、静态库、符号表等工具# 脚本解释器和工具
export MAKE AWK GENKSYMS INSTALLKERNEL  # make/awk/符号生成工具/内核安装脚本
export PERL PYTHON                     # 脚本解释器
export UTS_MACHINE                     # 机器标识符# 主机C++工具链(用于需要C++的构建步骤)
export HOSTCXX HOSTCXXFLAGS# 模块构建专用参数
export LDFLAGS_MODULE  # 模块链接参数# 代码检查工具
export CHECK CHECKFLAGS  # 静态分析工具(如sparse)# 预处理和包含路径
export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE  # 预处理标志和头文件路径# 二进制工具参数
export OBJCOPYFLAGS LDFLAGS  # objcopy和链接器参数# C编译器标志集
export KBUILD_CFLAGS          # 全局C标志
export CFLAGS_KERNEL         # 内核核心编译标志
export CFLAGS_MODULE         # 模块编译标志
export CFLAGS_GCOV           # GCOV覆盖率测试标志
export CFLAGS_KASAN          # KASAN内存检测标志# 汇编器标志集
export KBUILD_AFLAGS         # 全局汇编标志
export AFLAGS_KERNEL         # 内核核心汇编标志
export AFLAGS_MODULE         # 模块汇编标志# 模块构建专用标志
export KBUILD_AFLAGS_MODULE KBUILD_CFLAGS_MODULE KBUILD_LDFLAGS_MODULE# 内核核心构建专用标志
export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL# 静态库工具参数
export KBUILD_ARFLAGS  # ar命令参数

make xxx_defconfig 过程

第一次编译 Linux 之前都要使用“make xxx_defconfig”先配置 Linux 内核,在顶层 Makefile中有“%config”这个目标,如下所示:

# 初始化构建模式标志
config-targets := 0    # 是否为配置目标(如menuconfig)
mixed-targets := 0     # 是否混合了配置和编译目标
dot-config := 1        # 是否需要读取.config文件# 检查是否需要忽略.config(针对clean/mrproper等目标)
ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)dot-config := 0  # 当只有clean类目标时禁用.config依赖endif
endif# 检测配置类目标(仅在内核构建时检查)
ifeq ($(KBUILD_EXTMOD),)ifneq ($(filter config %config,$(MAKECMDGOALS)),)config-targets := 1  # 标记为配置目标ifneq ($(words $(MAKECMDGOALS)),1)mixed-targets := 1  # 多个目标混合时标记endifendif
endif# 混合目标处理(如 make menuconfig all)
ifeq ($(mixed-targets),1)PHONY += $(MAKECMDGOALS) __build_one_by_one# 将目标重定向到顺序执行$(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one@:  # 空命令# 逐个目标执行__build_one_by_one:$(Q)set -e; \for i in $(MAKECMDGOALS); do \$(MAKE) -f $(srctree)/Makefile $$i; \done# 纯配置目标处理(如 make menuconfig)
else ifeq ($(config-targets),1)# 包含架构相关配置include arch/$(SRCARCH)/Makefileexport KBUILD_DEFCONFIG KBUILD_KCONFIG# 处理config类目标config: scripts_basic outputmakefile FORCE$(Q)$(MAKE) $(build)=scripts/kconfig $@%config: scripts_basic outputmakefile FORCE$(Q)$(MAKE) $(build)=scripts/kconfig $@# 常规构建目标处理
else# [常规构建流程...]
endif

这段代码里,最开始是设置定义变量 config-targets、 mixed-targets 和 dot-config的值,最终这三个变量的值为:

config-targets= 1
mixed-targets= 0
dot-config= 1

因此会引用 arch/arm/Makefile 这个文件,这个文件很重要,因为 zImage、 uImage 等这些文件就是由 arch/arm/Makefile 来生成的。

“make xxx_defconfig”与目标“%config”匹配,因此执行。

%config: scripts_basic outputmakefile FORCE$(Q)$(MAKE) $(build)=scripts/kconfig $@

“%config”依赖scripts_basic、 outputmakefile 和 FORCE,“%config”真正有意义的依赖就只有 scripts_basic。

 scripts_basic 的规则如下:

scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic
$(Q)rm -f .tmp_quiet_recordmcount

其中,build 定义在文件 scripts/Kbuild.include 中,值为:

build := -f $(srctree)/scripts/Makefile.build obj

因此 scripts_basic展开为:

scripts_basic:
@make -f ./scripts/Makefile.build obj=scripts/basic //也可以没有@,视配置而定
@rm -f . tmp_quiet_recordmcount //也可以没有@

所以目标“%config”代入scripts_basic的值,展开为:

@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig

Makefile.build 脚本分析

我们现在已经知道:“ make xxx_defconfig“配置 Linux 的时候如下两行命令会执行脚本scripts/Makefile.build:

@make -f ./scripts/Makefile.build obj=scripts/basic
@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig

scripts_basic 目标对应的命令

打开文件 scripts/Makefile.build,有如下代码:

# Kbuild文件优先级高于Makefile
# 确定子目录路径(支持绝对路径和相对路径)
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))# 确定构建规则文件名(优先查找Kbuild,不存在则用Makefile)
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)# 包含找到的构建规则文件
include $(kbuild-file)

其中:将 kbuild-dir、kbuild-file都展开代入:

kbuild-dir =./scripts/basic
kbuild-file = ./scripts/basic/Makefile
include ./scripts/basic/Makefile

继续分析 scripts/Makefile.build,如下代码:

# 默认构建目标(通过__build伪目标实现)
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \$(subdir-ym) $(always)@:  # 空命令(实际工作由依赖项完成)

__build 是默认目标,在顶层 Makefile 中, KBUILD_BUILTIN 为 1, KBUILD_MODULES 为空,因此展开后目标__build 为:

__build:$(builtin-target) $(lib-target) $(extra-y)) $(subdir-ym) $(always)@:

可以看出目标__build 有 5 个依赖: builtin-target、 lib-target、 extra-y、 subdir-ym 和 always。这 5 个依赖的具体内容如下:

builtin-target =
lib-target =
extra-y =
subdir-ym =
always = scripts/basic/fixdep scripts/basic/bin2c

只有 always 有效,因此__build 最终为:

__build: scripts/basic/fixdep scripts/basic/bin2c@:

__build 依赖于 scripts/basic/fixdep 和 scripts/basic/bin2c,所以要先将 scripts/basic/fixdep 和scripts/basic/bin2c.c 这两个文件编译成 fixdep 和 bin2c。

@make -f ./scripts/Makefile.build obj=scripts/basic

综上所述, scripts_basic 目标的作用就是编译出 scripts/basic/fixdep scripts/basic/bin2c 这两个软件。

%config 目标对应的命令

%config 目 标 对 应 的 命 令 为 :

@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig

此命令会使用到的各个变量值如下:

src= scripts/kconfig
kbuild-dir = ./scripts/kconfig
kbuild-file = ./scripts/kconfig/Makefile
include ./scripts/kconfig/Makefile

可以看出, Makefile.build 会读取 scripts/kconfig/Makefile 中的内容,

此文件有如下所示内容:

 %_defconfig: $(obj)/conf$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@
$(Kconfig)

目标%_defconfig 与 xxx_defconfig 匹配,所以会执行这条规则,将其展开就是:

%_defconfig: scripts/kconfig/conf
@ scripts/kconfig/conf --defconfig=arch/arm/configs/%_defconfig Kconfig

_defconfig依赖scripts/kconfig/conf,所以会编译scripts/kconfig/conf.c生成conf 这个软件。

此软件就会将%_defconfig 中的配置输出到.config 文件中,最终生成 Linux kernel 根目录下的.config 文件。

我们下一讲内容再来说明make 过程、built-in.o 文件编译生成过程、make zImage 过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/90200.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/90200.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/90200.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenCV 官翻6 - Computational Photography

文章目录图像去噪目标理论OpenCV中的图像去噪1、cv.fastNlMeansDenoisingColored()2、cv.fastNlMeansDenoisingMulti()附加资源图像修复目标基础概念代码补充资源练习高动态范围成像&#xff08;HDR&#xff09;目标理论基础曝光序列HDR1、将曝光图像加载到列表中2、将曝光序列…

APT32F1732RBT8爱普特微电子 32位MCU国产芯片 智能家居/工业控制 首选

APT32F1732RBT8 爱普特微电子&#xff0c;32位MCU国产芯片一、产品简介APT32F1732RBT8 是爱普特微电子&#xff08;APT&#xff09;推出的高性能32位ARM Cortex-M0内核MCU&#xff0c;主频高达48MHz&#xff0c;内置64KB Flash8KB RAM&#xff0c;专为智能家居、工业控制、消费…

Smart Tomcat

本篇博客的内容是教你借助idea中的插件,把tomcat集成到idea中安装 Smart Tomcat 插件搜索下载 ,如果一直处于加载界面,就尝试一下科学上网配置 Smart Tomcat 插件 点击右上角的 "Add Configuration"选择左侧的 "Smart Tomcat" 在 Name 这一栏填写一个名字(…

Linux_shell编写

title: Linux_4 shell编写 shell pwd (/root/A/2025_7/19/myshell) 首先需要设计命令行提示 &#xff08;MakeCommandLine()&#xff09; 首先获取相关信息 getenv(“name”) // 获取用户名 const char* GetUserName() {const char* name getenv("USER");if (name …

【数据结构】栈的深入解析--用C语言实现

文章目录1.栈的概念2.栈的底层结构3.栈的功能4.栈的实现4.1.栈结构的定义4.2.栈的初始化4.3.栈的销毁4.4.入栈4.5.出栈4.6.取栈顶元素4.7.获取栈中有效元素个数5.完整代码Stack.hStack.cmain.c运行结果1.栈的概念 是一种特殊的线性表&#xff0c;只允许数据在固定的一段进行插…

Git仓库核心概念与工作流程详解:从入门到精通

Git仓库的基本概念版本库&#xff08;Repository&#xff09;是Git的核心概念&#xff0c;你可以简单理解为一个被Git管理的目录。这个目录里的所有文件都能被Git跟踪&#xff0c;记录每次修改和删除&#xff0c;让你可以随时追溯历史或在未来某个时刻"还原"文件。Gi…

Web开发 05

1 React库&#xff08;人话详解版&#xff09;别慌&#xff0c;React 刚接触时是会有点懵&#xff0c;咱们用 “人话 类比” 一步步拆&#xff1a;核心概念先抓牢组件&#xff08;Component&#xff09;把它想成 “乐高积木”&#xff0c;比如做个社交 App&#xff0c;顶部导航…

RustDesk 自建中继服务器教程(Mac mini)

&#x1f4d6; 教程目标 在家里的 Mac mini 上部署 RustDesk 中继服务器 (hbbs hbbr)&#xff0c;让你从办公室、笔电或手机 低延迟、安全 地远程控制家里的 Windows 和 Mac mini。 ✅ 不依赖第三方服务器 ✅ 支持 P2P 和中继双模式 ✅ 全流量可控、跨平台 &#x1f3d7;️ 架…

数据库—修改某字段默认值

前言有时候&#xff0c;数据库的字段默认值没有正确设置&#xff0c;这时候需要改默认值。以下是我做的改默认值的记录&#xff0c;希望对网友有所帮助。1.SQL SERVER下面的示例假设你要修改名为 YourColumnName 的字段&#xff0c;并为其设置一个新的默认值 NewDefaultValue。…

Spring快速整合Mybatis

MyBatis是一个优秀的持久层框架&#xff0c;Spring则是广泛使用的Java应用框架。可以将两者整合可以充分发挥各自的优势。 1、Spring整合MyBatis的基本配置 添加依赖&#xff1a; <dependency><groupId>org.springframework</groupId><artifactId>spri…

基于深度学习的语音识别:从音频信号到文本转录

前言 语音识别&#xff08;Automatic Speech Recognition, ASR&#xff09;是人工智能领域中一个极具挑战性和应用前景的研究方向。它通过将语音信号转换为文本&#xff0c;为人们提供了更加自然和便捷的人机交互方式。近年来&#xff0c;深度学习技术在语音识别领域取得了显著…

本地部署Nacos开源服务平台,并简单操作实现外部访问,Windows 版本

Nacos 是一款阿里开源的动态服务发现、配置、管理平台&#xff0c;拥有易于集成、高可用与可扩展等特点。它提供了动态服务注册和发现能力&#xff0c;使得服务自动注册到服务器并且消费真能够发现提供者。本文将详细介绍如何在本地安装 Nacos &#xff0c;以及结合nat123端口映…

数据结构:反转字符串(Reversing a String)

目录 方法一&#xff1a;双指针法 方法二&#xff1a;辅助数组 方法对比总结&#xff1a; 问题定义 给定一个字符串&#xff0c;例如&#xff1a; char str[] "hello";我们的目标是把它反转成&#xff1a; "olleh"&#x1f4cc; 输入特点&#xff…

Redis Copy-on-Write机制:

Copy-on-Write机制&#xff1a; 父子进程共享内存页 当父进程修改数据时&#xff0c;内核会复制被修改的页 这可能导致内存使用量暂时增加 通俗的话描述一下可以用一个生活中的例子来通俗解释 Copy-on-Write&#xff08;写时复制&#xff09; 机制&#xff1a;&#x1f4d6; 比…

iOS加固工具有哪些?从零源码到深度混淆的全景解读

在iOS安全加固领域&#xff0c;不同项目类型对保护需求有着本质差异&#xff1a;“我有源码”与“我只有IPA”两条路径决定了你该用什么工具。本文将从 无需源码处理整个IPA包 到 源码级编译期混淆&#xff0c;分层探讨主流工具如何发挥价值&#xff0c;并附上适配方案建议。工…

Composer 可以通过指定 PHP 版本运行

是的&#xff0c;Composer 可以通过指定 PHP 版本运行&#xff0c;尤其是在服务器上有多个 PHP 版本时&#xff08;如 PHP 7.x 和 PHP 8.x&#xff09;。以下是几种常见方法&#xff1a;方法 1&#xff1a;使用 php 命令指定版本 Composer 依赖系统中的 php 命令&#xff0c;因…

vscode文件颜色,只显示自己更改的文件颜色

这个主要是因为你github git下来以后&#xff0c;用vscode打开会默认显示更改了&#xff0c;你只要在这里先手动取消更改就行了&#xff0c;注意不要把你自己更改的取消了

记录我coding印象比较深刻的BUG

4778&#xff1a;我的BUG噩梦问题描述&#xff1a;DAB播放中关ACC掉电后开ACC&#xff0c;手动切到FM/AM(有时第一次切换出现问题/有时第二次切换出现问题)&#xff0c;FM/AM不记忆关ACC前电台或者FM/AM关ACC掉电后开ACC&#xff0c;手动切到DAB再回到FM/AM&#xff0c;FM/AM不…

Kubernetes集群中Istio mTLS握手失败问题排查与解决方案

Kubernetes集群中Istio mTLS握手失败问题排查与解决方案 在微服务架构中&#xff0c;Istio 提供了基于 Envoy 的服务网格能力&#xff0c;其中 mTLS&#xff08;双向 TLS&#xff09;是确保服务间通信安全的重要机制。但在生产环境中&#xff0c;开发者常常会遇到 mTLS 握手失败…

antd+react+可输入的下拉选择组件

该组件是一个可输入的下拉选择组件&#xff0c;支持从预设选项中选择或手动输入自定义值。组件基于 React 和 Ant Design 实现&#xff0c;具有良好的交互体验和灵活的配置选项。 &#x1f9e0; 核心逻辑分析 1. 状态管理 const [isInput, setIsInput] useState(false); con…