内核将应用程序和硬件分离开来。
内核一方面负责与计算机硬件进行交互,实现对硬件的编程控制和接口操作,调度对硬件资源的访问,另一方面为用户应用程序提供一个高级的执行环境
和访问硬件的虚拟接口。
部分内核为体系结构和硬件所特有,即体系相关部分;部分内核是可移植的,即体系无关部分。
体系无关部分通常会定义与体系相关部分的接口,这样,内核向新的体系结构移植的过程就变成确认这些接口的特性并将它们加以实现的过程。
同时,用户应用程序和内核之间的联系,一般是通过它和内核的中间层——标准C库来实现,而标准C库函数本身,则是建立在内核提供的系统调用基础之上。
通过标准C库,以及内核体系无关部分与体系相关部分的接口,用户应用程序和部分内核都成为可移植的。
(1)系统调用接口
(2)进程管理
(3)内存管理
(4)虚拟文件系统
(5)网络功能
(6)设备驱动程序
(7)依赖体系结构的代码
内核源码目录结构
(1)Document
这个目录下面没有内核代码,只有很多质量参次不齐的文档,但往往能够给我们提供很多的帮助。
(2)arch
所有与体系结构相关的代码都在这个目录以及include/asm-*/目录中,Linux支持的每种体系结构在arch目录下都有对应的子目录,而在每个体系结构特有
的子目录下又至少包含3个子目录。
kernel:存放支持体系结构特有的诸如信号量处理和SMP之类的实现。
lib:存放体系结构特有的对诸如strlen和memcpy之类的通用函数的实现。
mm:存放体系结构特有的内存管理程序的实现。
除了这3个子目录之外,大多数体系结构在必要的情况下还有一个boot子目录,包含了在这种硬件平台上启动内核所使用的部分或全部平台特有代码。
(3)drivers
这个目录是内核中最庞大的一个目录,显卡、网卡、SCSI适配器、PCI总线、USB总线和其他任何Linux支持的外围设备或总线的驱动程序都可以在这里找到。
(4)fs
虚拟文件系统(VFS,Virtual File System)的代码,和各个不同文件系统的代码都在这个目录中。Linux支持的所有文件系统在fs目录下面都有一个对应的子目录。
(5)include
这个目录包含了内核中大部分的头文件,它们按照下面的子目录进行分组。
include/asm-*/,这样的子目录有多个,每一个都对应这一个arch的子目录,比如include/asm-alpha、include/asm-arm、include/asm-i386等。每个子目录中
的文件都定义了支持给体系结构所必须的预处理器宏和内联函数,这些内联函数多数都是全部或部分使用汇编语言实现的。
include/linux,与平台无关的头文件都在这个目录下面,它通常会被链接到目录/usr/include/linux。
(6)init
内核的初始化代码。包括main.c、创建早期用户空间的代码以及其他初始化代码。
(7)ipc
IPC,即进程间通信。它包含了共享内存、信号量以及其他形式IPC的代码。
(8)kernel
内核中最核心的部分,包括进程的调度,以及进程的创建(kernel/sched.c)和撤销(kernel/fork.c和kernel/exit.c)等,和平台相关的另外一部分核心的代码在
arch/*/kernel目录。
(9)lib
库代码,实现了一个标准C库的通用子集,包括字符串和内存操作的函数(strlen、mmcpy和其他类似的函数)以及有关sprintf和atoi的系列函数。与arch/lib下
的代码不同,这里的库代码都是使用C编写的,在内核新的移植版本中可以直接使用。
(10)mm
包含了体系结构无关部分的内存管理代码,体系相关的部分位于arch/*/mm目录下。
(11)net
网络相关代码,实现了各种常见的网络协议,如TCP/IP,IPX等。
(12)scripts
该目录下没有内核代码,只包含了用来配置内核的脚本文件。当运行make menuconfig或者make xconfig之类的命令配置内核时,用户就是和位于这个目录下
的脚本进行交互的。
(13)block
block层的实现。最初block层的代码一部分位于drivers目录,一部分位于fs目录,从2.6.15开始,block层的核心代码被提取出来放在了顶层的block目录。
(14)crypto
内核本身所用的加密API,实现了常用的加密和散列算法,还有一些压缩和CRC校验算法。
(15)security
这个目录包括了不同的Linux安全模型的代码。
(16)sound
声卡驱动以及其他声音相关的代码。
(17)usr
实现了用于打包和压缩的cpio等。
内核代码的特点
Linux内核必须使用GNU的GCC编译器来编译,而GCC提供了很多的C语言扩展,这些扩展对优化、目标代码布局、更安全的检查等提供了很强的支持。因此,
内核代码所使用的C语法并不完全符合ANSI C标准,实际上,只要有可能,内核开发者总是要用到GCC提供了C语言扩展部分。
下面详细介绍Linux内核中常出现的、主要的GCC扩展:
1.语句表达式(statement-embedded expression)
GCC把包含在括号中的复合语句看作是一个表达式,称为语句表达式,它允许在一个表达式内使用循环、跳转、局部变量,并可以出现在任何允许表达式出现的
地方。
位于括号中的复合语句的最后一句必需是一个以分号结尾的表达式,它的值将成为这个语句表达式的值。
2.零长度数组(flexible array)
3.可变参数表
4.标号元素
5.特殊属性(_attribute_)
GCC允许声明函数、变量和类型的特殊属性,以便指示编译器进行特定方面的优化和更仔细的代码检查。使用方式为在声明后加上:
_attribute_((ATTRIBUTE))
其中ATTRIBUTE是属性的说明,多个说明之间以逗号分隔。GCC可以支持十几个属性,下面介绍一些比较常用的。
noreturn
属性noreturn用于函数,表示该函数从不返回。它能够让编译器生成较为优化的代码,消除不必要的警告信息。
format(archetype,string-index,first-to-check)
属性format用于函数,表示该函数使用printf,scanf,strftime或strfmon风格的参数,并可以让编译器检查函数声明和函数实际调用参数之间的格式化字符串是否
匹配。
archetype指定是哪种风格,string-index指定传入函数的第几个参数是格式化字符串,first-to-check指定从函数的第几个参数开始按照上述规则进行检查。
unused
属性unused用于函数和变量,表示该函数或变量可能并不使用,这个属性能够避免编译器产生警告信息。
section
属性section用于函数和变量。
aligned(ALIGNMENT)
属性aligned用于变量、结构或联合,设定一个指定大小的对其方式,以字节为单位。
packed
属性packed用于变量和类型,用于变量或结构体成员时表示使用最小可能的对齐,用于枚举、结构体或联合类型时表示该类型使用最小的内存。
属性packed多用于定义硬件相关的结构时,使元素之间不会因对齐产生问题。
内核中的链表
内核中链表的实现位于include/linux/list.h文件,链表数据结构的定义也很简单:
struct list_head{
struct list_head *next,*prev;
}
list_head结构包含两个指向list_head结构的指针prev和next,由此可见,内核中的链表实际上都是双链表(通常都是双循环链表)。
(1)声明与初始化
链表的声明可以使用两种方式,一种为使用LIST_HEAD宏在编译时静态初始化,一种为使用INIT_LIST_HEAD()在运行时进行初始化。
(2)判断链表是否为空
(3)插入
有了链表,自然就要对其进行操作,就要向里面加东西。list_add()和list_add_tail()这两个函数可以完成这个工作。
(4)删除
(5)遍历
Kconfig和Makefile
毫不夸张地说,Kconfig和Makefile是我们浏览内核代码时最为依仗的两个文件。基本上,Linux内核中每一个目录下边都会有一个Kconfig文件
和一个Makefile文件。Kconfig和Makefile就好似一个城市的地图,地图引导我们去认识一个城市,而Kconfig和Makefile则可以让我们了解一个
内核目录下面的结构。