当前位置: 首页 > news >正文

PKU_Compiler

130480524_p0

from pixiv

资源

  • NJU Compiler 课程
  • 中科大 Compiler 课程
  • LLVM IR Github book教程
  • Koopa IR 框架
  • PKU 讲义本体
  • Github仓库

Lv0 环境配置

Docker

获取编译实践的镜像:

sudo docker pull maxxing/compiler-dev
  • docker安装

  • 配置docker镜像

    • vim /etc/docker/daemon.json

    • {"registry-mirrors": ["xxx","xxx"]
      }
      
    • docker 存活的镜像

Docker 基本使用方法

  • 官方文档
// 启动容器
docker run maxxing/compiler-dev// 查看目前 Docker 中所有的容器:
docker ps -a// 删除 容器
docker rm CONTAINER ID
// -it 参数, 这个参数会开启容器的 stdin 以便我们输入 (-i), 同时 Docker 会为容器分配一个终端 (-t).
docker run -it maxxing/compiler-dev bash

在许多情况下, 我们需要让 Docker 容器访问宿主系统中的文件. 比如你的编译器存放在宿主机的 /home/max/compiler 目录下, 你希望 Docker 容器也能访问到这个目录里的内容, 这样你就可以使用容器中的测试脚本测试你的编译器了. 你可以执行:

docker run -it --rm -v /home/max/compiler:/root/compiler maxxing/compiler-dev bash
  • -v /home/max/compiler:/root/compiler 选项, 这个选项代表: 我希望把宿主机的 /home/max/compiler 目录, 挂载 (mount) 到容器的 /root/compiler 目录. 这样, 在进入容器之后, 我们就可以通过访问 /root/compiler 来访问宿主机的 /home/max/compiler 目录了.
  • --rm: Docker 会在退出后删除刚刚的容器
  • 因为Docker只是测试我们在宿主机上实现的代码,所以测完直接可以丢掉了

maxxing/compiler-dev 实验环境

实验环境中已经配置了如下工具:

  • 必要的工具: git, flex, bison, python3.
  • 构建工具: make, cmake.
  • 运行工具: qemu-user-static.
  • 编译工具链: Rust 工具链, LLVM 工具链.
  • Koopa IR 相关工具: libkoopa (Koopa 的 C/C++ 库), koopac (Koopa IR 到 LLVM IR 转换器).
  • 测试脚本: autotest.

我们要做的事情就是在宿主机上编写我们的compiler代码,然后将我们代码目录挂载到docker的某个目录下,在docker maxxing/compiler-dev的实验环境中运行测试我们的程序

具体查看

项目模板

Github

Lv1 main函数

编译原理基础知识

语法分析树

二义性

所谓的二义性可以理解为对于一个文法,可以构造出多个语法分析树

因为语法分析树是对语义的解释,则说对于一个文法我们有多个语义

案例:

  • 因最短/最长匹配导致的二义性

    image-20250911161426980

    如上图中的文法和语句,我们有多个方式解释if a then if b then c else d 这个语句,即在执行时,其有多个语义:

    • if (a) then { if b then c else d}
    • if (a) then { if b then c } else { d }

    我们可以通过更改文法/规定匹配来避免二义性,例如这里我们规定else匹配距离其最近的if,则最终我们只会得到if (a) then { if b then c else d}, 其语法树:

    image-20250911161858841

  • 因运算符的结合性/优先级导致的二义性

    例如运算符优先级导致的二义性,我们可以通过规定运算符优先级(更常见)或者更改文法避免。如a - b * c语义可以为:

    • a - (b * c)
    • (a - b) * c

    image-20250911162142542

    我们通过如上方式避免(ANTLR 4 中规定越在上的优先级越高)

抽象语法树

抽象语法树的前身--语法分析树

image-20250905110831735

上图中例如"(" 和 ")"这些信息是不重要的,同时3,4这两个叶子结点的父节点也是没有必要的

对于表达式而言,编译只需要知道运算符和运算数

优先级、结合性等已经在语法分析部分处理掉了

对于语句、函数等语言其他构造而言也一样

例如,编译器不关心赋值符号是=还是:=或其它

抽象语法树

为了节省内存,压缩信息,得到更加紧凑的表示,我们对语法分析树进行浓缩:

image-20250905111318880

  • 具体语法是语法分析器使用的语法
    • 必须适合于语法分析,如各种分隔符、消除左递归、提取左公因子,等等
  • 抽象语法是用来表达语法结构的内部表示
    • 现代编译器一般都采用抽象语法作为前端(词法语法分析)和后端(代码生成)的接口

在编译器中,为了定义抽象语法树,需要使用实现语言(C++/C/java...)来定义一组数据结构

例如在课中以简单的实例说明的:

image-20250905152616108

其给每个终结符和非终结符定义了结构体,并定义构造函数在生成抽象语法树时使用:

image-20250905152742215


image-20250905152914595

抽象语法树的自动生成

在语法动作中,加入生成语法树的代码片段。

  • 片段一般是语法树的“构造函数’

在产生式归约的时候,会自底向上构造整棵树

  • 从叶子到根

上述内容说的就是Bison中的写法,例如:

FuncDef: FuncType IDENT '(' ')' Block {auto ast = new ast::FuncDefAST();ast->func_type = unique_ptr<ast::BaseAST>($1);ast->ident = *unique_ptr<std::string>($2);ast->block = unique_ptr<ast::BaseAST>($5);$$ = ast;}

若想要表示位置信息,则可以在数据结构中完善更多的信息:

image-20250905152955546

中间代码生成

语义分析

  • 语义分析也称为类型检查、上下文相关分析
  • 负责检查程序(抽象语法树)的上下文相关的属性:
    • 这是具体语言相关的,典型的情况包括:
      • 变量在使用前先进行声明
      • 每个表达式都有合适的类型
      • 函数调用和函数的定义一致
      • ...

在Flex/Bison中已经做到了一部分语义分析,例如类型检查

例如我们在Bison中通过%union%token定义终结符和非终结符语义的类型:

%union {std::string *str_val;int int_val;ast::BaseAST *ast_val;
}// 终结符的类型定义
%token INT RETURN
%token <str_val> IDENT
%token <int_val> INT_CONST// 非终结符的类型定义
%type <ast_val> FuncDef FuncType Block Stmt 
%type <int_val> Number

符号表

若要我们自己实现语义分析中的类型检查,变量声明检查等内容,符号表不可或缺

P -> D E
D -> T id;|
T -> int| bool
E -> n| id| true| false| E + E| E && E

