主要内容:
- 获取内核源码
- 内核源码结构
- 编译内核方法
- 内核开发特点
1、获取内核源码
linux是一个开源的内核,linux源码随手可得,想要获取内核源码非常的方便,可以通过网站下载或者git方式下载。
2、内核源码结构
目录 | 说明 |
arch | 特定体系结构的代码 |
block | 块设备I/O层 |
crypo | 加密API |
Documentation | 内核源码文档 |
drivers | 设备驱动程序 |
firmware | 使用某些驱动程序而需要的设备固件 |
fs | VFS和各种文件系统 |
include | 内核头文件 |
init | 内核引导和初始化 |
ipc | 进程间通信代码 |
kernel | 像调度程序这样的核心子系统 |
lib | 同样内核函数 |
mm | 内存管理子系统和VM |
net | 网络子系统 |
samples | 示例,示范代码 |
scripts | 编译内核所用的脚本 |
security | Linux 安全模块 |
sound | 语音子系统 |
usr | 早期用户空间代码(所谓的initramfs) |
tools | 在Linux开发中有用的工具 |
virt | 虚拟化基础结构 |
3、编译内核方法
- make config 基于文本模式的交互式内核配置
- make menuconfig 基于文本模式的菜单型内核配置
- make gconfig 基于gtk窗口模式的内核配置
- make defconfig 基于默认的体系结构配置
- make oldconfig 基于之前编译的内核
- make 编译内核
- make > /dev/null 编译时输出的信息重定向到永无返回值的黑洞
- make -jn n表示衍生的作业数,及参与编译的核心数
4、内核开发特点
4.1 无标准C库
为了保证内核的小和高效,内核开发中不能使用C标准库,所以连最常用的printf函数也没有,但是还好有个printk函数来代替。
4.2 使用GNU C,推荐用gcc 4.4或以后的版本来编译内核
因为使用GNU C,所有内核中常使用GNU C中的一些扩展:
4.2.1 内联函数
内联函数在编译时会在它被调用的地方展开,减少了函数调用的开销,性能较好。但是,频繁的使用内联函数也会使代码变长,从而在运行时占用更多的内存。
所以内联函数使用时最好要满足以下几点:函数较小,会被反复调用,对程序的时间要求比较严格。
内联函数示例:static inline void sample();
4.2.2 内联汇编
内联汇编用于偏近底层或对执行时间严格要求的地方。示例如下:
unsigned int low, high;
asm volatile("rdtsc" : "=a" (low), "=d" (high));
/* low 和 high 分别包含64位时间戳的低32位和高32位 */
4.2.3 分支声明
如果能事先判断一个if语句时经常为真还是经常为假,那么可以用unlikely和likely来优化这段判断的代码。
/* 如果error在绝大多数情况下为0(假) */
if (unlikely(error)) {
/* ... */
}
/* 如果success在绝大多数情况下不为0(真) */
if (likely(success)) {
/* ... */
}
4.3 没有内存保护
因为内核是最低层的程序,所以如果内核访问的非法内存,那么整个系统都会挂掉!!所以内核开发的风险比用户程序开发的风险要大。
而且,内核中的内存是不分页的,每用一个字节的内存,物理内存就少一个字节。所以内核中使用内存一定要谨慎。
4.4 不使用浮点数
内核不能完美的支持浮点操作,使用浮点数时,需要人工保存和恢复浮点寄存器及其他一些繁琐的操作。
4.5 内核栈容积小且固定
内核栈的大小有编译内核时决定的,对于不用的体系结构,内核栈的大小虽然不一样,但都是固定的。
查看内核栈大小的方法:
ulimit -a | grep "stack size"
4.6 同步和并发
Linux是多用户的操作系统,所以必须处理好同步和并发操作,防止因竞争而出现死锁。
4.7 可移植性
Linux内核可用于不用的体现结构,支持多种硬件。所以开发时要时刻注意可移植性,尽量使用体系结构无关的代码。