👋 Welcome to Z’Blog

  • Hi, A Software Engineer here! 🤗
  • Passionate about exploring Linux 🐧, AI 🤖 and Software Development 💻.

Step by step to build a blog with PaperMod like mine

Follow Installation doc to install Hugo and PaperMod. Git Submodule Method is recommended, which keeps your repo independent and tidy by seperating your configs and posts from the theme’s source code. Notice: Don’t forget to add a .gitignore file in your repo, like this: 1 2 3 4 /public .DS_Store .hugo_build.lock resources/_gen/ Following are the tutorials of configuring PaperMod. You can reproduce the features by simply copy the corresponding code snippets or just clone my github page repo and replace my posts with yours....

June 30, 2024 · 6 min · 1209 words

Linux 内核添加自定义系统调用

网上的教程[1]大多使用老版本内核,许多内容已经不再适用了。本文依托于我的项目 mycall 进行讲解,旨在把自己踩过的坑全部记录下来,具体实现请参考源码。实验在 ubuntu 20.04 amd64 虚拟机(内核版本 5.15.0-105-generic)中进行,如有错误或建议(如不适用于 6.x 内核),欢迎在我的 Github Page repo 提 issue。 本文将先后介绍给内核添加自定义系统调用的两种方式: 通过内核模块将系统调用插入正在运行的内核中。 将系统调用添加到内核源码中,再重新编译安装内核。 通过内核模块添加系统调用 这个方法最简单但是坑也最多,因为内核开发组显然不希望我们通过内核模块修改/覆盖系统调用[2],并且做了诸多限制,为此我们只能用一些 trick。 定义系统调用 从 Linux 4.17 开始,x86 下系统调用服务例程只接收 struct pt_regs * 一个参数[9]。因此系统调用的定义为如下形式: 1 2 3 4 asmlinkage long sys_mycall(struct pt_regs *regs) { ... } 根据 Linux x86 calling convention,Linux 系统调用通过寄存器传递参数:rax 存储系统调用号,rdi 存储第一个参数, etc. pt_regs 就是一个包含了寄存器值的结构体[10],需要从中读取参数。 获取系统调用表地址 要插入系统调用,首先需要能够找到系统调用表的地址 sys_call_table。可惜 2.6 版本以后内核就不再 export sys_call_table 了[3],只能寻求其他办法: 从 System.map 读取 编译内核时生成的内核符号表中包含系统调用表的地址,可以通过以下命令获取: 1 cat /boot/System.map-$(uname -r) | grep sys_call_table 但是若内核开启了 KASLR (Kernel Address Space Layout Randomization),实际地址会和 System....

April 27, 2024 · 3 min · 501 words

Hadoop/HDFS 虚拟机集群部署及 MapReduce 实验

大数据软件生态分为三方面: 数据存储:Hadoop HDFS, HBase, KUDU, 阿里云 OSS, AWS S3 数据计算:Hadoop MapReduce, Hive, Spark, Flink 数据传输:Kafka, Pulsar, Flume, Sqoop Hadoop 是一个大数据整体解决方案,包括三大组件:分布式数据存储 HDFS,分布式数据计算 MapReduce 和分布式资源调度 YARN。本科的时候就做过 Hadoop 实验,几年之后对 Hadoop 是啥都几乎没概念了,刚好又需要做个实验,就记录一下实验过程,从简单的 URLs 统计看 Hadoop 和 HDFS 的基本功能。 本实验在虚拟机 VirtualBox 中完成,系统镜像为 Ubuntu。 虚拟机集群部署 准备虚拟机 首先准备三台虚拟机,主机名和配置如下。 节点 CPU Mem ubuntu-server-0 1 4GB ubuntu-server-1 1 2GB ubuntu-server-2 1 2GB 在 Vbox 中将虚拟机网络都配制成桥接模式,以便相互之间可以连通。 设置虚拟机固定 IP,免得重启后发生变动。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 # 1....

September 16, 2023 · 9 min · 1824 words

浅尝 SELinux

