Linux(06)之获取内核代码
Author:OnceDay Date:2023年1月5日
漫漫长路,有人对你微笑过嘛…
参考文档:
- 《Linux内核设计和实现》
1.概述
linux内核的基本架构如下:
所以每个处理器运行的地方只有以下可能:
- 运行于用户空间,执行用户进程。
- 运行于内核空间,处于进程上下文,代表某个特定的进程执行。
- 运行于内核空间,处于中断上下文,于任何进程无关,处理某个特定的中断。
内核设计一般有两种,单内核和微内核。Linux基于单内核设计,但同时吸收了微内核设计的模块化思想。Linux内核运行在单独的内核地址空间上 ,同时支持抢占式内核、支持内核线程、动态装载内核模块的能力。
Linux内核支持对称多处理机制(SMP),对待线程和进程一视同仁,线程就像一些能够共享资源的进程。
Linux内核有多种版本,有一些版本是LTS(长期支持),这类版本一般用于工业生产中,另外一些就是处于开发之中的版本。命名规则如下:
主版本号+从版本号用于描述内核系列。如上,就是5.15版内核系列。
2. 获取linux内核源码
获取linux源码非常简单,直接在官方网站获取即可,但是下载速度有限:
- The Linux Kernel Archives
可以考虑从国内镜像网站下载:
- linux-kernel安装包下载_开源镜像站-阿里云 (aliyun.com)
此外,也可以使用git
下载,源代码在github
上:
- https://github.com/torvalds/linux
但是同样下载很慢,因此使用gitee
码云来加速下载:
- Linux Kernel: Linux 内核源码镜像 (gitee.com)
可以使用以下方式直接下载:
git clone https://gitee.com/mirrors/linux_old1.git
注意,最好在linux环境下安放源码,window下可能遇到文件系统不兼容的问题。
内核源码一般由以下目录组成:
目录 | 描述 |
---|---|
arch | 特定体系结构的源码 |
block | 块设备I/O层 |
crypto | 加密API |
Documentation | 内核源码文档 |
drivers | 设备驱动程序 |
firmware | 使用某些驱动程序而需要的设备固件 |
fs | VFS和各种文件系统 |
include | 内核头文件 |
init | 内核引导和初始化 |
ipc | 进程间通信代码 |
kernel | 像调度程序这样的核心子系统 |
lib | 通用内核函数 |
mm | 内存管理子系统 |
samples | 示例,示范代码 |
scripts | 编译内核所用的脚本 |
sound | 语音子系统 |
usr | 早期用户空间代码(所谓的initramfs) |
tools | 在Linux开发中有用的工具 |
virt | 虚拟化基础结构 |
除了上面文件夹外,还有一些许可证和其他的配套文件。
2.1 阅读linux源码
这里采用VScode + gtags的方法。
首先是直接通过下面命令在linux服务器上克隆整个linux kernel源码:
git clone https://gitee.com/mirrors/linux_old1.git
内核源码仓库很大,大小有3.5G,实际占用空间4~5G。
然后vscode使用ssh远程连接到Linux服务器上,vscode会自动安装vscode-server。
离线安装可参考下面文章,linux服务器能联网,安装很简单,请自行百度。
- 离线安装vscode_Once_day的博客-CSDN博客_vscode 离线安装
vscode需要安装以下插件:
C/C++
,必备插件。c/c++ Extension Pack
,扩展包。c/C++ GNU Global
,支持gtags。GitLens
,用于查看git commit记录和相关操作,非常好用。
此外Linux服务器需要安装global,这是gnu提供的一个解析代码的工具。生成二进制的数据库,对于大型代码工程非常有帮助。
对于ubuntu系统,直接apt install global
即可。其他linux系统仿照如此即可。
使用vscode连接ssh打开服务器上的内核代码文件夹。
设置配置文件(没有就新建)如下.vscode/settings.json
:
{
"editor.tabSize": 8,
"C_Cpp.default.intelliSenseMode": "linux-gcc-x64",
"C_Cpp.intelliSenseCacheSize": 51200,
"search.followSymlinks": false,
"C_Cpp.workspaceParsingPriority": "high",
"C_Cpp.default.browse.limitSymbolsToIncludedHeaders": true,
"C_Cpp.intelliSenseEngine": "disabled"
}
"search.followSymlinks": false
很关键,内核代码有些文件是符号链接文件,可能造成循环引用,这会导致cpptool直接cpu占用拉满。当然,最关键的是
这个地方,必须要手动暂停解析,这个解析再进行到一定文件之后就会卡死循环,所以干脆不要了。
其他配置字段的含义,可以自行百度,网上介绍非常多。
接下来是使用global生成代码解析文件,直接在vscode里面按下F1,然后搜索global
:
第一个命令是建立数据库,执行一次即可,如果代码改变,也可以再执行一次。
这个过程会在后台一直运行,大概几分钟,等着就好。如果运行正常,目录下面会出现三个文件:
-rw-r--r-- 1 ubuntu ubuntu 17M Jan 5 23:27 GPATH
-rw-r--r-- 1 ubuntu ubuntu 534M Jan 5 23:27 GRTAGS
-rw-r--r-- 1 ubuntu ubuntu 708M Jan 5 23:27 GTAGS
可以看到,数据是非常大的,然后就可以正常在代码使用跳转等功能了。
更多有关的信息可以自行去收集,此外c也可以使用langd工具。
2.2 内核代码开发的特点
内核代码的开发和用户空间有一些独特之处,主要如下:
- 内核编程不能访问C库或者标准的C头文件。
- 内核编程必须使用GNU C。
- 内核编程时缺乏像用户空间这样的内存保护机制。
- 内核编程难以执行浮点运算。
- 内核给每个进程只有一个很小的定长堆栈。
- 内核支持SMP、抢占、异步中断,所以必须时刻注意同步和并发。
- 考虑可移植的重要性。
首先,内核不能链接使用标准C函数库。主要原因是这些库都太大了,效率也底下。不过大部分的C库函数都在内核中已经实现了。
在内核中没有内存保护机制,如果一个用户试图进行一次非法的内存访问,内核就会发现这个错误,发送SIGSEGV信号,并结束整个进程。如果内核这么操作,会导致oops发生(一种内核的错误警告,包含错误的信息)。
内核里面也不要进行浮点数操作,这是因为浮点数的实现方式各异,需要人工保存和恢复浮点寄存器之类的操作。
内核的栈大小是受限的,根据体系不同值也不同,一般由4KB,8KB,16KB等,64位机器一般比32位要多。
内核的抢占、SMP、异步中断等过程都可能产生竞争条件,因此需要并发的访问共享数据,如RCU机制。
Linux是一个可移植的系统,因此,字节序、64位对齐,字长和页面长度等不应该假定。
此外,内核代码还用了很多GNU C的扩展语法支持。