试试触发(几乎)所有信号
输入 kill -L,你可以看到 Linux 下所有可用的标准信号,总共有 31 个。 $ kill -L 1 HUP 2 INT 3 QUIT 4 ILL 5 TRAP 6 ABRT 7 BUS 8 FPE 9 KILL 10 USR1 11 SEGV 12 USR2 13 PIPE 14 ALRM 15 TERM 16 STKFLT 17 CHLD 18 CONT 19 STOP 20 TSTP 21 TTIN 22 TTOU 23 URG 24 XCPU 25 XFSZ 26 VTALRM 27 PROF 28 WINCH 29 POLL 30 PWR 31 SYS 这回试试在这些信号原本的应用场景下触发它们。...
Linux 进程的内存管理
虽然我们都学过一个进程的内存由堆和栈组成。但是这样的模型还是太抽象了,其中掩盖了许多操作系统的细节。所以这里简单梳理一下进程的内存管理有关知识。 堆的增长 libc 中提供 malloc 函数申请堆上内存。底层由 brk 系统调用负责申请堆上内存。 在内核的视角下,堆空间是一个简单的结构。它由一个固定的堆底(符号 end)和可变的堆顶(称为 program break)组成。内核所要做的就是根据用户设定的 program break 将堆底和堆顶之间的内存标为有效。而 brk 系统调用的作用便是将某一地址设置为堆顶。 在 brk 系统调用之上,glibc 提供了两个不同的函数 int brk(void *addr) 和 void *sbrk(intptr_t increment)。前者直接设置 program break 地址,后者则根据 increment 取值调整 program break 位置。 下面使用 sbrk() 函数进行内存分配。sbrk() 会返回调用之前原本的 program break 地址,因此还需额外进行加减以获取当前地址。这一语义比较合理,因为这样 brk(N) 和 malloc(N) 的返回值都代表新分配的内存的起始地址。 #include <stdio.h> #include <unistd.h> extern char end; int main() { printf("Address of end symbol: %p\n", (void *)&end); printf("Current program break: %p\n", sbrk(0)); printf("New program break: %p\n", sbrk(1024) + 1024); printf("After deallocation: %p\n", sbrk(-512) - 512); } 运行结果如下...
写个编译期排序
这回摆弄一下模板。 C++ 里有一个 std::integer_sequence。可以定义编译期整数序列。比如说 #include <utility> using my_seq = std::integer_sequence<int, 1, 2, 3, 4, 5>; 接下来写一个 trait seq_sort_t,实现编译期排序。类似于: using my_seq = std::integer_sequence<int, 2, 5, 3, 1, 4>; using sorted_my_seq = seq_sort_t<my_seq>; // std::integer_sequence<int, 1, 2, 3, 4, 5> 首先做一些准备工作。 运行时输出 通过类型萃取,将 std::integer_sequence 转换为运行时的 std::initializer_list。方便输出结果。 template <typename S> struct seq_to_init_list; template <typename T, T... Is> struct seq_to_init_list<std::integer_sequence<T, Is...>> { static constexpr std::initializer_list<T> value = {Is...}; }; template <typename S> constexpr auto seq_to_init_list_v = seq_to_init_list<S>::value; 用法如下:...
Corio - 基于 Asio 的 C++20 协程框架
最近研究了一下协程和网络编程,结合 C++20 的 Coroutine 和 Asio 编写了一个轻量级的协程框架 Corio。Corio 与 Asio 无缝集成,提供了多线程运行时支持和灵活的协程控制接口。 项目仓库:wokron/corio。 安装 corio 是一个 Header-only Library。因此只需要将本仓库 include/ 路径下的一系列头文件放在你的项目中指定位置,并将该位置添加到编译器的包含路径中即可。 以 CMake 为例,为了保持项目的模块化,可以选择将本项目作为 git 子模块。 git submodule add https://github.com/wokron/corio.git ./your/path/to/corio 随后在 CMakeLists.txt 中引入 corio。 add_library(corio INTERFACE) target_include_directories(corio INTERFACE ./your/path/to/corio/include) target_link_libraries(corio INTERFACE ${ASIO_LIBRARY}) 如前所述,Corio 依赖了 Asio。因此为了使用 Corio,还需安装 Asio(非 Boost 版本)。 最后,在你的代码中包含 corio.hpp 头文件以引入 corio。corio 的所有功能均位于 corio:: 命名空间下。 #include <corio.hpp> 使用 协程类型 lazy corio::Lazy<T> 是 corio 库的核心。通过将函数的返回值设定为 Lazy<T>,我们将该函数定义为协程。在 Lazy<T> 所定义的协程中,不再使用 return,而是使用 co_return 以便从协程中返回。其中 T 为该协程的返回值类型。...
编译 Tensorflow 踩坑
前段时间发现了 Tensorflow 里的一处小 Bug,现在有空正好提一个 PR。Bug 很快就修好了,不过之后进行本地编译时我却踩了不少坑。现在记录一下。 一、各种版本傻傻分不清 在开始编译之前,需要介绍一下相关的 Nvidia GPU 依赖项。 Nvidia 有不同架构的各型显卡。为了区分硬件上的区别,Nvidia 使用计算能力(Compute Capability)加以区分。计算能力版本分为两部分 x.y。大版本号表示计算架构(如 Pascal、Volta、Ampere 等等)上的变化,之间不可兼容;小版本号则表示同一架构内部的差别,更高版本可以兼容更低版本。 GPU 驱动(GPU Driver)为操作系统提供硬件驱动。其版本可以通过 nvidia-smi --query-gpu=driver_version --format=csv 找到。同一版本的驱动支持一系列不同计算能力、不同架构的显卡。 CUDA 驱动(CUDA Driver)在 GPU 驱动之上提供了 CUDA 接口。与 GPU 驱动属于内核态设备驱动不同,CUDA 驱动是一用户态的动态链接库(DSO)。CUDA 驱动的版本一般应当随着 GPU 驱动版本的更新而更新。 CUDA Toolkit 提供了构建 CUDA 程序所需的编译器、运行时和库。构建后的 CUDA 应用程序依赖于 CUDA 驱动所提供的接口。又由于 CUDA 是向后兼容(Backward Compatibility)的,所以旧的 CUDA 应用程序可以运行在新的 CUDA 驱动上;换句话说,要运行某一 CUDA 程序,需要高于特定版本的 CUDA 驱动。 向后兼容中的 Backward 指的是与时间上在前的进行兼容。这似乎是中英文导致的思维差异。 二、构建配置 在构建 Tensorflow 时添加 CUDA 支持后,Tensorflow 需要满足 CUDA Toolkit 和 CUDA 驱动之间的兼容性要求。如前所述,为了更强的兼容性,我们希望 CUDA Toolkit 的版本较低。但更高的 Tensorflow 版本又会需要更高版本的 CUDA 特性。因此在编译时需要在这两方面进行权衡。...
讨论一下环境变量
这里所说的环境变量并不仅仅指 Shell 中的变量,而是每一个进程各自拥有的,能够通过系统 API 获取的变量。当然,Shell 通常会提供环境变量的操作方法,我们也经常通过在 Shell 中管理环境变量。但是 Shell 中的变量和环境变量实际上并不完全相同,在使用 Shell 时可能会混淆这两种概念,这里便稍微分辨一下。 一、环境变量 从程序的角度来看,环境变量很简单。环境变量是每个进程各自拥有的键值对集合。进程可以从其环境变量中读取已有变量,修改已有变量或创建新的变量。当进程创建其子进程时,子进程会继承父进程的环境变量,但子进程的环境变量的修改并不会影响父进程的环境变量。 为了方便,这里以 python 为例。 进程可以读取已有环境变量: # inherit.py import os print(os.environ["HOME"]) 也可以写入或创建环境变量: os.environ["MY_VAR"] = "my_value" 子进程会继承父进程的环境变量: # env.py import os import sys assert len(sys.argv) == 2 father_proc = bool(int(sys.argv[1])) print_my_var = lambda: print( f"process={'father' if father_proc else 'child'}, " f"MY_VAR={os.environ.get('MY_VAR', None)}" ) print_my_var() if father_proc: os.environ["MY_VAR"] = "1" print_my_var() os.system(f"{sys.executable} {sys.argv[0]} 0") print_my_var() else: os.environ["MY_VAR"] = "2" print_my_var() $ python inherit....