关键字:解读Linux操作系统内核源码的方法 Linux系统中使用命令行修改时区 Linux系统下调整屏幕刷新率的方法 Linux操作系统摄像头驱动的安装 在Linux系统下如何利用SSH进行远程控制 通过网络安装Linux CentOS 系统的方法 Linux和Windows系统线程间的区别 Linux操作系统下修改IP、DNS等网络配置
针对好多Linux 爱好者对内核很有兴趣却无从下口
本文旨在介绍一种解读linux内核源码
入门方法
而不
解说linux复杂
内核机制;
一.核心源程序
文件组织:
1.Linux核心源程序通常都安装在/usr/src/linux下
而且它有一个非常简单
编号约定:任何偶数
核心(例如2.0.30)都
一个稳定地发行
核心
而任何奇数
核心(例如2.1.42)都
一个开发中
核心
本文基于稳定
2.2.5源代码
第二部分
实现平台为 Redhat Linux 6.0
2.核心源程序
文件按树形结构进行组织
在源程序树
最上层你会看到这样一些目录:
●Arch :arch子目录包括
所有和体系结构相关
核心代码
它
每一个子目录都代表一种支持
体系结构
例如i386就
关于intel
cpu及与之相兼容体系结构
子目录
PC机一般都基于此目录;
●Include: include子目录包括编译核心所需要
大部分头文件
与平台无关
头文件在 include/linux子目录下
与 intel
cpu相关
头文件在include/asm-i386子目录下,而include/scsi目录则
有关scsi设备
头文件目录;
●Init: 这个目录包含核心
初始化代码(注:不
系统
引导代码)
包含两个文件main.c和Version.c
这
研究核心如何工作
一个非常好
起点
●Mm :这个目录包括所有独立于 cpu
体系结构
内存管理代码
如页式存储管理内存
分配和释放等;而和体系结构相关
内存管理代码则位于arch/*/mm/
例如arch/i386/mm/Fault.c
●Kernel:主要
核心代码
此目录下
文件实现
大多数linux系统
内核函数
其中最重要
文件当属sched.c;同样
和体系结构相关
代码在arch/*/kernel中;
●Drivers: 放置系统所有
设备驱动程序;每种驱动程序又各占用一个子目录:如
/block
下为块设备驱动程序
比如ide(ide.c)
如果你希望查看所有可能包含文件系统
设备
如何初始化
你可以看drivers/block/genhd.c中
device_setup()
它不仅初始化硬盘
也初始化网络
因为安装nfs文件系统
时候需要网络其他:
如, Lib放置核心
库代码; Net,核心与网络相关
代码; Ipc,这个目录包含核心
进程间通讯
代码; Fs
,所有
文件系统代码和各种类型
文件操作代码
它
每一个子目录支持一个文件系统
例如fat和ext2;
Scripts, 此目录包含用于配置核心
脚本文件等
一般
在每个目录下
都有一个 .depend 文件和一个 Makefile
文件
这两个文件都
编译时使用
辅助文件
仔细阅读这两个文件对弄清各个文件这间
联系和依托关系很有帮助;而且
在有
目录下还有Readme
文件
它
对该目录下
文件
一些说明
同样有利于我们对内核源码
理解;
二.解读实战:为你
内核增加一个系统调用
虽然
Linux
内核源码用树形结构组织得非常合理、科学
把功能相关联
文件都放在同一个子目录下
这样使得程序更具可读性
然而
Linux
内核源码实在
太大而且非常复杂
即便采用
很合理
文件组织方法
在不同目录下
文件之间还
有很多
关联
分析核心
一部分代码通常会要查看其它
几个相关
文件
而且可能这些文件还不在同一个子目录下
体系
庞大复杂和文件之间关联
错综复杂
可能就
很多人对其望而生畏
主要原因
当然
这种令人生畏
劳动所带来
回报也
非常令人着迷
:你不仅可以从中学到很多
计算机
底层
知识(如下面将讲到
系统
引导)
体会到整个操作系统体系结构
精妙和在解决某个具体细节问题时
算法
巧妙;而且更重要
:在源码
分析过程中
你就会被一点一点地、潜移默化地专业化;甚至
只要分析十分之一
代码后
你就会深刻地体会到
什么样
代码才
一个专业
程序员写
什么样
代码
一个业余爱好者写
为
使读者能更好
体会到这一特点
下面举
一个具体
内核分析实例
希望能通过这个实例
使读者对Linux
内核
组织有些具体
认识
从中读者也可以学到一些对内核
分析方法
以下即为分析实例:
A、操作平台:
硬件:cpu intel Pentium II ;
软件:Redhat Linux 6.0; 内核版本2.2.5
B、相关内核源代码分析:
1.系统
引导和初始化:Linux 系统
引导有好几种方式:常见
有 Lilo,
Loadin引导和Linux
自举引导(bootsect-loader),而后者所对应源程序为arch/i386/boot/bootsect.S
它为实模式
汇编程序
限于篇幅在此不做分析;无论
哪种引导方式
最后都要跳转到
arch/i386/Kernel/setup.S
setup.S主要
进行时模式下
初始化
为系统进入保护模式做准备;此后
系统执行
arch/i386/kernel/head.S (对经压缩后存放
内核要先执行 arch/i386/boot/compressed/head.S);
head.S 中定义
一段汇编程序setup_idt
它负责建立一张256项
idt 表(Interrupt Descriptor
Table),此表保存着所有自陷和中断
入口地址;其中包括系统调用总控程序 system_call
入口地址;当然
除此之外
head.S还要做一些其他
初始化工作;
2.系统初始化后运行
第一个内核程序asmlinkage void __init start_kernel(void) 定义在
/usr/src/linux/init/main.c中,它通过调用usr/src/linux/arch/i386/kernel/traps.c 中
一个函数
void __init trap_init(void) 把各自陷和中断服务程序
入口地址设置到 idt
表中,其中系统调用总控程序system_cal就
中断服务程序之一;void __init trap_init(void) 函数则通过调用一个宏
set_system_gate(SYSCALL_VECTOR,&system_call); 把系统调用总控程序
入口挂在中断0x80上;
其中SYSCALL_VECTOR
定义在 /usr/src/linux/arch/i386/kernel/irq.h中
一个常量0x80; 而
system_call
即为中断总控程序
入口地址;中断总控程序用汇编语言定义在/usr/src/linux/arch/i386/kernel/entry.S中;
3.中断总控程序主要负责保存处理机执行系统调用前
状态,检验当前调用
否合法, 并根据系统调用向量
使处理机跳转到保存在 sys_call_table
表中
相应系统服务例程
入口; 从系统服务例程返回后恢复处理机状态退回用户程序;
而系统调用向量则定义在/usr/src/linux/include/asm-386/unistd.h 中;sys_call_table
表定义在/usr/src/linux/arch/i386/kernel/entry.S 中; 同时在
/usr/src/linux/include/asm-386/unistd.h 中也定义
系统调用
用户编程接口;
4.由此可见 , linux
系统调用也象 dos 系统
int 21h 中断服务, 它把0x80 中断作为总
入口, 然后转到保存在
sys_call_table 表中
各种中断服务例程
入口地址 , 形成各种不同
中断服务;
由以上源代码分析可知, 要增加一个系统调用就必须在 sys_call_table 表中增加一项 ,
并在其中保存好自己
系统服务例程
入口地址,然后重新编译内核
当然
系统服务例程
必不可少
由此可知在此版linux内核源程序中,与系统调用相关
源程序文件就包括以下这些:
arch/i386/boot/bootsect.S
arch/i386/Kernel/setup.S
arch/i386/boot/compressed/head.S
arch/i386/kernel/head.S
init/main.c
arch/i386/kernel/traps.c
arch/i386/kernel/entry.S
arch/i386/kernel/irq.h
include/asm-386/unistd.h
当然
这只
涉及到
几个主要文件
而事实上
增加系统调用真正要修改文件只有include/asm-386/unistd.h和arch/i386/kernel/entry.S两个
一流导航网16dh.com