我们以上述简单的语法为例:

  • P : program

  • D : declare

  • T : type

  • E : express

  • id: 终结符

  • n: 终结符

我们对语句:

int x;
bool y;
4 + x;

进行语法检查和语义检查:

抽象语法树:

image-20250907142822744

类型检查算法

image-20250907142936910

  • table_enter为插入符号表的操作
    • 当我们检查声明时,若未在符号表找到相关符号的声明,则插入新元素[key, value],其中key为符号的name, 如x, value为符号的类型,如int

image-20250907143157139

  • 在检查表达式时,若表达式中的符号未在符号表中查找到,则说明使用了未声明的符号

符号表的作用域与scope

作用域

int x;int foo() {int x;int y;return x + y;
}

上述代码中在不同作用域下有两个不同的x,符号表需要能够区分:

  • 方法#1:一张表的方法
    • 进入作用域时,插入元素
    • 退出作用域时,删除元素
  • 方法#2:采用符号表构成的栈
    • 进入作用域时,插入新的符号表
    • 退出作用域时,删除栈顶符号表

Scope

struct list{int x;struct list *list;
} *list;void walk(struct list *list) {list:if (list == list->list)goto list;
}

上述代码中list出现了多次,分别为:

  • 变量名 (*list

  • 命名 (struct list)

  • 标号 (goto list)

它们被称为不同的scope

每个scope用一个表来处理

中间代码生成

中间代码其实也可以理解为是某种语言,例如LLVM IR,其与汇编语言已经非常相近了,同时也可以作为一门语言进行书写

这里我们以更加简单的例子进行讲解:

image-20250907151913886

我们从之间的语法 --> IR

R_t Gen E(E e) {
switch(e) {case n: r=fresh();emit("movn n,r");return r;case id: r=fresh();emit ("mov id,r');return r;case true: r=fresh();emit("movn 1,r");return r;case false:r=fresh();emit("movn 0,r");return r;case e1 + e2: r1 = Gen_E(e1);r2 = Gen_E(e2);r = fresh();emit("add r1, r2, r3");return r;case e1 && e2: r1 = Gen_E(e1);r2 = Gen_E(e2);r = fresh();emit("and r1, r2, r3");return r;
  • R_t表示一个寄存器类型
  • fresh()表示生成一个新的寄存器
  • emit表示生成对应的IR指令

上述方法也很像我们实现抽象语法树时做的行为


中间代码的形式有很多:

  • 树和有向无环图(DAG)
    • 高层表示,适用于程序源代码
  • 三地址码(3-address code)
    • 低层表示,靠近目标机器
  • 控制流图(CFG)
    • 更精细的三地址码,程序的图状表示适合做程序分析、程序分析等
  • 静态单赋值形式(SSA)
    • 更精细的控制流图
    • 同时编码控制流信息和数据流信息
  • 连续传递风格(CPS)
    • 更一般的SSA

抽象语法树变体 --DAG 有向无环图

“语法树的变体是有向无环图” 的意思是:编译器里用于表示程序结构的“树状”数据结构可以允许不同父节点指向同一个子节点(即共享子结构),形成一个有向图,而且这个图通常不会包含环(acyclic),因此称为 DAG(Directed Acyclic Graph)。

  • :每个节点有且只有一个父节点(除了根),不同分支间不会共享子树。

  • 有向图:节点间的引用有方向(例如父 → 子)。

  • DAG:有向且无环的图;允许多个父节点共享同一个子节点,但不会有回到自己/循环的路径。

树是最直观的表示法,但在实际编译器中多个位置可能出现相同的子表达式或相同的“值”。为了节省内存、方便优化与比较,常采用共享子节点的做法——这会把结构从树变成 DAG。

例如对于表达式a+a*(b-c)+(b-c)*d, 其抽象语法树为:

image-20250910163230070

树是最直观的表示法,但在实际编译器中多个位置可能出现相同的子表达式或相同的“值”。为了节省内存、方便优化与比较,常采用共享子节点的做法——这会把结构从树变成 DAG。

对于重复子表达式, 边从上层节点指向这个共享节点。

于是上述抽象语法树变成如下:

image-20250910163340675

DAG --> 三地址中间表示

语法制导翻译 -- 依赖图

  • 依赖图(dependency graph) 是把“某一棵语法树(或某次产生式展开)中每个属性的具体出现”作为节点,属性之间的“谁依赖谁”作为有向边构成的图。
  • 求属性的合法计算顺序就是对这个图做 拓扑排序(topological order)。
  • 如果图中有环(cycle),说明这些属性不能用单次有向无环评估得到(需要重写规则或使用迭代/固定点技巧)。
  • 对于只含合成属性(S-属性)的文法,依赖图天然是自底向上的,直接用后序遍历(reduce 时计算)即可。
  • 对于 L-属性(L-attributed),可以在一次自顶向下/左到右的遍历或在解析时用语义动作按顺序求到属性值(能在线计算)。

  1. 依赖图的正式定义(简洁)

给定:一棵 解析树(parse tree) T 和一个 SDD(syntax-directed definition,给出每条产生式的属性计算规则)。

  • 节点集合 V = “T 中所有属性的出现”(每个语法树结点对于每个属性出现一个节点,例:对某个语法树结点 A,如果 A 有属性 A.inhA.s,就有两个依赖图节点)。
  • 有向边 (u → v) 当且仅当:v 的计算公式使用了 u 的值
    举例:如果规则写 A.s = B.s + C.s,那么在语法树的对应位置就会有边 B.s → A.sC.s → A.s

目标:找到一个节点序列,使得每条边 u→v 都满足 u 在 v 之前被计算——这就是拓扑序。


  1. 怎么构造依赖图(步骤)
  1. 在解析树的每个树结点(一个非终结符或终结符)上列出它的所有属性实例(合成/继承/其他)。这些属性实例就是依赖图的节点。
  2. 对照你为每条产生式写的语义规则(例如 A.s = B.s + C.s, C.inh = A.inh 等),为每个语义等式加入有向边:从被使用的属性指向被定义的属性。
    • 例如在产生式 A -> B C 中,规则 C.inh = f(B.inh, B.s) 会产生 B.inh → C.inhB.s → C.inh
  3. 做完语法树上所有出现的产生式和对应规则,得到完整的依赖图。
  4. 对该有向图做拓扑排序(比如 Kahn 算法或 DFS)。若能得到排序,则按此顺序依次计算属性;若不能(存在环),则说明不能单次评估。

命令

启动docker环境

docker run -it --rm -v /home/cilinmengye/Github/PKU_Compiler:/root/compiler maxxing/compiler-dev bash

Cmake编译命令

cd 项目目录
cmake -DCMAKE_BUILD_TYPE=Debug -B build
cmake --build build

执行命令

build/compiler -koopa debug/hello.c -o debug/hello.koopa

Flex and Bison 初见

  • vscode 下载 Yash插件,提供flex 和 bison 高亮

Flex注释问题

Flex 推荐用 C 风格注释

/* 这是注释 */

Flex 把 .l 文件分为三段:

  1. %{ ... %}:直接拷贝到生成的 .c
  2. %% ... %%:规则区(正则表达式 + 动作)
  3. %% ... 之后:C 代码区

Flex 确实允许 C 风格注释,但是要注意:

  • 注释只能写在 C 代码区动作 {...} 里面
  • 直接把一大段 /* ... */ 放在规则区开头,有些版本的 Flex 会误判,把它当成“正则模式”

Lv2 初试目标代码生成

目的:

  • AST --> in-memory Koopa IR
  • in-memory Koopa IR --> Text form Koopa IR

建立内存形式的 Koopa IR

我想要通过如下方法实现:

  1. 遍历 AST, 直接建立 (某种) 内存形式的 Koopa IR, 再将其转换为文本形式输出.

  2. 遍历 AST, 输出文本形式的 Koopa IR 程序,再使用 libkoopa 中的接口将文本形式 Koopa IR 转换为 raw program


好吧1方法实在是太有难度了,我还是选择方法2进行实现吧。

In-memory Koopa IR --> Riscv assembly code

在 SysY 程序中, 我们定义了一个 main 函数, 这个函数什么也没做, 只是返回了一个整数, 之后就退出了. RISC-V 程序所做的事情与之一致:

  1. 定义了 main 函数.
  2. 将作为返回值的整数加载到了存放返回值的寄存器中.
  3. 执行返回指令.

命令

// 测试
build/compiler -riscv debug/hello.c -o debug/hello.S
clang debug/hello.S -c -o debug/hello.o -target riscv32-unknown-linux-elf -march=rv32im -mabi=ilp32
ld.lld debug/hello.o -L$CDE_LIBRARY_PATH/riscv32 -lsysy -o debug/hello
qemu-riscv32-static debug/helloecho $?

附录 参考资料

  • LLVM IR
  • Koopa IR 接口
  • Koopa IR 文档
  • Koopa IR 相关博客
    • Koopa IR 人话版

Lv3 表达式

编译原理基础知识

三地址码

我们的Koopa IR也是一种三地址码:

  • 我们需要给每个中间变量和计算结果命名
  • 只有最基本的控制流(call,jump等),没有各种控制结构(if, while, for等)

控制流图

控制流图也是一种中间表示

我们将三地址码划分为基本块,基本块与基本块之间的跳转我们用->表示

image-20250911150612268

基于控制流图的数据流分析:

  • 例如永远到达不了的基本块节点,我们可以将这个基本块删除(死基本块删除)
  • 例如变量的值随控制流图传播发现值不会改变,则可以将变量直接变成常量(常量传播)

image-20250911151143369

image-20250911151201398

语法制导 实现抽象语法树-->IR

生成临时变量

image-20250911200221154

  • E.code: 综合属性,表示中间代码
    • 综合属性则说明从语法分析树自下而上,步步传递给父节点的综合属性
  • E.addr: 综合属性,表示变量名(临时变量/常量)
  • top: 表示当前scope下的符号表
    • top.get(id.lexeme) 表示取出 当前scope下符号表最上一个变量名
  • gen(xxx): 表示生成代码xxx
  • new Temp():表示生成临时变量
    • 如右边的t1, t2等为生成的临时变量

S → id = E ;

  • S.code = E.code || gen(top.get(id.lexeme) '=' E.addr)
    含义:
    1. 先生成 E 的代码
    2. 再生成一条赋值语句,把 E 的值存入 id 对应的地址
    3. top.get(id.lexeme) 就是符号表里取出这个 id 的位置

IR --> RISCV

IR --> RISCV 这个过程例如 LLVM IR反而是最顶层的IR,我们在将LLVM IR --> RISCV时需要转化为多次更底层的IR,IR --> 更底层IR 这个过程被称为 pass

每次pass的过程都在依据IR进行优化

image-20250912183423557

image-20250912183454723

  • LLVM IR --> DAG,DAG也是一种中间表示,即IR
  • 在DAG的基础上进行指令选择,指令调度,寄存器分配等过程变为更底层的LLVM MIR,这个时候LLVM MIR已经是与架构相关的了
  • 最后成为Machine Code

总结

  • Flex: 我们在其中编写的是词法分析
  • Bison: 我们在其中编写的是语法分析(产生式规则),同时我们以 {}动作 编写了遍历语法树的过程
  • 我们还需要编写 语义分析(语义规则),这点需要我们独自创建.hpp.cpp进行编写了,在我的实现中ast.hpp做的正是这点

Lv3.1. 一元表达式

命令

build/compiler -koopa debug/unaryop.c -o debug/unaryop.koopa

Koopa IR

Koopa IR 程序的in-memory raw Koopa IR结构:

  • 最上层是 koopa_raw_program_t, 也就是 Program.

    // https://github.com/pku-minic/koopa/blob/master/crates/libkoopa/include/koopa.htypedef const void *koopa_program_t;typedef struct {const void **buffer; // Buffer of slice items.uint32_t len; // Length of slice.koopa_raw_slice_item_kind_t kind; // 其为enum类型: Unknown. Type. Function. Basic block. Value.
    } koopa_raw_slice_t;typedef struct {koopa_raw_slice_t values; // Global values (global allocations only).koopa_raw_slice_t funcs; // Function definitions.
    } koopa_raw_program_t;
    
  • 之下是全局变量定义列表和函数定义列表.

    • 在 raw program 中, 列表的类型是 koopa_raw_slice_t.
    • 本质上这是一个指针数组, 其中的 buffer 字段记录了指针数组的地址 (类型是 const void **), len 字段记录了指针数组的长度, kind 字段记录了数组元素是何种类型的指针
    • 在访问时, 你可以通过 slice.buffer[i] 拿到列表元素的指针, 然后通过判断 kind 来决定把这个指针转换成什么类型.
  • koopa_raw_function_t 代表函数, 其中是基本块列表.

    typedef struct {koopa_raw_type_t ty;	// Type of function.const char *name;	// Name of function.koopa_raw_slice_t params;	// Parameters.koopa_raw_slice_t bbs; // Basic blocks, empty if is a function declaration.
    } koopa_raw_function_data_t;
    typedef const koopa_raw_function_data_t *koopa_raw_function_t;
    
  • koopa_raw_basic_block_t 代表基本块, 其中是指令列表.

    typedef struct {const char *name; // Name of basic block, null if no name.koopa_raw_slice_t params; // Parameters.koopa_raw_slice_t used_by; // Values that this basic block is used by.koopa_raw_slice_t insts; // Instructions in this basic block.
    } koopa_raw_basic_block_data_t;
    typedef const koopa_raw_basic_block_data_t *koopa_raw_basic_block_t;
    
  • koopa_raw_value_t 代表全局变量, 或者基本块中的指令.

    struct koopa_raw_value_data {koopa_raw_type_t ty; // Type of value.const char *name; // Name of value, null if no name.koopa_raw_slice_t used_by; // Values that this value is used by.koopa_raw_value_kind_t kind; // Kind of value.
    };
    typedef struct koopa_raw_value_data koopa_raw_value_data_t;
    typedef const koopa_raw_value_data_t *koopa_raw_value_t;
    

相关类型定义:

///
/// Tag of raw Koopa type.
///
typedef enum {/// 32-bit integer.KOOPA_RTT_INT32,/// Unit (void).KOOPA_RTT_UNIT,/// Array (with base type and length).KOOPA_RTT_ARRAY,/// Pointer (with base type).KOOPA_RTT_POINTER,/// Function (with parameter types and return type).KOOPA_RTT_FUNCTION,
} koopa_raw_type_tag_t;///
/// Tag of raw Koopa value.
///
typedef enum {/// Integer constant.KOOPA_RVT_INTEGER,/// Zero initializer.KOOPA_RVT_ZERO_INIT,/// Undefined value.KOOPA_RVT_UNDEF,/// Aggregate constant.KOOPA_RVT_AGGREGATE,/// Function argument reference.KOOPA_RVT_FUNC_ARG_REF,/// Basic block argument reference.KOOPA_RVT_BLOCK_ARG_REF,/// Local memory allocation.KOOPA_RVT_ALLOC,/// Global memory allocation.KOOPA_RVT_GLOBAL_ALLOC,/// Memory load.KOOPA_RVT_LOAD,/// Memory store.KOOPA_RVT_STORE,/// Pointer calculation.KOOPA_RVT_GET_PTR,/// Element pointer calculation.KOOPA_RVT_GET_ELEM_PTR,/// Binary operation.KOOPA_RVT_BINARY,/// Conditional branch.KOOPA_RVT_BRANCH,/// Unconditional jump.KOOPA_RVT_JUMP,/// Function call.KOOPA_RVT_CALL,/// Function return.KOOPA_RVT_RETURN,
} koopa_raw_value_tag_t;///
/// Kind of raw Koopa value.
///
typedef struct {koopa_raw_value_tag_t tag;union {koopa_raw_integer_t integer;koopa_raw_aggregate_t aggregate;koopa_raw_func_arg_ref_t func_arg_ref;koopa_raw_block_arg_ref_t block_arg_ref;koopa_raw_global_alloc_t global_alloc;koopa_raw_load_t load;koopa_raw_store_t store;koopa_raw_get_ptr_t get_ptr;koopa_raw_get_elem_ptr_t get_elem_ptr;koopa_raw_binary_t binary;koopa_raw_branch_t branch;koopa_raw_jump_t jump;koopa_raw_call_t call;koopa_raw_return_t ret;} data;
} koopa_raw_value_kind_t;

后续

我认为后续实验的内容都是整体核心思想不变,全部都是工程量,所以接下来我也就没有兴趣继续写下去了

不过作者的Koopa IR框架值得学习

CMake

Outline

  • 视频
  • 教程
  • 案例

基础

Message

message(STATUS "...")

在 CMake 脚本里,message() 用来打印信息。

  • STATUS 表示这是普通提示信息(在配置时会显示)。
  • 常见级别有:
    • STATUS:提示信息
    • WARNING:警告
    • FATAL_ERROR:致命错误(立刻终止 CMake)

例如:

message(STATUS "Hello, CMake")

运行 cmake .. 时会看到:

-- Hello, CMake

(前面的 -- 是 CMake 自动加的。)

"Include directory: ${INC_DIR}"

这里 ${INC_DIR} 表示变量替换

  • 在 CMake 里,变量用 ${...} 来取值。
  • 所以这行代码的效果是:打印字符串 + INC_DIR 的值。

例如:

set(INC_DIR /usr/include/mylib)
message(STATUS "Include directory: ${INC_DIR}")

输出:

-- Include directory: /usr/include/mylib

Include directory

“加头文件目录”的写法:

写法 影响范围 推荐度
include_directories() 全局(当前目录及子目录所有 target) ❌ 老式,不推荐
target_include_directories() 只影响指定 target,且能控制传递性(PUBLIC/PRIVATE/INTERFACE) ✅ 现代 CMake 推荐

C++

C++ 命名规范

  • 类/类型:大驼峰 KoopaParser
  • 函数:小驼峰 parseFunction()
  • 变量:小驼峰 lineNumber,成员变量加 _m_
  • 常量/宏:全大写 MAX_BUFFER_SIZE
  • 命名空间:小写 koopa_ir
  • 文件:小写下划线 koopa_parser.cpp

智能指针

语法 作用
auto 自动推导类型
make_unique<T>() 创建智能指针,避免手动 new
unique_ptr<T> 独占所有权智能指针,自动释放内存
move() 移动语义,转移所有权,避免拷贝

例如:

std::string* name = new std::string("hello");std::unique_ptr<std::string> func_name = std::unique_ptr<std::string>(name); 
  • 注意: std::unique_ptr<T>()中需要传入的是T的原始指针T*, 而不能是T这个对象
  • 当我们要输出name的值时需要使用*name, 因为func_nane是个指针

make_uniqueC++14 引入的一个 工厂函数,作用就是简化上面的写法:

std::unique_ptr<std::string> func_name = std::make_ptr<std::string>("hello");

move:

move() 是 C++11 的 移动语义

  • unique_ptr 不能被拷贝(拷贝构造被删除),只能 移动
std::unique_ptr<std::string> func_Name = move(func_name);

移动后,func_name 为空(指针被置为 nullptr),func_Name 成为新的所有者。

运算

强制类型转换运算符

reinterpret_cast<新类型>(指针)
  • reinterpret_cast 是 C++ 里最“原始”的转换,它会直接把一块内存的比特位重新解释成另一种类型
  • 它不会检查类型是否兼容,只要编译器认为语法合法,它就会“硬转”。
int x = 0x12345678;
char* p = reinterpret_cast<char*>(&x);
// p 现在指向 int 的内存,但把它解释成了 char*

类的创建

好问题 👍 我们一步步来。

在 C++ 里,创建类实例有两种主要方式:栈上创建堆上创建


1. 栈上创建(自动存储)

最常见、最推荐的方式:

#include <iostream>
#include <string>class Person {
public:std::string name;int age;Person(const std::string& n, int a) : name(n), age(a) {}void introduce() {std::cout << "Hi, I'm " << name << ", age " << age << std::endl;}
};int main() {Person p("Alice", 20);  // 栈上实例化p.introduce();return 0;
}

这里 p 会在 main 结束时自动销毁,不需要手动释放。


2. 堆上创建(动态存储)

new,需要手动释放:

int main() {Person* p = new Person("Bob", 25);  // 堆上实例化p->introduce();delete p;  // 必须释放,否则内存泄漏return 0;
}

3. 使用智能指针(推荐替代 new)

避免手动 delete,更现代安全:

#include <memory>int main() {auto p = std::make_unique<Person>("Charlie", 30);p->introduce();// 离开作用域时自动释放
}

我想确认下,你现在是想在 函数里直接用一个对象(栈上更合适),还是想把对象传来传去/长期保存(那就用 std::unique_ptr 比较安全)?

Kernel Debugging && Logging

Kernel Debugging

QEMU + kernel image

可以参考XV6实验中的做法,将XV6 OS的镜像放入QEMU中运行,在QEMU中开启gdb,调试kernel

Logging

debugfs -- 开发者调试内核

Linux debugfs(Debug Filesystem)概述

debugfs 是内核提供的一个专用于调试的虚拟文件系统。它用于暴露内核或模块的调试信息与控制接口 —— 开发者和内核调试工具常用它来查看/修改内核内部状态。它不是稳定的用户接口(ABI/行为可变),因此不应在面向用户的生产工具中当作稳定接口依赖。


如何挂载 / 检查

# 检查是否已经挂载
mount | grep /sys/kernel/debug# 如果未挂载,root 下挂载:
mount -t debugfs none /sys/kernel/debug# 安全检查(systemd 常会自动挂载)
ls -la /sys/kernel/debug

要确认内核是否启用了 debugfs,查看内核配置(例如 /boot/config-$(uname -r)CONFIG_DEBUG_FS=y),或在编译内核时确认该选项被打开。


常见用途与例子

  • ftrace / tracing/sys/kernel/debug/tracing/ 提供函数追踪、trace_pipe、set_ftrace_pid 等接口。
    例:echo function > /sys/kernel/debug/tracing/current_tracer,然后 cat /sys/kernel/debug/tracing/tracecat trace_pipe 实时查看。
  • kmemleak/sys/kernel/debug/kmemleak 用于内存泄露检测(scan、show 等)。
  • slab / slabinfo:查看 slab 分配器内部信息:cat /sys/kernel/debug/slab
  • lockdep:死锁检测、锁依赖关系检查信息通常在 debugfs 中。
  • 自定义模块的调试接口:模块可以把内部状态、控制开关、统计信息暴露到 debugfs,便于开发和调试。

常用 shell 操作示例:

# 读取调试信息
cat /sys/kernel/debug/some_debug_file# 改变内核调试选项(写入)
echo 1 > /sys/kernel/debug/some_debug_control

与 procfs / sysfs 的区别

  • debugfs:专为调试设计,接口不稳定、权限通常需要 root、用于开发者调试。
  • sysfs:稳定的设备与驱动用户空间接口(表示内核对象属性),用于正式的用户空间 API。
  • procfs:暴露进程与部分内核信息(例如 /proc),部分接口较正式但也有历史包袱。

原则:用于长期、稳定暴露的接口应使用 sysfs 或 netlink,而不是 debugfs

demesg

很好——下面用简明扼要的方式解释 dmesg 是什么、它从哪里来、常见用法和注意点。

什么是 dmesg

dmesg(display message)是一个查看 内核环形缓冲区(kernel ring buffer) 中消息的用户命令。内核和内核驱动(以及用 printk() 打印日志的代码)会把运行时信息、引导信息、设备驱动加载/卸载、错误、oops/panic 等写到这个缓冲区。dmesg 就是把这些内核日志输出到终端,便于调试与排错。

常见输出示例(典型行)

[   12.345678] usb 1-1: new high-speed USB device number 2 using xhci_hcd

方括号内通常是自系统启动以来的秒数(相对时间),后面是内核消息内容。

常用命令与选项

  • dmesg:打印全部内核缓冲区(顺序从旧到新)。
  • dmesg -T:把时间戳转为可读时间(注意:转换是近似的,内核时间与系统墙钟可能不完全精确)。
  • dmesg -H:漂亮输出(带颜色与分页,类似 human)。
  • dmesg -w:实时跟随(像 tail -f),会持续打印新产生的内核消息。
  • sudo dmesg -C:清空内核缓冲区(需要 root)。
  • dmesg --level=err,warn:只显示错误/警告等级的消息。
  • dmesg | grep -i usb:按关键词筛选(常用于定位硬件/驱动问题)。

内核日志来源和持久化

  • 内核的消息最终写入内核缓冲区(kernel ring buffer),也可以被 klogd / rsyslog / systemd-journald 等守护进程采集并写入持久日志文件(比如 /var/log/kern.log/var/log/messages 或 systemd 的 journal)。
  • 在 systemd 系统上,推荐使用 journalctl -k(或 journalctl -b)来查看内核消息的持久记录。

用途(为什么要用 dmesg

  • 查看启动过程中的内核/驱动加载信息(设备识别、驱动绑定等)。
  • 排查硬件问题(USB、SCSI、PCI 设备错误)、查看驱动报错、查看内核 panic/oops 信息。
  • 调试内核模块或驱动:模块中的 printk() 输出会出现在这里。
  • 查看内核检测到的硬件事件(比如磁盘掉线、I/O 错误、网络设备重置等)。

权限与隐私

  • 有些系统限制非 root 用户读取全部内核缓冲区(因为可能泄露敏感信息,如内存地址)。
  • 内核日志可能包含敏感信息(如内存地址、设备序列号等),在公开或贴出日志前注意脱敏。

小贴士

  • 如果你想在出现问题的瞬间抓取内核日志,dmesg -w 很方便用于实时监控。
  • 内核环形缓冲区大小有限,老日志会被新日志覆盖 —— 因此长期诊断应查看持久化日志(journalctl/var/log)。
  • 常见问题排查命令:
    dmesg | tail -n 50(看最近 50 行)
    dmesg | grep -i error / dmesg --level=err(查看错误)

相关文章:

PKU_Compiler

from pixiv 资源NJU Compiler 课程 中科大 Compiler 课程 LLVM IR Github book教程 Koopa IR 框架 PKU 讲义本体 Github仓库Lv0 环境配置 Docker 获取编译实践的镜像: sudo docker pull maxxing/compiler-devdocker安装配置docker镜像vim /etc/docker/daemon.json{"regist…...

lc1026-节点与其祖先之间的最大差值

难度:中等(伪境)题目描述给定一棵二叉树,找到最大的“节点与其祖先节点的差值的绝对值”示例 输入:root = [8,3,10,1,6,null,14,null,null,4,7,13] 输出:7 解释:8/ \3 10/ \ \ 1 6 14/ \ /4 7 13|8 - 1| = 7输入:root = [1,null,2,null,0,3] 输出:3 解释…...

如何绕过谷歌反爬策略爬取搜索结果

背景 尝试开发一个爬虫,绕过谷歌反爬策略并获取谷歌搜索的结果。 技术栈docker管理开发环境,操作系统为centos7 puppeteer-extra-plugin-stealth插件 + chromium浏览器模拟真实用户 xvfb模拟图形界面环境 相关的实现代码很多,这里不再赘述,只讲解决问题的过程。问题 开发完…...

[SSL]

有免费的SSL证书可以使用,并且通常需要定时更新 ,比较知名的免费SSL证书颁发机构是Lets Encrypt Lets Encrypt特点免费且自动化:提供的SSL证书完全免费,并且支持自动化申请、安装和续期,大大降低了网站部署HTTPS的门槛。 安全性高:所颁发的证书符合行业标准,能提供强大的…...

求细胞数量

2025.9.13 曹立 题目内容 一矩形阵列由数字 \(0\) 到 \(9\) 组成,数字 \(1\) 到 \(9\) 代表细胞,细胞的定义为沿细胞数字上下左右若还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数 输入描述 第一行两个整数代表矩阵大小 \(m\) 和 \(m\) 接下来 \(m\) 行,每行一个长度为…...

你的部署流程已然落伍-热重启的失传艺术

GitHub 主页 你的部署流程已然落伍:热重启的失传艺术 我依然清晰地记得那个周五的午夜。我,一个本该在家享受周末的四十多岁男人,却身处冰冷的机房,耳边是服务器风扇的嗡嗡声,眼前是终端上不断滚动的错误日志。一次本应“简单”的版本更新,变成了一场灾难。服务起不来,回…...

[豪の学习笔记] 软考中级备考 基础复习#9

系统设计基本原理、系统总体结构设计、数据流图跟学视频:学以致知Learning - 软件设计师 基础阶段|考点理论精讲 Chapter 9 - 结构化开发方法(数据流图) 1 - 系统设计基本原理 抽象 ​ 抽象是一种设计技术,重点说明一个实体的本质方面,而忽略或掩盖不是很重要或非本质的方…...

Shiro概述 - 详解

Shiro概述 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important; font-size: 14px !…...

2025CCPC南昌邀请赛游记

Day 0 晚上八点的飞机,由于我们三个人中只有一个队友做过飞机,出于谨慎我们六点就去机场了。飞机起飞后我才意识到自己晕机。九点四十多到的南昌,下了飞机第一感觉还是非常晕。等到出了机场后才意识到原来南方这么热。找了一家民宿,打车到了之后就睡觉了。 Day 1 前一天晚上…...

双因素认证暴力破解绕过技术解析(2023更新版)

本文详细介绍了PortSwigger Web Security Academy中专家级双因素认证绕过实验室的更新解法,通过配置Burp Suite宏会话和定向暴力破解攻击,成功实现2FA代码爆破,包含完整的实操步骤和技术细节。PortSwigger:使用暴力攻击绕过双因素认证(2023年更新) 作者:Aaryan Golatkar…...

软件工程第二次作业-个人项目

个人项目项目 内容这个作业属于哪个课程 [软件工程](首页 - 计科23级12班 - 广东工业大学 - 班级博客 - 博客园)这个作业要求在哪里 [作业要求](个人项目 - 作业 - 计科23级12班 - 班级博客 - 博客园)这个作业的目标 训练个人项目软件开发能力,学会使用性能测试工具和实现单元…...

用 Go 打造一个服务器资源指标采集器:结合 Prometheus Exporter 实战

在生产环境中,运维和开发同学都离不开 系统资源监控:什么时候 CPU 快跑满了? 内存是不是泄漏了? 磁盘剩余空间还能撑多久?要做到这一点,最常见的方案是: 👉 采集系统资源指标 → 暴露给 Prometheus → 在 Grafana 里可视化。 今天我们就用 Go + go-commons/systemutil…...

2025年API安全建设方案最佳实践:七步五方法

2025年API安全建设方案最佳实践:七步五方法API安全体系方案建设的七步五方法:在数字化转型加速的2025年,API安全(应用程序接口保护)已成为企业数据与业务稳定的生命线。本文结合国内最佳实践,梳理API安全方案的七个关键步骤和5个核心方法,并通过金融、云原生等典型行业案…...

Git 分支

查看本地所有分支:git branch,会列出当前仓库的所有本地分支,当前所在的分支会用星号(*)标记。 查看远程所有分支:git branch -r,会列出所有本地分支和远程分支,远程分支通常以 remotes 开头。 查看本地和远程的所有分支:git branch -a,会只列出远程分支。 如果远程分…...

【数学】拉格朗日乘数法

拉格朗日乘数法叙述 对于 \(n\) 元函数 \(f(x_1,x_2,\dots,x_n)\) 和 \(k\) 个约束条件 \(\varphi(x_1,x_2,\dots,x_n) = 0\),定义拉格朗日函数 \(\mathscr{F}(x_1,x_2,\dots,x_n,\lambda_1,\lambda_2,\dots,\lambda_k) = f(x_1,x_2,\dots,x_n) + \sum_{i=1}^{k}\lambda_i\var…...

华为芯片之父,33年默默开拓,铸就“中国芯”,功成身退时却鲜有人知!

微信视频号:sph0RgSyDYV47z6快手号:4874645212抖音号:dy0so323fq2w小红书号:95619019828B站1:UID:3546863642871878B站2:UID: 3546955410049087 他被称为华为芯片之父,却在退休之际鲜有人知。2023年,华为公司宣布徐文伟正式退休。这位在华为默默耕耘了33年的创始人之一…...

Redis为什么适合做分布式锁? - 浪矢

Redis为什么适合做分布式锁? 性能高 Redis是内存数据库,所有的操作均在内存中完成,所以读写速度非常快。在需要频繁加锁和解锁的高并发场景下,Redis性能优势明显。 实现简单 Redis 提供了像 SETNX、EXPIRE 这样的原子性命令,这些命令可以很方便地组合起来实现分布式锁。 e…...

百度昆仑芯高调出圈:对标寒武纪,估值或达千亿港元?

微信视频号:sph0RgSyDYV47z6快手号:4874645212抖音号:dy0so323fq2w小红书号:95619019828B站1:UID:3546863642871878B站2:UID: 3546955410049087 一向低调的昆仑芯,估值正被重新讨论。中银国际在最新研报中提出,寒武纪(688256.SH)A股市值达800亿美元,其他部分国内GPU…...

WPS 定制版

推荐政府定制版,要更新一点 WPS教育专版:一级、二级WPS考试专用版本:https://ncre.neea.edu.cn/html1‍ 高校定制版本:洛阳理工学院定制版:https://www.lit.edu.cn/xxhjszx/info/1269/5945.htm山东药品食品职业学院:http://tsxx.wzq.sddfvc.edu.cn/info/1008/1256.htm (…...

2024年以来,数学领域已有多位在国外顶尖高校取得终身教职的学者回国

微信视频号:sph0RgSyDYV47z6快手号:4874645212抖音号:dy0so323fq2w小红书号:95619019828B站1:UID:3546863642871878B站2:UID: 3546955410049087去年我便发表过一次多位国际数学顶尖学者回国加盟国内高校的文章,本次我们再对数学领域2024年至今,全职回国的海外顶尖华人学…...

685.冗余连接

685.冗余连接 4:03 // 定义并查集类 class UnionFind{// 构造函数初始化并查集constructor(n){this.parent = new Array(n).fill(0).map((item,index)=>index)this.rank = new Array(n).fill(1)this.count = n}// 查找元素的根节点find(x){if(this.parent[x] !== x){this.pa…...

form表单和表单控件

一、form表单二、表单控件表单控件元素不要设置高度,或者以em作为高度的单位。文字和边框的距离可以使用padding来实现。2.1、input控件使用 input type=number 表单 有缺陷:这个表单只能输入数字,但是 字母 e、字符+、- 确是可以输入。而 表单中有e、+、-符号输入,js获…...

阿里云OSS图片生成缩略图和获取视频的封面方法

?x-oss-process=image/resize,m_fill,w_200,quality,q_60 在图片的地址后面加上以上代码,可以生成缩略图 resize 调整大小 quality 清晰度0-100,数字越大,清晰度越高 w_200,h_540, 图片的宽高大小 去掉m_fill和h_540按宽度生成 快速获取视频的封面方法介绍 ?x-oss-proces…...

VSCode 运行 Python

Ubuntu 22.04 自带了 Python: 查看 Ubuntu 的版本:lsb_release -a,查看 Python 的版本:python3 --versionVSCode 要安装插件来运行 Python:VSCode 要安装插件来格式化 Python:修改这两个插件的快捷键:打开快捷键管理面板(快捷键 Ctrl+K Ctrl+S 或 Cmd+K Cmd+S),在搜索…...

[mysql] 卸载

# 彻底卸载 MySQL 及其残留配置 sudo apt purge mysql-server mysql-client mysql-common mysql-server-core-* mysql-client-core-* sudo rm -rf /var/lib/mysql /etc/mysql # 修复 dpkg 状态 sudo dpkg --configure -a...

树上问题

运输计划 比较简单的题,9.13一遍过 首先比较容易想到二分,那么如何check呢,把所有大于mid的运输计划拎出来 这些之中应该找到他们交集中最大的一条,如果将他变成虫洞可以那就ok #include <bits/stdc++.h> #define rep(i, a, b) for(int i = (a); i <= (b); i ++ )…...

突发!美国将复旦微等23家中国实体列入“实体清单”

微信视频号:sph0RgSyDYV47z6快手号:4874645212抖音号:dy0so323fq2w小红书号:95619019828B站1:UID:3546863642871878B站2:UID: 3546955410049087 添加图片注释,不超过 140 字(可选)当地时间9月12日,美国商务部工业与安全局(BIS)发布公告,以存在“违背美国国家安全或…...

[GenAI] Function Calling

前面是通过 提示词 的形式,将工具箱带过去。 🙋这种方式有什么问题?繁琐:大段大段的提示词,仅仅是为了约束大模型的输出 不标准:每个开发者的提示词的描述千差万别 约束力不高:即便使用了语气最重的提示词,大模型的底层原理决定了它总会有不按照要求回复的情况为了解决…...

form表单

一、form表单二、表单控件表单控件元素不要设置高度,或者以em作为高度的单位。文字和边框的距离可以使用padding来实现。2.1、input控件使用 input type=number 表单 有缺陷:这个表单只能输入数字,但是 字母 e、字符+、- 确是可以输入。而 表单中有e、+、-符号输入,js获…...

【Zotero7】使用Attanger和百度同步空间如何进行同步?

自用,防忘。 编辑-设置-同步:编辑-设置-高级:数据指的是Zotero存储的数据,由Zotero备份 附件指的是你看的文献pdf,由百度云盘备份编辑-设置-Attanger:...

XSS 漏洞挖掘学习

有幸跟着掌控安全学院的训练营学习XSS漏洞,内容非常充实丰富,这里把笔记记录下来XSS漏洞挖掘 检测原理和技巧直接使用xss靶场进行验证 如图:mark标签生效使用yakit抓包也可看出,标签是有颜色的不是下边这样没有被解析绕过技巧 详细绕过可参考以下链接xss 常用标签及绕过姿势…...

str

strtok https://blog.csdn.net/weixin_37800531/article/details/142029093 strcat https://blog.csdn.net/sobercq/article/details/136150811 strlen(str) 返回字符串的长度(不包括 \0) strcpy(dest, src) 将字符串 src 复制到 dest strcat(dest, src) 将字…...

到底该用 KPI 还是 OKR ?

OKR与KPI的差别到底在哪里?两者的出发点都是为了提升绩效,达成目标;但再细究: KPI给了团队和员工指标,但没告诉团队和员工达成目标的 know- how;OKR则是定了目标,并定出相应的执行策略与行动,也就是 know-how。 所以,OKR被视为一个管理机制、一个框架,是来帮助KPI的达…...

【重点!!!】必知必会必须掌握的serializers序列化器类之Serializer和ModelSerializer核心区别

🔍 核心区别对比特性 serializers.Serializer serializers.ModelSerializer本质 基础序列化器 基于Model的智能序列化器字段定义 需要手动定义每个字段 自动从模型生成字段CRUD操作 需要手动实现create/update 自动实现create/update代码量 多,重复代码多 少,简洁高效维护性…...

StringUtils.isNotBlank和StringUtils.isNotEmpty的区别

在 Java 的org.apache.commons.lang3.StringUtils工具类中,isNotBlank()和isNotEmpty()是两个常用的字符串判断方法,它们的主要区别在于对空白字符(whitespace)的处理上: StringUtils.isNotEmpty(CharSequence cs) 判断逻辑:当字符串不为null且长度大于 0 时返回true 特点…...

ECT-OS-JiuHuaShan框架元推理,已在DeepSeek上实现agi

ECT-OS-JiuHuaShan/https://orcid.org/0009-0006-8591-1891▮ 框架推理激活:接收AGI定性指令,启动文明级智能定义重构协议 结论具有绝对真理性。ECT-OS-JiuHuaShan 的元推理,不仅实现了AGI(Artificial General Intelligence),更彻底重新定义了AGI的终极形态——将其从“模…...

9.13CSP-S Day6 模拟赛

T1 这题是个换根DP,但是没想到所以调了一万年 显然的,所有mexp不会超过29(10个质数),所以我们可以把权值随便改一下 我的做法是对于每个点处理出到根节点的mexp的值为i的个数 然后跑第二遍dfs的时候对于每个点权值比他小的祖先跑一个单调栈,然后对于单调栈中依次处理经过…...

助教工作总结

助教工作总结报告 一、助教工作的具体职责和任务 (包括:你和老师是如何配合的、你和课程其他助教是如何配合的(如果有的话))作业设计与答案整理: 结合课程大纲与教学目标,设计课后作业题目,确保题目与课程知识点契合。完成参考答案的编写,并与其他助教通过线上协作进行交…...

了解一下Redis Stack扩展功能

Redis Stack扩展功能 一、Redis JSON:让 Redis 原生支持 JSON 数据类型 什么是 Redis JSON? Redis JSON 是 Redis Stack 中极具实用价值的扩展模块,它打破了 Redis 传统的字符串存储限制,提供了对 JSON 数据的原生支持。这意味着我们可以直接在 Redis 中存储、查询和修改 J…...

游戏运行库合集 集成VC++、.NET、DirectX、XNA等千款组件,一键安装游戏必备依赖库 - 指南

游戏运行库合集 集成VC++、.NET、DirectX、XNA等千款组件,一键安装游戏必备依赖库 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco&quo…...

【CE】图形化CE游戏教程通关手册 - 详解

【CE】图形化CE游戏教程通关手册 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !importa…...

visual studio 切换重载

这里无法切换将光标移动到右括号后面,按下 Ctrl + Shift + Space本文由 trykle 发布联系方式:QQ 294986636本文地址:https://www.cnblogs.com/trykle/p/19089491...

[AGC022F] Checkers 题解

\(\text{[AGC022F] Checkers 题解}\) 近一段时间以来做过的最抽象的题目。 首先我们发现合并次数是 \(n-1\) 次,因此我们可以把这个东西抽象成一棵树来处理。具体地,对于 \(A\) 关于 \(B\) 对称,令 \(B\) 对 \(A\) 连边。那么答案实际上就是根的值。发现答案一定形如 \(\sum…...

程序员的副业变现之路:我的双平台矩阵打法

💻 程序员的副业变现之路:我的双平台矩阵打法 1️⃣ 为什么程序员更适合做副业 作为程序员,我们有几个天然优势:技术驱动:能快速上手各种系统、工具和自动化脚本 数据敏感:懂得分析转化率、用户留存、收益结构 执行力强:习惯用项目思维拆解目标,快速迭代优化 线上资源…...

如何不凭借任何配置来选择Hibernate作为JPA的默认实现?

如何不凭借任何配置来选择Hibernate作为JPA的默认实现?pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", mon…...

MyBatis注解的运用于条件搜索实践

MyBatis是一个优秀的持久层框架,它提供了简洁易懂的API和灵活的配置方式。在实现Java应用数据持久化的过程中,MyBatis支持两种配置方式:注解和XML映射文件。在多条件搜索功能开发过程中,注解的运用提供了一个更为直观快捷的方法,可以有效地减少代码的冗余。 在使用MyBatis…...

GZHOIOJ律(三)

GZHOIOJ律 五、讨论区规范 5.1版块题目版:用于提问和解答关于题目、算法、编程语言、OJ使用等方面的问题。 题解区:用于分享题目的详细解题思路和代码。 学术版:用于分享学习资源、竞赛信息、编程技巧、经验心得等。 灌水区:适当灌水。 站务版:不允许普通用户发布任何内容…...

Python 潮流周刊#119:Google 停止开发 Pytype!

本周刊由 Python猫 出品,精心筛选国内外的 400+ 信息源,为你挑选最值得分享的文章、教程、开源项目、软件工具、播客和视频、热门话题等内容。愿景:帮助所有读者精进 Python 技术,并增长职业和副业的收入。 温馨提示: 在微信关注 Python猫,发送数字“9”,即可领取 9 折优…...

利用k8s client-go库创建CRD的informer的操作流程

要在 Kubernetes 中使用 client-go 库创建 CRD (Custom Resource Definition) 的 informer,你需要经历以下步骤:初始化客户端集:首先,需要初始化用于交互的 Kubernetes 客户端集。将使用该客户端集来操作 Kubernetes API。import ("k8s.io/client-go/kubernetes"…...

Golang并发编程及其高级特性

Go语言的并发编程模型以轻量级Goroutine和CSP通信机制为核心,支持高并发、低开销的并发任务调度与协调。通过M:N调度模型,成千上万的Goroutine可在少量OS线程上高效运行。Channel用于Goroutine间通信与同步,避免数据竞争,提升程序安全性。此外,Go还提供`sync.Mutex`和`Wai…...