xv6_lecture2 C in xv6

经典的一道指针和数组题: 假设 x 的地址为 0x7fffdfbf7f00, 打印出来的值分别是多少? #include <stdio.h> int main() { int x[5]; printf("%p\n", x); printf("%p\n", x+1); printf("%p\n", &x) printf("%p\n", &x+1); return 0; } 0x7fffdfbf7f00 # 打印的数组的地址 0x7fffdfbf7f04 # 打印的是数组第二个元素的地址 0x7fffdfbf7f00 # &x也是数组的地址 0x7fffdfbf7f14 # x + sizeof(x)的地址

2024-12-26 · 1 min

Professional CMake: A Practical Guide Part III The Bigger Picture

Chapter 23. Finding Things 中等规模以上的项目除了本身的项目之外, 可能还依赖于其他东西. 比如 a particular library or tool, location of a specific configuration file or a header for a library. 甚至项目可能需要找一个 package, 其中定义了一系列内容, 包括 targets, functions, variables… find_...命令提供了搜索 file、library 或 progaram,甚至 package 的能力. 23.1 Finding Files and Paths find_file(outVar name | NAMES name1 [name2...] [HINTS path1 [path2...] [ENV var]...] [PATHS path1 [path2...] [ENV var]...] [PATH_SUFFIXES suffix1 [suffix2 ...]] [NO_DEFAULT_PATH] [NO_PACKAGE_ROOT_PATH] [NO_CMAKE_PATH] [NO_CMAKE_ENVIRONMENT_PATH] [NO_SYSTEM_ENVIRONMENT_PATH] [NO_CMAKE_SYSTEM_PATH] [CMAKE_FIND_ROOT_PATH_BOTH | ONLY_CMAKE_FIND_ROOT_PATH | NO_CMAKE_FIND_ROOT_PATH] [DOC "description"] ) 搜索顺序按如下表格:...

2024-11-30 · 5 min

Professional CMake: A Practical Guide Part II Builds In Depth

Chapter 13. Build Type 13.1 Build Type Basics cmake 有以下几种 build type, 不同的 tpye 会导致 compiler 和 linker flags 不同: Debug: no optimization and full debug information. Release: typically full optimization and no debug information. RelWithDebInfo: 有优化 + debug info. MinRizeRel: 优化 size. 13.1.1 Single Configuration Generators 像 make, ninja, 每个 build directory 只支持一种 build type, 需要在编译时指定 cache variable CMAKE_BUILD_TYPE: cmake -G Ninja -DCMAKE_BUILD_TYPE:STRING=Debug ../source cmake --build . 一种可能的文件布局方式: 13.1.2 Multiple Configuration Generators 类似 Xcode and Visual Studio, 不关注, 跳过....

2024-11-29 · 13 min

Professional CMake: A Practical Guide Part I Fundamentals

Reference Professional CMake A Practical Guide version 1.0.0 2018 年出版, 基于 cmake 3.12 版本(2018-7-17). 目前该书出到了 19th Edition, 支持到 cmake 3.30, 不断更新中. Chapter 1. Introduction From Wikipedia: CMake is a free, cross-platform, software development tool for building applications via compiler-independent instructions. It also can automate testing, packaging and installation. It runs on a variety of platforms and supports many programming languages. Build 部分由其他不同的 build tool 负责, 比如 make, ninja, Visual Studio, XCode…...

2024-11-20 · 14 min

CSAPP - Chapter7 链接

7.2 静态链接 为了构造可执行文件,链接器必须完成两个主要任务: 符号解析。将每个符号引用和一个符号定义连接起来。 重定位。编译器和汇编器生成从 0 地址开始的代码和数据节。连接器通过把每个符号定义与一个内存位置关联起来,从而重定位这些节。然后修改所有对这些符号的引用,使得他们指向这个内存位置。 7.3 目标文件 可重定位目标文件 .o, .a(.a 就是一堆打包的.o) 可执行目标文件 elf 共享目标文件 .so 7.4 可重定位目标文件 .text 代码段 .rodata 只读数据段,保存字符串,const 数据等。 .data 数据段。全局和静态变量。 .bss 未初始化和初始化为 0 的全局和静态变量。不占空间。 .symtab 符号表,存放程序中定义和引用的函数和全局变量信息。 .rel.text .text 节中需要重定位的代码段。 .rel.data .data 节中需要重定位的数据段,被模块引用或定义的所有全局变量。 .debug 调试符号表。包含程序中定义的局部变量和类型定义,定义和引用的全局变量,原始的 C 源文件。 .line 原始 C 文件中的行号和.text 节中机器指令的映射。 .strtab 字符串表。 7.5 符号和符号表 由当前模块定义并能被其他模块引用的全局符号 其他模块定义并被当前模块引用的全局符号,称为外部符号 只被当前模块定义和引用的的局部符号 下面两个局部静态变量: int f() { static int x = 1; return x; } int g() { static int x = 2; return x; } 两个 x 分别在各自的函数中可见,这两个 x 都会保存到....

