三年前,小方离开了某互联网大厂加入了一个外企,之后工作和生活相对balance,我开始在技术上做了一些收拢。我选择了底层技术原理方向深挖。
我之所以做这样的决定是:
意识到工作是永远做不完,工作只是谋生的手段之一,而不是生活的全部的意义
年龄变大,需要兼顾身体和家庭
技术更新换代带快,新式技术和框架层出不穷,一味地追求新技术,永远只能浮于表明,也没法培养自己的核心技术能力,而基础技术原理长期适用和实用
中美科技竞争越来越激烈,国家对掌握基础和核心技术的人才越来越重视,需求越来越多,尤其是国产信创大环境背景下
希望我的一些思考能对其他读者,尤其是担忧自己三十五岁危机的技术同学,有一些启发。
在 2024年 我深入地把linux 5.x的内核代码关于网络协议栈等部分深入地阅读和调试了一遍。在学习内核的过程中也发现了一些好书,其中有一本爱不释手,已经过去的春节一边阅读,一边翻译,现在它终于完工了,我把它做成专栏放在小方说服务器开发知识星球中供小伙伴阅读,专栏这两天将会上线,这个专栏的主要是linux内核调试和新模块开发,特点是非常细致,行文娓娓道来,如同与一位老友喝茶聊天,对新手学习和入门linux内核调试和开发非常友好,更让人惊喜的是,其使用的linux内核源码版本为5.x代码,非常新。毕竟讲操作系统原理和linux低版本内核的资料和书籍太多了,而新的内核变化如此之多,操作系统的理论知识我们也学的太多了,纸上得来终觉浅,与实际试一试差别很大。
以下是详细目录:
前言
第一部分:基础内容
第1章:内核工作区设置
技术要求
在虚拟机(VM)中运行Linux
安装64位Linux虚拟机
开启x86系统的虚拟化扩展支持
为磁盘分配足够空间
安装Oracle VirtualBox Guest Additions
试用树莓派(Raspberry Pi)
设置软件发行版和软件包
安装软件包 安装Oracle VirtualBox Guest Additions 安装所需软件包 安装交叉工具链和QEMU 安装交叉编译器 重要安装说明
其他有用的项目
使用Linux手册页 tldr变体 查找和使用Linux内核文档 从源代码生成内核文档 Linux内核静态分析工具 Linux跟踪工具包下一代(Linux Trace Toolkit next generation) procmap 实用程序 简单嵌入式ARM Linux系统开源项目 使用[e]BPF进行现代跟踪和性能分析 LDV(Linux驱动程序验证,Linux Driver Verification)项目
总结
问题
延伸阅读
第2章:从源代码构建5.x Linux内核 - 第1部分
技术要求
内核构建的前期准备
内核版本命名规则 内核开发工作流程 - 基础内容 内核源代码树的类型
从源代码构建内核的步骤
步骤1 - 获取Linux内核源代码树
下载特定的内核树 克隆Git树
步骤2 - 解压内核源代码树
内核源代码树简要介绍
步骤3 - 配置Linux内核
理解kbuild构建系统 生成默认配置 获取内核配置的良好起点 典型嵌入式Linux系统的内核配置 以发行版配置为起点的内核配置 通过 localmodconfig方法优化内核配置开始使用 localmodconfig方法通过 make menuconfig用户界面优化我们的内核配置make menuconfig用户界面的示例用法更多关于kbuild的内容 查找配置差异
自定义内核菜单 - 添加我们自己的菜单项
Kconfig*文件在 Kconfig文件中创建新菜单项关于Kconfig语言的一些细节
总结
问题
延伸阅读
第3章:从源代码构建5.x Linux内核 - 第2部分
技术要求
步骤4 - 构建内核映像和模块
步骤5 - 安装内核模块
在内核源代码中定位内核模块 安装内核模块
步骤6 - 生成初始内存盘(initramfs)映像和引导加载程序设置
在Fedora 30及更高版本上生成initramfs映像 深入了解initramfs映像的生成过程
理解initramfs框架
为什么使用initramfs框架? 理解x86上引导过程的基础知识 更多关于initramfs框架的内容
步骤7 - 自定义GRUB引导加载程序
自定义GRUB - 基础内容 选择默认引导的内核 通过GNU GRUB引导加载程序引导我们的虚拟机 试用GRUB提示符
验证我们新内核的配置
树莓派的内核构建
步骤1 - 克隆内核源代码树 步骤2 - 安装交叉工具链 第一种方法 - 通过 apt安装软件包第二种方法 - 通过源代码仓库安装 步骤3 - 配置和构建内核
内核构建的其他提示
最低版本要求 为其他站点构建内核 监控内核构建过程 构建过程的快捷Shell语法 处理编译器开关问题 处理缺少OpenSSL开发头文件的问题
总结
问题
延伸阅读
第4章:编写你的第一个内核模块 - LKMs第1部分
技术要求
理解内核架构 - 第1部分
用户空间和内核空间 库和系统调用API 内核空间组件
探索可加载内核模块(LKMs,Loadable Kernel Modules)
LKMs框架 内核源代码树中的内核模块
编写我们的第一个内核模块
介绍我们的“Hello, world” LKM C代码 详细解析 0/-E返回约定 ERR_PTR和PTR_ERR宏__init和__exit关键字内核头文件 模块宏 入口和出口点 返回值
内核模块的常见操作
构建内核模块 运行内核模块 快速初识内核 printk()函数列出活动的内核模块 从内核内存中卸载模块 我们的lkm便利脚本
理解内核日志记录和printk
使用内核内存环形缓冲区
内核日志记录和systemd的
journalctl使用
printk日志级别pr_<foo>便利宏连接到控制台 将输出写入树莓派控制台 启用 pr_debug()内核消息限制
printk实例的速率从用户空间生成内核消息
通过
pr_fmt宏标准化printk输出可移植性和
printk格式说明符
理解内核模块Makefile的基础知识
总结
问题
延伸阅读
第5章:编写你的第一个内核模块 - LKMs第2部分
技术要求
一个更好的内核模块Makefile模板
配置“调试”内核
交叉编译内核模块
为交叉编译设置系统 尝试1 - 设置“特殊”环境变量 尝试2 - 将Makefile指向目标的正确内核源代码树 尝试3 - 交叉编译我们的内核模块 尝试4 - 交叉编译我们的内核模块
收集最小系统信息
提高安全意识
内核模块的许可
为内核模块模拟“类似库”的功能
通过多个源文件进行库模拟 理解内核模块中的函数和变量作用域 理解模块堆叠 试用模块堆叠
向内核模块传递参数
声明和使用模块参数 插入后获取/设置模块参数 模块参数的数据类型和验证 验证内核模块参数 覆盖模块参数的名称 与硬件相关的内核参数
内核中不允许使用浮点数
系统启动时自动加载模块
模块自动加载 - 更多细节
内核模块和安全性 - 概述
影响系统日志的 proc文件系统可调参数内核模块的加密签名 完全禁用内核模块
内核开发者的编码风格指南
为内核主线做出贡献
开始为内核做出贡献
总结
问题
延伸阅读
第二部分:理解和使用内核
第6章:内核内部基础 - 进程和线程
技术要求
理解进程和中断上下文
理解进程虚拟地址空间(VAS,Virtual Address Space)的基础知识
组织进程、线程及其堆栈 - 用户空间和内核空间
用户空间组织 内核空间组织 总结当前情况 查看用户和内核堆栈 查看给定线程或进程的内核空间堆栈 查看给定线程或进程的用户空间堆栈 查看堆栈的传统方法 [e]BPF - 查看两种堆栈的现代方法 进程VAS的宏观视图
理解和访问内核 task 结构
查看任务结构
使用
current访问 task 结构确定上下文
通过current使用 task 结构
内置内核辅助方法和优化 试用打印进程上下文信息的内核模块 认识到Linux操作系统是单体内核 使用 printk进行安全编码
遍历内核的任务列表
遍历任务列表I - 显示所有进程 遍历任务列表II - 显示所有线程 区分进程和线程 - 线程组ID(TGID,Thread Group ID)和进程ID(PID,Process ID) 遍历任务列表III - 代码实现
总结
问题
延伸阅读
第7章:内存管理内部基础
技术要求
理解虚拟内存分割(VM split)
深入分析“Hello, world” C程序
超越 printf()API64位Linux系统上的虚拟内存分割
虚拟寻址和地址转换 进程VAS - 完整视图
探究进程虚拟地址空间
详细探究用户虚拟地址空间 procmap进程VAS可视化实用程序解释 /proc/PID/maps输出vsyscall页面使用 procfs直接查看进程内存映射查看进程内存映射的前端工具 理解虚拟内存区域(VMA,Virtual Memory Area)基础
检查内核段
32位系统上的高端内存 编写一个内核模块来显示内核段的信息 空陷阱页 通过 dmesg在树莓派上查看内核段描述内核段布局的宏和变量 试用 - 查看内核段详细信息 通过 procmap查看内核VAS试用 - 查看用户段 查看关于内存布局的内核文档
随机化内存布局 - 内核地址空间布局随机化(KASLR,Kernel Address Space Layout Randomization)
用户模式地址空间布局随机化(User-mode ASLR) KASLR 使用脚本查询/设置KASLR状态
物理内存
物理随机存取存储器(RAM,Random Access Memory)组织 节点 内存区域(Zones) 直接映射的RAM和地址转换
总结
问题
延伸阅读
第8章:模块作者的内核内存分配 - 第1部分
技术要求
介绍内核内存分配器
理解和使用内核页分配器(或伙伴系统分配器,BSA,Buddy System Allocator)
页分配器的基本工作原理
最简单的情况 更复杂的情况 失败的情况 空闲列表组织
页分配器的工作过程
分析一些场景
页分配器内部 - 更多细节
学习如何使用页分配器API
确切的页分配器API 处理GFP标志 使用页分配器释放页面 编写一个内核模块来演示使用页分配器API 部署我们的 lowlevel_mem_lkm内核模块页分配器和内部碎片 深入研究GFP标志
在中断或原子上下文中绝不能睡眠
理解和使用内核slab分配器
对象缓存的概念 学习如何使用slab分配器API 分配slab内存 释放slab内存 数据结构 - 一些设计技巧 实际用于 kmalloc的slab缓存编写一个使用基本slab API的内核模块
kmalloc API的大小限制
测试限制 - 单次调用内存分配 通过 /proc/buddyinfo伪文件检查
slab分配器 - 一些其他细节
使用内核的资源管理内存分配API 额外的slab辅助API 控制组和内存
使用slab分配器的注意事项
背景细节和结论 使用 ksize()测试slab分配 - 案例1使用 ksize()测试slab分配 - 案例2解释案例2的输出 绘制图表 内核中的slab层实现
总结
问题
延伸阅读
第9章:模块作者的内核内存分配 - 第2部分
技术要求
创建自定义slab缓存
在内核模块中创建和使用自定义slab缓存 创建自定义slab缓存 使用新的slab缓存内存 销毁自定义缓存 自定义slab - 一个演示内核模块 理解slab收缩器 slab分配器的优缺点总结
在slab层进行调试
通过slab中毒进行调试 试用 - 触发一个使用后释放(UAF,Use-After-Free)漏洞 在引导和运行时的SLUB调试选项
理解和使用内核vmalloc() API
学习使用 vmalloc系列API关于内存分配和请求调页的简要说明 vmalloc()的相关函数指定内存保护 测试 - 一个快速的概念验证 为什么将内存设置为只读? kmalloc()和vmalloc()API的快速比较
内核中的内存分配 - 何时使用哪些API
可视化内核内存分配API集 选择合适的内核内存分配API 关于直接内存访问(DMA,Direct Memory Access)和连续内存分配器(CMA,Contiguous Memory Allocator)的说明
内存不足(OOM,Out-Of-Memory)杀手
回收内存 - 内核的清理任务和OOM 故意调用OOM杀手 通过Magic SysRq调用OOM杀手 使用疯狂的分配器程序调用OOM杀手 理解OOM杀手背后的原理 案例1 - vm.overcommit设置为2,关闭过度提交案例2 - vm.overcommit设置为0,开启过度提交(默认设置)请求调页和OOM 理解OOM分数
总结
问题
延伸阅读
第10章:CPU调度器 - 第1部分
技术要求
学习CPU调度内部原理 - 第1部分 - 基本背景知识
Linux上的KSE是什么? POSIX调度策略
可视化流程
使用 perf可视化流程通过其他(命令行界面,CLI,Command-Line Interface)方法可视化流程
学习CPU调度内部原理 - 第2部分
理解模块化调度类
查询调度类
关于完全公平调度器(CFS,Completely Fair Scheduler)和虚拟运行时(vruntime)值的说明
线程 - 调度策略和优先级
学习CPU调度内部原理 - 第3部分
谁运行调度器代码? 调度器何时运行? 上下文切换 定时器中断部分 进程上下文部分 可抢占内核 CPU调度器入口点
总结
问题
延伸阅读
第11章:CPU调度器 - 第2部分
技术要求
使用LTTng和trace-cmd可视化流程
使用LTTng和Trace Compass进行可视化 使用LTTng记录内核跟踪会话 使用图形用户界面(GUI,Graphical User Interface)报告 - Trace Compass 使用 trace-cmd进行可视化使用 trace-cmd record记录示例会话使用 trace-cmd report(命令行界面)进行报告和解释使用图形用户界面前端进行报告和解释
理解、查询和设置CPU亲和性掩码
查询和设置线程的CPU亲和性掩码 使用 taskset(1)设置CPU亲和性设置内核线程的CPU亲和性掩码
查询和设置线程的调度策略和优先级
在内核中 - 对内核线程进行设置
使用控制组(cgroups)进行CPU带宽控制
在Linux系统上查找cgroups v2 试用 - cgroups v2 CPU控制器
将主线Linux转换为实时操作系统(RTOS,Real-Time Operating System)
为x86_64上的主线5.x内核构建实时Linux(RTL,Real-Time Linux) 获取RTL补丁 应用RTL补丁 配置和构建RTL内核 主线和RTL - 技术差异总结
延迟及其测量
使用 cyclictest测量调度延迟获取并应用RTL补丁集 在设备上安装 cyclictest(和其他所需软件包)运行测试用例 查看结果 使用现代BPF工具测量调度器延迟
总结
问题
延伸阅读
第三部分:深入探究
第12章:内核同步 - 第1部分
临界区、互斥执行和原子性
什么是临界区? 一个经典案例 - 全局 i++概念 - 锁 要点总结
Linux内核中的并发问题
多核对称多处理(SMP,Symmetric Multi-Processing)系统和数据竞争 可抢占内核、阻塞I/O和数据竞争 硬件中断和数据竞争 锁定准则和死锁
互斥锁(Mutex)还是自旋锁(Spinlock)?何时使用哪个
理论上确定使用哪个锁 实际中确定使用哪个锁
使用互斥锁
初始化互斥锁 正确使用互斥锁 互斥锁的加锁和解锁API及其用法 互斥锁 - 通过[不可]中断睡眠? 互斥锁示例驱动程序 互斥锁 - 剩余要点 互斥锁的 trylock变体互斥锁的可中断和可终止变体 互斥锁的I/O变体 互斥锁API变体 信号量(Semaphore)和互斥锁 优先级反转和实时互斥锁(RT-mutex) 内部设计
使用自旋锁
自旋锁的简单用法 自旋锁示例驱动程序 测试 - 在原子上下文中睡眠 在5.4调试内核上测试 在5.4非调试发行版内核上测试
锁与中断
使用自旋锁的快速总结
总结
问题
延伸阅读
第13章:内核同步 - 第2部分
使用atomic_t和refcount_t接口 649
较新的 refcount_t与较旧的atomic_t接口 650更简单的 atomic_t和refcount_t接口 652在内核代码库中使用 refcount_t的示例 65464位原子整数操作符 656
使用读-修改-写(RMW)原子操作符 658
读-修改-写原子操作 - 对设备寄存器进行操作 658 使用读-修改-写按位操作符 661 使用按位原子操作符 - 示例 663 高效搜索位掩码 666
使用读写自旋锁 666
读写自旋锁接口 667
注意事项 669
读写信号量 670
缓存影响和伪共享 671
使用每个CPU变量的无锁编程 673
每个CPU变量 674 分配、初始化和释放每个CPU变量 675 对每个CPU变量执行I/O(读和写) 676 使用每个CPU变量 675 每个CPU变量 - 一个内核模块示例 678 内核中每个CPU变量的使用 682
内核中的锁调试 684
配置用于锁调试的调试内核 685 锁验证器 lockdep- 尽早捕获锁问题 687示例 - 使用 lockdep捕获死锁错误 690修复它 694 示例1 - 使用 lockdep捕获自死锁错误 690示例2 - 使用 lockdep捕获AB - BA死锁 695lockdep- 注释和问题 700lockdep注释 700lockdep问题 701锁统计信息 702 查看锁统计信息 702
内存屏障 - 简介 704
在设备驱动程序中使用内存屏障的示例 705
总结 707
问题 708
进一步阅读 708
加入星球方式:
专栏对全体小方说服务器开发知识星球球友开发,加入星球后,看置顶帖子即可阅读。
星球提供五大服务:
优问优答
不定期的技术直播和录像
优质源码分享和指导
模拟面试、职业解惑和简历review
星球专属技术专栏。
小方说服务器开发知识星球详细介绍点击。
如果你还不是球友,原价325一年,现在可以通过下面的优惠券扫码加入,立省40元:
老球友续费扫码如下(可在半价的基础上再优惠20元):
推荐阅读
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……




发表评论