目录
- Linux 内核基础知识
- 进程调度
- 内存管理
- 虚拟文件系统和网络接口
- 进程间通信
- Linux 内核编译
- Makefile 和 Kconfig
- 内核Makefile
- 内核Kconfig
- 配置项标识的写法
- depend 关键字
- select 关键字
- 表达式逻辑关系
- Kconfig 其他语法
- 配置文件的编译
- Linux 内核引导方法
- Booloader 定义
- Linux 内核C编程特点
- Linux 内核编码风格
- 零长度数组和变量数组
- 标号元素
- 可变参数宏
- 特殊属性声明
- type of关键字
- 内建函数
- do...while(0)
- Arm 处理器工作模式
Linux 内核基础知识
Linux 内核主要由调度(SCHED
),内存管理(MM
),虚拟文件系统(VFS
),网络接口(Net
),进程间通信(IPC
)这5个子系统组成
Linux 内核各个部分的组成关系如下图所示:
进程调度
进程调度控制系统中多个进程对CPU
进行访问,使得CPU能够 微观串行,宏观并行的进行执行
linux
进程的状态切换如下图所示:
内存管理
内存管理的作用是控制多个进程安全的共享主内存区域,当CPU提供内存管理单元(MMU
)时,Linux 内存管理完成每个进程进行虚拟地址到物理内部的转换
一般而言,Linux 进程享有4GB 的内存空间,0-3G
属于内存空间,3-4G
属于内核空间,如下图所示:
虚拟文件系统和网络接口
Linux 的虚拟文件系统(VFS
)隐藏了硬件的具体细节,为所有的设备提供了统一的接口
网络接口提供了对各个网络标准的存取和对网络硬件的支持
进程间通信
Linux 支持多种进程间通信机制,包含 信号量、共享内存、管道等,这些机制可以协助多个进程,多资源的互斥访问、进程间的同步和消息传递
Linux 内核编译
内核编译方式分为两步 : 配置内核和编译内核
配置内核命令
make menuconfig
//或者如果有现成的配置文件 如 lddxxxx_defconfig
make lddxxxx_defconfig
编译内核和模块的方式是:
make zImage
make modules
编译完成后生成的文件:
- 未压缩的内核镜像文件
vmlinux
- 内核符号表文件
System.map
- arch/arm/boot 下得到压缩镜像文件
zImage
Makefile 和 Kconfig
内核Makefile
内核Makefile 文件的规则
obj-y = foo.o 表示将 foo 编译并连接进内核
obj-m = foo.o 表示将 foo 以模块化编译 生成 ko 文件
更加通用的做法是:
obj-$(CONFIG_ISDN) = isdn.o
多文件的处理方法:
obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-y := balloc.o dir.o file.o fsync.o ialloc.o inode.o
ioctl.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
添加目录的结构:
obj-$(CONFIG-EXT2_FS) += exte2/
内核Kconfig
内核Kconfig 项的基本格式
config xxxxx
bool “xxxxxxxxxxxxx”
default n
depend on <expr>
select xxxx
//举例如下
config DRM_DP_AUX_CHARDEVbool "DRM DP AUX Interface"depends on DRMhelpChoose this option to enable a /dev/drm_dp_auxN node that allows toread and write values to arbitrary DPCD registers on the DP auxchannel.
- config 表示一个配置项目
bool 表示的是数据类型,其他数据类型还包括 bool、tristate、string、hex、int, tristate 和 string** 是两种基本类型,其他类型都基于这两种类型 - help 信息用于提示,格式如下:
help ( 或者— help — )
开始
….
结束
配置项标识的写法
bool “DRM DP AUX Interface” 和
bool
prompt “DRM DP AUX Interface” 是一致的 prompt(英语含义:提示)
输入提示的一般格式为:
prompt [if ]
depend 关键字
default [if ] // 表示配置的默认值,一个配置选项可以存在任意多个默认的值,在这种情况下,生效的第一个的值
bool “foo” if BAR
default y if BAR
和下面的等价
depends on BAR
boo “foo”
default y
select 关键字
config A
Select B
表示如果这个配置项被选中,那么配置项B也会被选中
示例如下:
config DRM_RADEONtristate "ATI Radeon"depends on DRM && PCI && MMUselect FW_LOADERselect DRM_KMS_HELPERselect DRM_TTMselect POWER_SUPPLYselect HWMONselect BACKLIGHT_CLASS_DEVICEselect INTERVAL_TREEhelpChoose this option if you have an ATI Radeon graphics card. Thereare both PCI and AGP versions. You don't need to choose this torun the Radeon in plain VGA mode.If M is selected, the module will be called radeon.
表达式逻辑关系
depends on 关键字后面的表达式之间可以有逻辑关系,列举如下:
元素 | 说明 |
---|---|
等于 | <symbol> ‘=’ <symbol> |
不等于 | <symbol> ‘!=’ <symbol> |
赋值 | ‘(’ <expr> ‘)’ |
逻辑非 | ‘!’ <expr> |
逻辑与 | <expr> ‘&&’ <expr> |
Kconfig 其他语法
菜单结构
menu… endmenu
choices… endchoice
配置文件的编译
配置文件在经过Linux 系统编译后会生成一个头文件
autoconf.h
- linux 路径:
include/generated
- andoird 路径:
out/target/product/xxx/obj/KERNEL_OBJ/android-5.4/xxx/include/generated
内容如下:
.....c
#define CONFIG_IP6_NF_MATCH_AH_MODULE 1
#define CONFIG_NLS_CODEPAGE_861_MODULE 1
#define CONFIG_MTD_SPI_NAND_MODULE 1
#define CONFIG_RING_BUFFER 1
#define CONFIG_HARDENED_USERCOPY_FALLBACK 1
#define CONFIG_UWB_HWA_MODULE 1
#define CONFIG_SND_SOC_WM8804_MODULE 1
#define CONFIG_NF_CONNTRACK_H323_MODULE 1
#define CONFIG_HAVE_ARCH_SECCOMP_FILTER 1
#define CONFIG_IP6_NF_SECURITY_MODULE 1
#define CONFIG_SND_PROC_FS 1
#define CONFIG_VFIO_PCI_MMAP 1
.....
generated 中的文件还包括:
元素 | 说明 |
---|---|
compile.h | 编译主机信息 |
timeconst.h | 时间转换的固定宏 |
uapi/linux/version.h | 内核版本信息 |
Linux 内核引导方法
- 系统上电的时候,CPU会将PC指针赋值为一个特定的地址 0xFFFF0 并执行该地址的指令,在 PC 中该地址位于 BIOS 中,保存在主板的ROM 或者 Flash 中
- BIOS 按照CMOS设置定义的启动设备的顺序来搜索处于活动状态并且可以引导的设备,若是从硬盘启动,BIOS会将硬盘的 MBR(主引导记录)中的内容加载到RAM,MBR是一个512 字节的扇区,处于磁盘上的第一个扇区中(0道0柱面1扇区)。当MBR被加载到 RAM中之后,BIOS就会将控制权交给 MBR
- 主引导程序查找并且加载次引导加载程序,它在分区表中查找活动分区,将活动分区的引导记录从这个设备读入RAM并且启动它
- 次引导程序加载Linux 内核和可选的初始RAM磁盘,将控制权交给linux 内核代码
- 运行被加载的内核,并且启动用户空间的应用程序
Booloader 定义
-
可以在系统上电或者复位的时候以某种方法执行,
执行方法包括:被BIOS引导执行,直接 NorFlash 执行,NAND Flash 代码被 MCU自动拷贝进入内部或者外部 RAM直接执行 -
能将U盘,磁盘,光盘,NAND/Nor Flash ROM SD卡中存储介质,甚至串口网口中的操作系统加载到 RAM,并且将控制权交给内核源代码执行
内核镜像不是完全直接可以执行的代码,而是一个压缩过的
zImage 小内核
bzImage big 大内核
但是不是 zImage 和 bzImage 中的一切都被压缩了,实际当中存在没有被压缩的部分,这部分中包含解压缩程序,解压缩程序会解压映像中被压缩的部分,zImage 和 bzImage 都是 gzip 压缩的,在这两个文件头部内嵌有gzip 解压缩代码.
Linux 内核C编程特点
Linux 内核编码风格
C++/windows 中习惯使用驼峰式命名法比如:
sendData
minValue
但是在Linux 中习惯使用下划线命名方法:
send_data
min_value
Linux 社区对编码规范的要求:
- if/for/while/switch { 不另外起一行
- if for 如果只有一行 不加{ }
- 函数的 { } 都是要另外起一行
- swicth 和 case 要对齐的
switch(suffix) {
case 'G':
break;
case 'W':
break;
Linux 代码规范的详细文档位于linux 代码中
http://androidxref.com/kernel_3.18/xref/Documentation/CodingStyle
可以使用:
scripts/checkpatch.pl 进行代码规范的检查
零长度数组和变量数组
struct data {
int a;
char data[0];
}
char data[0];不占用实际空间,但是通过 data[i] 可以访问 len 之后的 第index 个地址,并没有为data[] 分配内存 sizeof(struct data) == sizeof(int)
int n = 5;
char demo[n];
标号元素
GNUC
支持 case x…y 这样的写法
swicth(ch) {
case '0' ... '9':
break;
case 'a'
break;
GNUC
中,通过制定索引或者结构体成员名,允许初始化的值以任意的顺序出现,指定数组索引的方法是添加 [index] = ,当然也可以用[first … last]的形式制定一个范围
unsigned char data[MAX] = { [0...MAX-1] = 0 };
可以借助结构体成员名初始化结构体,Linux 2.6 推荐使用标准C的形式初始化结构体,就是在每个结构体成员的名称前加上一个点
可变参数宏
标准的C支持可变参数的函数,比如printf,在GNUC 中宏也可以接受可变数目的参数
#define pr_debug(fmt,arg...) printk( fmt,##arg); )
##arg 表示的是零个或者多个参数的情况,这个参数以及参数之间的逗号构成了arg值
特殊属性声明
GNUC 允许声明函数,变量类型的特殊属性,如果要指定一个声明得到属性,在声明后面添加 attribute((ATTRIBUTE))
如果存在多个属性,句需要以逗号分隔,GNUC 支持的noreturn
format
section
aligned
packed
等十多个属性
type of关键字
type of关键字可以定义更加通用的宏 不需要预先知道变量的类型 类似于模板的作用
#define min(x,y) ({ /
const typeof(x) _x = (x); /
const typeof(y) _y = (y); /
(void) (&_x == &_y); /
_x < _y ? _x : _y; })(void) (&_x == &_y); 的作用是检查这两个变量的类型是否一致。
内建函数
使用 gcc
编译的时候,如果使用 -ansi-pedanic
编译选项,则告诉GNC编译器不使用GNU扩展语法
do…while(0)
使用的优点:可以在宏之中使用,使相关的语句可以正确的展开
#define safe_free§ do{ free§; p = null; }while(0) (注意不需要使用逗号)
使用的时候,可以和函数一样的使用 safe_free§;
Arm 处理器工作模式
元素 | 说明 |
---|---|
用户模式(Usr) | 用于正常执行程序 |
快速中断模式(FIQ) | 用于高速数据传输 |
管理模式(svc) | 操作系统使用的保护模式,由系统调用执行软中断SWI命令触发 |
外部中断模式(IRQ) | 用于通常的中断处理 |
数据访问终止模式(abt) | 当数据或指令预取终止时进入该模式,可用于虚拟存储以及存储保护 |
系统模式(sys) | 运行具有特权的操作系统任务 |
未定义指令中止模式(und) | 当未定义的指令执行时进入该模式,可用于支持硬件 |
Arm的工作模式切换有两种方法:
被动切换:在arm运行的时候产生一些异常或者中断来自动进行模式切换
主动切换:通过软件改变,即软件设置寄存器来经行arm的模式切换,应为arm的工作模式都是可以通过相应寄存器的赋值来切换的
除了用户模式 其余6种模式被称为特权模式
特权模式中除了系统模式之外 其余5种模式称为异常模式
Linux 只能通过系统调用或者硬件中断完成用户模式到特权模式的组合
特权模式可访问任意系统资源,异常模式通常由系统异常状态切换进来
大多数程序运行于用户模式;进入特权模式是为了处理中断、异常、或者访问被保护的系统资源;