2024-08-07 · 5 min

CSAPP - Chapter6 存储器层次结构

6.1 存储技术 6.1.1 随机访问存储器 RAM SRAM DRAM ROM 6.1.2 磁盘存储 6.1.3 固态硬盘 大约十万次写会导致块损坏。 6.2 局部性 6.4 高速缓冲存储器 // todo: 添加截图 高速缓存确定一个请求是否命中,然后抽出被请求字的过程分为三步: 组选择 行匹配 字抽取 6.4.2 直接映射高速缓存 每组只有一行(E=1)。 // todo: 添加截图 6.4.3 组相连高速缓存 1<E<C/B 组选择 和直接映射高速缓存相同。 行匹配和字选择 每组的任何一行都可以包含任何映射到这个组的内存块。 所以高速缓存必须搜索组中的每一行,寻找一个有效行,其标记与地址中的标记相匹配。 行替换 行替换算法: LFU: Least frequently used. 替换过去某个时间窗口引用次数最少的一行。 LRU: Least recently used. 替换最后一次访问时间最久远的一行。 6.4.4 全相连高速缓存 //todo: 插图 E=CB,一个组包含所有行。 组选择 只有一个组,没有组索引。 行匹配和字选择 构建一个又大又快的相连高速缓存很困难,而且很昂贵。因此全相连高速缓存只适合做小的高速缓存,例如虚拟内存中的 TLB。 6.4.5 有关写的问题 写命中 假设我们要写一个已经缓存了的字 w(写命中)。 首先都会先更新高速缓存中的字 w,接着更新低一级的内存中的数据,有两种方式: write-through 直写。立即将 w 的高速缓存块写回到低一层中。 wirte-back 写回。尽可能地推迟更新,只有当替换算法要驱逐这个更新过的块时,才写到低一层去。 写回可以显著减少总线流量,但增加了复杂性,需要额外维护一个 dirty bit,表示这个高速缓存快块是否修改过。...

2024-07-28 · 1 min

CSAPP - Chapter5 优化程序性能

5.1 优化编译器的能力和局限性 gcc 优化等级: -O0:这是默认的优化等级,基本上不进行任何优化,以便于调试。源代码会被尽可能地转化为目标代码,而不会对程序的执行速度进行优化。 -O1:这是一个适度的优化等级,它会在编译时执行一些简单的优化,如常量折叠、死代码删除、循环展开等,但不会影响编译时间太多。这个等级的优化通常不会显著改变程序的行为。 -O2:这是最常见的优化等级,它会执行更多的优化,包括函数内联、循环优化、寄存器分配等,可能会稍微增加编译时间,但是通常可以产生更快的可执行文件。 -O3:这是最高级别的优化,它包含了-O2 中的所有优化,并且会执行更激进的优化策略,如更多的内联、循环展开等。这可能会显著增加编译时间,但同时也可能产生执行效率最高的代码。 -Os:这个优化等级专注于减少输出文件的大小,同时仍然保持一定的运行时性能。它会使用一些特定的技巧来减小代码和数据的大小,适合用于嵌入式系统或资源有限的环境。 -Og:这个优化等级主要用于调试,它会生成更多的调试信息,同时尽量保持源代码和目标代码的一致性,以便于调试。 -Ofast:这是为了追求极致性能的优化等级,它会启用所有可用的优化,并且可能会违反 C/C++语言标准的一些规则,比如浮点运算的精度可能会有所损失。 两种指针可能指向同一个内存位置的情况称为内存别名使用。 比如 void twiddle1(long *xp, long *yp) { *xp += *yp; *xp += *yp; } void twiddle2(long *xp, long *yp) { *xp = 2* *yp; } 在xp和yp指针不指向同一地址时,twiddle1可以被优化成twiddle2,但如果xp == yp,那结果就不一样了,twiddle1该地址的值会变成 4 倍,而twiddle2只是两倍。所以编译器对这种情况不能进行优化。 提供一个 combine1 函数,看看我们能如何来优化它。 // for add #define IDENT 0 #define OP + // for multiply #define INENT 1 #define OP * void combine1(vec_ptr v, data_t *dest) { long i; *dest = IDENT; for (i = 0; i < vec_length(v); i++) { data_t val; get_vec_element(v, i, &val); *dest = *dest OP val; } } int get_vec_element(vec_ptr v, long index, data_t *dest) { if (index < 0 || index >= v->Len) return 0; *dest = v->data[index]; return 1; } long vec_length(vec_ptr v) { return v->len; } 5....

2024-07-21 · 2 min