用 Fedora 的时候,发现系统默认启用了 SELinux,还动不动弹警告窗口,于是稍微研究了一下。 我们平常经常接触的存取控制机制叫做自主式存取控制 (DAC, Discretionary Access Control),如 user/group/other 对文件的 rwx 权限就是 DAC。SELinux 是一种强制式存取控制 (MAC, Mandatory Access Control) 的实现(没错,SELinux 只是最常见的一种 MAC,其实还有 TOMOYO、AppArmor 等等),能够提供更细粒度的权限控制,如某个类型的进程被允许读某个类型的文件。 SELinux 的实现依赖 LSM (Linux Security Modules)。LSM 在很多 Linux 内核服务(如程序执行、文件操作、socket操作、IPC操作、内存段、信号量、sysctl、syslog、audit)中插入了 hook,第三方的存取控制机制如 SELinux、AppArmor、SMACK、TOMOYO 就是利用这些 hook 实现的。 在 SELinux 下,每个进程和资源都被赋予了 SELinux context (i.e. SELinux label),利用这些 context 可以编写一系列 rules 来定义进程和进程之间、进程和资源之间允许哪些交互(白名单方式,除非你在 rule 中显式地允许了,否则 SELinux 默认不允许任何交互,这就是为什么叫做强制式),这些 rules 组合在一起就是 SELinux policy。需要注意的是,DAC 和 MAC 是合作而不是互斥关系,要在通过 DAC 的检查后才能检查 SELinux policy 的 rules ,也就是说如果你的访问连 DAC 都过不去那就不用劳烦 SELinux 了。...

July 13, 2023 · 21 min · 4373 words

虚拟地址空间:局部空间与全局空间 | 用户态与内核态

局部空间与全局空间 32 位机的虚拟地址位数为 32 位,每个任务都有自己独立的 4GB 虚拟地址空间。所谓独立,指的是不同任务相同的虚拟地址映射到不同的物理地址,本质上是依赖每个任务有自己的 PDT/PT。 这 4GB 虚拟地址空间又分为两部分:低端虚拟地址位于局部空间/用户空间、高端虚拟地址位于全局空间/内核空间。虚拟地址的高低端分界线依赖于操作系统的实现,如 Linux 的分界线位于 3GB 处,0 - 3GB 为低端虚拟地址,3GB - 4GB 为高端虚拟地址。 所有任务共享全局空间(所有任务的高端虚拟地址指向相同的内核页表),有各自的局部空间(LDTR 指向当前任务的 LDT,低端线性地址指向各自的页表)。 下图参考《x86 汇编语言 从实模式到保护模式》绘制,虚拟空间中 0 - 2GB 为局部空间,2 - 4GB 为全局空间,所有段均处于平坦模式。 用户态与内核态 特权级是处理器用来区分不同执行环境的一种机制。例如,在 x86 架构中,有四个特权级(Ring 0 到 Ring 3),其中 Ring 0(最高特权级)是内核态,操作系统内核在这里运行,拥有全部权限;Ring 3(最低特权级)是用户态,普通应用程序运行于此,权限受到限制。由固件负责实施特权级保护。 DPL:在描述符中指定,代表着一个段的特权级。 CPL:在 CS 段选择子中指定,代表着当前执行的代码段的特权级。 RPL:在段选择子中指定,代表请求者的特权级。 IOPL: 在 TSS 的 EFLAGS 中指定,代表着当前任务的输入输出特权级。 在绝大多数时候,请求者都是当前程序自己,因此 CPL = RPL。有时可能用户程序需要传入一个选择子作为参数,让内核例程代为访问,进入内核态后 CPL 变为 0,这时操作系统必须负责把这个选择子的 RPL 设置为用户程序自己的 CPL。 特权级在以下场景中发挥作用: 特权指令使用条件:CPL = 0。 访问数据段条件:CPL 和 RPL $\leq$ 目标 DPL。 访问代码段条件:除从高特权级例程返回外,不允许从高到低转移,因为操作系统不会引用可靠性比自己低的代码。一般来说,控制转移只允许发生在两个特权级相同的代码段之间(CPL 和 RPL = 目标 DPL);若目标代码段是依从的,可以从低到高,但转移后不允许改变 CPL(CPL 和 RPL $\geq$ 目标 DPL);还可以通过调用门从低到高(用 jmp far 不改变 CPL,用 call far 会把 CPL 提升到目标 DPL)。 访问调用门条件:目标 DPL $\leq$ CPL 和 RPL $\leq$ 调用门描述符 DPL。 态 !...

September 9, 2022 · 1 min · 206 words