
简介Linux 4.12长期支持版内核源码包面向Linux内核学习者、嵌入式与驱动开发人员适合用于阅读源码、掌握操作系统底层机制及定制化系统构建该版本为LTS可获得更长期更新与安全维护。压缩包共2000个文件以1857个h头文件和132个c源文件为主另含6个txt及5个doc说明文档整包约184.94MB头文件定义了各类内核接口与数据结构C文件实现了进程调度、内存管理、文件系统、网络协议栈和设备驱动等核心模块少量文档则提供编译与使用提示。源码目录遵循标准内核布局包含arch、drivers、fs、include、init、kernel、net与scripts等模块便于按需查阅和裁剪实验已有231人浏览学习适合以此为蓝本开展内核编译、驱动调试、源码分析乃至二次开发实践。1. linux-4.12(长期版).zip 这个压缩包里装的到底是什么拿到这个包的人通常不是随便下个最新内核来编译着玩的。带“长期版”三个字说明它承担的是某一类需要稳定交付的任务嵌入式或老平台适配项目里要一条维护周期比普通版本长、补丁持续回移的基线教学和实验环境里需要一份不带 git 历史、可以直接解压就阅读的完整源码树也有不少研发团队会把某个达成过内部验收的内核版本整体打包归档作为后续定制开发的分叉点。zip 封装格式比内核官方发布的 tar.xz 更常见于 Windows 和 Linux 混用的办公网段镜像站、交付光盘、网盘同步的源头决定了它大概率不是纯官方产物下载回来第一步就是校验。这个包解决三类问题搭一个能编译出可启动内核的环境、精读调度器和内存管理等核心子系统的源码、把配置改成项目需要的形态并产出 vmlinuz 和模块。适合内核入门者、嵌入式开发工程师、做系统定制交付的工程师。4.12 属于老基线新硬件驱动基本指望不上这是用长期版的代价后面所有操作都围绕“在旧基线上做出可用产物”展开。2. 解压 linux-4.12(长期版).zip 前的校验与源码目录定位不管是交付出来的镜像还是从镜像站拉下来的包拿到任何内核源码压缩包的固定第一步都是先校验、再解压、然后确认版本号。长期版尤其要小心同一名字在不同站点可能对应不同补丁级别校验信息对不上时宁可换源不要将就。2.1 用 sha256sum 做完整性校验别只看文件大小官方主线版本的校验和发布在源码站对应版本的 sha256sums 文件里企业定制包也会在交付说明或随包文本里给一份。校验命令只有一行sha256sum linux-4.12(长期版).zip # 输出示例b8f0b5a4c1e8d7f2... linux-4.12(长期版).zip # 与交付文档逐字符比对有差异就丢弃sha256sum属于 coreutils所有发行版默认都有不用额外安装。这一步看着多余但源码包传输中只要坏一个字节编译期就会报出极其隐蔽的语法错误或直接让编译器崩溃那时候再排查成本高一个数量级。顺便说一句文件名里的中文和括号在大部分 Linux 文件系统上都没问题但在 FAT32 和 NTFS 挂载分区上解压时可能出现文件名编码问题也就是常见的 linux 解压文件乱码内核源码本身是纯 ASCII 命名不会触发可包内常见的中文说明文档会中招解法是让 unzip 明确按 GBK 解码unzip -O gbk linux-4.12(长期版).zip -d /usr/src/-O gbk是 Info-ZIP 的字符集覆盖参数告诉解压器压缩包内部文件名用的是 GBK 而不是 UTF-8。不是所有发行版打的 unzip 包都编译了-O支持遇到invalid option错误时可以用7z x代替7-Zip 对中文名的处理更宽容。2.2 解压后的源码树先认目录再动手解压后进入目录推荐用一条命令把自己定位到根目录同时看版本号cd /usr/src/ ls head -n 5 /usr/src/linux-4.12/MakefileMakefile 前几行定义的VERSION、PATCHLEVEL、SUBLEVEL、EXTRAVERSION拼起来就是完整内核版本号比如 4.12.0 或打了补丁的 4.12.25。这一手在嵌入式内核源码的交付包里格外有用厂商的 BSP 包经常预编译好内核通过比对源码 Makefile 和预编译产物里uname -r的输出一眼就能看出源码和产物对不对得上。源码顶层目录的职责划分如下目录负责内容常见配置项arch/体系结构相关x86 和 arm64 主要代码都在这里CONFIG_X86、CONFIG_ARM64kernel/进程调度、信号、时间子系统、模块加载CONFIG_SCHED_*、CONFIG_MODULESmm/页面分配、slab、vmalloc 等内存管理CONFIG_MMU、CONFIG_SLABfs/各种文件系统的实现CONFIG_EXT4_FS、CONFIG_VFSdrivers/设备驱动占比最大CONFIG_*_DRIVER 等net/网络协议栈和 netfilterCONFIG_INET、CONFIG_NETFILTERinit/内核启动主流程和初始化CONFIG_INITRAMFS_SOURCEscripts/kbuild、配置、模块签名等构建工具不产生配置项这张表不需要背下来但要知道改什么功能去哪个目录翻源码调文件系统去 fs/调网络去 net/调驱动去 drivers/调进程调度去 kernel/sched/。拿到源码之后我还会顺手看两样东西顶层 README 里的工具链版本说明以及 scripts/ 下有没有打包方追加的补丁脚本。4.12 是 2017 年的基线用 GCC 10 以上的版本编译时经常会遇到版本漂移警告部分老代码在-Werror语境下还会直接失败。常见做法是给编译命令加HOSTCCgcc-10或者在顶层 Makefile 里做一次KBUILD_HOSTCC覆盖确保主机编译器与源码同代。3. 为 linux-4.12(长期版) 内核源码做第一次配置defconfig、menuconfig 与 olddefconfig 的取舍内核编译失败的原因里配置阶段犯错占比很高。4.12 时代使用的 Kconfig/kbuild 体系和今天新版本基本一致所以这套经验现在依然有效。配置的本质是回答一个问题哪些功能编译进内核本体哪些编成模块哪些根本不编。分别对应y、m、n。3.1 三种配置生成方式分别什么时候用make defconfig # 按当前架构生成一份可启动的默认配置 make menuconfig # 基于 ncurses 的菜单交互光标逐项修改 make olddefconfig # 读入现有 .config把新增符号全部置为默认值在新环境上第一次配置的顺序是先make defconfig拿到一个能启动的底子再make menuconfig按项目需求做加减法。老手维护既有配置时推荐直接用make olddefconfig它不会改掉已有选项只会静默处理旧配置里没有的新增符号比打开菜单一个个翻快得多。选完配置后.config就是后续所有编译动作的唯一依据。make menuconfig依赖 ncurses 开发库缺少会直接报Unable to find the ncurses libraries。Debian/Ubuntu 上补依赖的命令是sudo apt-get install libncurses-dev flex bison libssl-devflex 和 bison 是生成配置解析器的工具libssl-dev 是 4.x 内核开启模块签名等选项时编译必需的这三个缺失是新手装内核最常见的三连坑。menuconfig 里按/可以按名称搜索符号比如输入IKCONFIG_PROC直接定位选项位置再按提示的路径逐级进入。方向键移动空格循环切换 y/m/nEnter 进入子菜单Esc 返回上级。这些快捷键从 4.12 到新版本内核都一致学会了就不需要鼠标。3.2 编译前必须确认的 5 个配置项不管什么项目下面几项我每次都检查配置项推荐值作用与影响CONFIG_LOCALVERSION如 -custom追加到版本号尾部便于 uname -r 区分内核CONFIG_PREEMPT桌面 y / 服务器 n抢占调度影响交互延迟与吞吐CONFIG_HZ1000 交互 / 250 服务内核时钟节拍频率影响调度粒度与开销CONFIG_IKCONFIG_PROCy把配置写到 /proc/config.gz排错时提取真实配置CONFIG_DEBUG_INFO调试 y / 交付 n生成带调试符号的 vmlinuz体积和编译时间明显变大CONFIG_LOCALVERSION设置后uname -r会直接输出4.12.0-custom多内核共存的机器靠这个区分谁是谁。CONFIG_HZ的坑在于它同时影响jiffies的时间粒度和系统调用的计时精度桌面体验敏感选 1000纯计算或负载均衡类选 250 够用。CONFIG_IKCONFIG_PROC强烈建议选 y代价极小回报是排错时随时能拿到真实生效的配置快照不用去翻编译机上的原始 .config。3.3 不编译也验证语法scripts/config 与孤儿符号检查配置改完先用脚本工具验证一次不必急着进入编译scripts/config --enable CONFIG_BLK_DEV_INITRD scripts/config --disable CONFIG_DEBUG_INFO scripts/config --module CONFIG_ATAscripts/config支持--enable、--disable、--module三种操作适合在脚本里批量调整不用打开交互菜单。跑完后再执行make listnewconfig看有没有新增且未配置的符号输出为空说明旧配置已经完整覆盖。这套流程适合把同一份 4.12 配置从 x86 迁移到 arm64 的场景先 defconfig 生成基线再 scripts/config 做差异化修改最后 olddefconfig 收尾三个命令一气呵成。配置阶段完成.config已经是一份完整的内核定义下一章进入真正消耗 CPU 的部分。4. 编译安装 linux-4.12(长期版) 内核并让 GRUB 认出来配置阶段通过后编译安装流程对 4.12 长期版来说非常标准。最容易出问题的地方不在 make 本身而在三处并行编译把内存吃爆、模块安装路径和内核本体安装分离、安装完 GRUB 没更新导致重启后进不了新内核。4.1 并行编译参数与依赖检查编译 4.12 涉及两个独立视角本地编译和交叉编译。嵌入式场景极少在开发板上直接 make常见的做法是用交叉工具链make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- -j4ARCHarm64指定目标架构CROSS_COMPILE是交叉工具链前缀配置阶段同样用 menuconfig但要在架构选择里指定 arm64。BSP 厂商给的内核源码包通常自带工具链压缩包和 build.sh优先用配套脚本而不是自己拼命令这是我做嵌入式内核源码适配时的默认选择。本地编译则简单直接make -j$(nproc) # 8核机器等于 make -j8-j的数值不是越大越好。每个编译任务通常要 150MB 到 300MB 内存8 核并行时 8GB 内存的机器会明显吃紧一旦触发 OOM 编译器进程直接被杀死日志末尾只能看到Killed。内存不足时把并发减半make -j44.12 完整编译在主流桌面 CPU 上大约 15 到 40 分钟取决于配置项数量。日志里出现Error 1、Error 2时需要往回翻真正的报错原因通常在三五行之前常见的是缺少头文件、缺少密钥文件或配置引用了不存在的选项。前面说的 libssl-dev、flex、bison 装好后这些大半能避开。4.2 安装模块与内核本体顺序不能反sudo make modules_install # 内核模块安装到 /lib/modules/4.12.x/ sudo make install # 复制 vmlinuz、initramfs、System.map 到 /bootmodules_install把所有编译出来的.ko模块安装到/lib/modules/$(uname -r)/下make install负责把arch/x86/boot/bzImage复制成/boot/vmlinuz-4.12.x同时调用发行版的 installkernel 钩子生成对应的System.map和 initramfs。顺序反过来不会报错但 initramfs 里可能缺少对应模块列表启动时会出现找不到模块的状况。如果只是装来调试、不需要调试符号可以给安装步骤加一个变量让模块剥离符号表体积和耗时都明显下降sudo make INSTALL_MOD_STRIP1 modules_installINSTALL_MOD_STRIP1等价于对每个.ko执行strip --strip-debug能砍掉约 30% 到 50% 的模块体积。生产交付、需要在线调试驱动的环境不建议开符号没了 kprobe 和 crash 工具都难用。4.3 驱动模块单独重编不必反复编译整个内核开发驱动时最忌讳每改一次就去全量编译。4.12 的 kbuild 支持指定目录或指定文件单独编译# 编译源码树外的模块目录 make -C /usr/src/linux-4.12 M/home/dev/transp # 编译源码树内指定驱动目录 make -C /usr/src/linux-4.12 Mdrivers/misc/transp-C指定源码根目录M可以指向源码树之外的外部模块目录也可以指向树内子目录。对树内驱动更快的做法是直接编译单个目标make drivers/misc/transp/transp.ko编译出的.ko先用modprobe测试加载它会按依赖关系自动拉起其他模块insmod不会做这件事。卸载用rmmod transp不需要带 .ko 后缀。一个典型的场景是安全类驱动做透明加密时在file_operations里挂read/write回调拦截数据读写这种动态加载方式必须把模块编成可卸载结构否则rmmod时会因为回调还挂在已打开的文件上而拒绝卸载。4.4 让 GRUB 认识新内核make install在某些发行版上并不会自动刷新 GRUB 菜单这是“编译成功但重启没看到新内核”的第一大原因。手动更新引导配置sudo update-grub # Debian/Ubuntu 系 sudo grub2-mkconfig -o /boot/grub2/grub.cfg # RHEL/CentOS 系执行后打开/boot/grub2/grub.cfg或/boot/grub/grub.cfg搜索4.12能看到对应 menuentry 才算注册成功。顺手检查一下/boot分区剩余空间文件来源用途/boot/vmlinuz-4.12.xmake install内核本体/boot/initrd.img-4.12.xinstallkernel 钩子启动早期驱动与根文件系统挂载/boot/System.map-4.12.xmake install符号表调试 oops 必用长期版反复安装多个内核后/boot经常被打满vmlinuz 加 initramfs 通常占用不到 100MB但旧的 3.x、4.x 内核堆在一起就很可观该清就清。5. 用 dmesg 和 /proc 快速验证 linux-4.12(长期版) 内核的运行状态重启进入新内核后第一件事不是跑业务而是用三条命令确认自己没装错、配置没失效。长期版尤其要核对 SUBLEVEL 是否与交付说明一致。5.1 版本和启动参数确认uname -r cat /proc/cmdline zcat /proc/config.gz 2/dev/null | grep CONFIG_PREEMPTuname -r核对版本号与编译时设置的CONFIG_LOCALVERSION拼接一致才算对/proc/cmdline能看到实际传给内核的启动参数/proc/config.gz只有在配置期开启CONFIG_IKCONFIG_PROC后才存在它和编译机上的 .config 可能不完全一样因为打包脚本会在构建前二次改配置以这里为准。5.2 dmesg 分级过滤快速定位问题sudo dmesg -l err sudo dmesg -l warn sudo dmesg | grep -Ei oom|segfault|BUG:|Oopsdmesg -l err只显示错误级别日志-l warn显示警告级别。刚启动的新内核经常有一堆firmware failed之类的警告多数不影响使用。最后一条 grep 把内核崩溃和内存相关关键字一把捞出来是驱动自检时最快的过滤手段。想看日志对应的时间点用dmesg -T输出人类可读时间戳和journalctl --since对得上。加载自写的.ko后出现Unknown symbol时先看/proc/kallsyms里对应符号是否存在再看模块依赖是否编译齐全。5.3 透明加密类模块的加载自检做拦截文件读写的驱动时加载后这样确认钩子真的挂上了cat /proc/modules | grep transp lsmod | grep transp/proc/modules第一列是模块名第三列是引用计数。引用计数不为 0 说明有文件正被打开拦截此时rmmod会返回Device or resource busy这是行为正确而不是故障。需要卸载时先处理掉持有 fd 的进程再执行rmmod。把模块参数调整脚本放进/etc/sysctl.d/或 systemd 单元里开机就能自动加载并生效。本文还有配套的精品资源点击获取