CSAPP - Chapter3 程序的机器级表示

3.3 数据格式 Intel用术语字来表示16位数据类型。 32位:双字。 64位:四字。 3.4 访问信息 %rbp是帧指针。 生成1字节和2字节数字的指令会保持剩下的字节不变; 生成4字节数字的指令会把高位的4个字节置为0。 3.4.1 操作数指示符 3.4.2 数据传送指令 MOV S, D: S->D 传送指令和的两个操作数不能都指向内存地址。 movabsq: 常规的movq指令只能以表示为32位补码数字的立即数作为源操作数。然后把这个值符号扩展得到64位的值,放到目的位置。 movabsq指令能够以任意64位立即数作为源操作数,并且只能以寄存器作为目的。 MOVZ S, R: 零扩展(S)->R MOVS S, R: 符号扩展(S)->R 这两个指令目的只能是寄存器。 cltq: 符号扩展(%eax)->%rax, 把%eax符号扩展到%rax。 3.4.4 压入和弹出栈数据 练习题 3.2 内存引用总是以4字节寄存器给出,比如%rax。 练习题 3.3 movl %eax, %rdx: 目的寄存器%rdx长度不匹配,应该为%edx。 3.5算术和逻辑操作 图3-10: 3.5.1 加载有效地址 如果%rdx保存的值为x leaq 7(%rdx, %rdx, 4), %rax: 将%rax寄存器的值设为5x+7。 指令形式是从内存读数据到寄存器,但实际上根本没有引用内存。 3.5.3 移位操作 移位量可以是一个立即数,或者放在单字节寄存器%cl中。(只允许以这个特定的寄存器) 3.5.5 特殊的算术操作 //TODO: 3.6 控制 3.6.1 条件码 CF: 进位标志。 ZF: 零标志。...

2024-06-07 · 3 min

CSAPP - Chapter2 信息的表示和处理

2.1.1 十六进制表示法 2的整数幂转化为16进制: 多少次方就有多少个零。 \(2048 = 2^{11}\) \(n = 11 = 3 +4*2\) 因此十六进制表示为0x800。 2.1.2 字数据大小 计算机字长等于指针数据的长度,32/64。 2.1.3 寻址和字节顺序 typedef unsigned char *byte_pointer; void show_bytes(byte_pointer start, size_t len) { size_t i; for (int i = 0; i < len; i++) printf("%.2x\n", start[i]); } int val = 0x87654321 byte_pointer valp = (byte_pointer)&val; show_bytes(valp, 1); // 结果是0x21, 小端法低地址保存的是数据低有效位 const char *s = "12345"; show_bytes((byte_pointer)s, strlen(s)); // 结果是31 32 33 34 35。没有字符串结尾的00,因为strlen不统计结束符。 // 注意字符串会以顺序排列,和字节序无关。 2....

2024-05-25 · 1 min

Operating Systems Three Easy Pieces(OSTEP) - Concurrency

Chapter26 Concurrency: Introduction 线程和进程类似,但多线程程序,共享 address space 和 data。 多线程各自独享寄存器组,切换进程的时候需要上下文切换。 进程上下文切换的时候把上一个进程的寄存器组保存到 PCB(Process Control Block)中,线程切换需要定义一个或更多新的结构体 TCBs(Thread Control Blocks)来保存每个线程的状态。 线程切换 Page table 不用切换,因为线程共享地址空间。 线程拥有自己的栈。 26.1 Why use threads? Parallelism。单线程程序只能利用一个 CPU 核,多线程程序可以并行利用多个 CPU 核,提高效率。 防止 I/O 操作导致程序 block。即使是单核 CPU,多线程程序可以在一个线程执行 I/O 操作的时候,其他线程继续利用 CPU。 如果是 CPU 密集型工作,单核 CPU 跑多线程对效率提升不大 26.2~26.4 Problem of Shared data 创建两个线程,分别对全局变量 counter 加 1e7, 最后结果会不等于 2e7。 #include <stdio.h> #include <pthread.h> #include "common.h" #include "common_threads.h" static volatile int counter = 0; void *mythread(void *arg) { printf("%s: begin\n", (char *) arg); int i; for (i = 0; i < 1e7; i++) counter = counter + 1; printf("%s: done\n", (char *) arg); return NULL; } int main(int argc, char *argv[]) { pthread_t p1, p2; printf("main: begin (counter = %d)\n", counter); Pthread_create(&p1, NULL, mythread, "A"); Pthread_create(&p2, NULL, mythread, "B"); // join waits for the threads to finish Pthread_join(p1, NULL); Pthread_join(p2, NULL); printf("main: done with both (counter = %d)\n", counter); return 0; } pthread_create: 创建线程。 pthread_join: 阻塞等待线程终止。...

2024-04-01 · 12 min