现代计算机由 CPU、Memory(内存和外存)、输入输出设备、网络设备和其它的外围设备。为了管理这些设备,Linux内核提出了如下的架构:
LINUX架构_第1张图片

上图说明了Linux内核的整体架构。根据内核的核心功能,Linux内核提出了5个子系统,分别负责如下的功能:

  1. Process Scheduler,也称作进程管理、进程调度。负责管理CPU资源,以便让各个进程可以以尽量公平的方式访问CPU。

  2. Memory Manager,内存管理。负责管理Memory(内存)资源,以便让各个进程可以安全地共享机器的内存资源。另外,内存管理会提供虚拟内存的机制,该机制可以让进程使用多于系统可用Memory的内存,不用的内存会通过文件系统保存在外部存储器中,需要使用的时候,再取回到内存中。

  3. VFS(Virtual File System),虚拟文件系统。Linux内核将不同功能的外部设备,例如Disk设备(硬盘、磁盘、NAND Flash、Nor Flash等)、输入输出设备、显示设备等等,抽象为可以通过统一的文件操作接口(open、close、read、write等)来访问。这就是Linux系统“一切皆是文件”的体现(其实Linux做的并不彻底,因为CPU、内存、网络等还不是文件,如果真的需要一切皆是文件,还得看贝尔实验室正在开发的"Plan 9”的)。

  4. Network,网络子系统。负责管理系统的网络设备,并实现多种多样的网络标准。

  5. IPC(Inter-Process Communication),进程间通信。IPC不管理任何的硬件,它主要负责Linux系统中进程之间的通信。

内存管理(Memory Manager, MM)

ARM架构寄存器

Arm处理器有七种工作模式,为的是形成不同的使用级别,以防造成对系统的破坏。不同模式可以访问的寄存器不同,可以运行的指令不同。

(1)usr(10000):普通应用程序运行的模式(应用程序)
(2)FIQ(10001):快速中断模式,以处理快速情况,高速数据传输
(3)IRQ(10010):外部中断模式,普通中断处理
(4)svc(10011):保护模式(管理模式),操作系统使用的特权模式(内核)
(5)abt(10111):数据访问中止模式,用于虚拟存储和存储保护
(6)und(11011):未定义指令终止模式,用于支持通过软件仿真硬件的协处理器
(7)sys(11111):系统模式,用于运行特权级的操作系统任务(armv4以上版本才具有)

注意:usr是普通模式,其他六种是特权模式,而除了usr和sys模式以外的五种模式是异常模式

我们首先可以找到这样一本资料《ARM Architecture Reference Manual》在其中的Programmers’Model一章中,我们可以轻松的找到官方文档对寄存器的说明:
Arm处理器总共有37个寄存器其可以分为以下两类(在此我先列出大框架下面会一一介绍):

通用寄存器(31个)
--不分组寄存器(R0—R7)
--分组寄存器(R8—R14)
--PC指针(R15)

状态寄存器(6个)
--CPSR(1个)
--SPSR(5个)

ARM微处理器共有37个32位寄存器,其中31个为通用寄存器,6个为状态寄存器。
LINUX架构_第2张图片

LINUX架构
通用寄存器
1、不分组寄存器(R0-R7)
2、分组寄存器(R8-R14)
程序计数器R15(PC)

不分组通用寄存器
R0-R7是不分组寄存器。这意味着在所有处理器模式下,访问的都是同一个物理寄存器。不分组寄存器没有被系统用于特别的用途,任何可采用通用寄存器的应用场合都可以使用未分组寄存器。

分组寄存器R8-R12
1、FIQ模式分组寄存器R8-R12
2、FIQ以外的分组寄存器R8-R12
分组寄存器R8-R12
1、FIQ模式分组寄存器R8-R12
2、FIQ以外的分组寄存器R8-R12

分组寄存器R13、R14
1、寄存器R13通常做堆栈指针SP
2、寄存器R14用作子程序链接寄存器(Link Register-LR),也称为LR,指向函数的返回地址。
r15充当程序寄存器PC,r14(link register)存储子程序的返回地址,r13存储的是堆栈地址。

程序计数器
寄存器R15被用作程序计数器,也称为PC。其值等于当前正在执行的指令的地址+8(因为在取地址和执行之间多了一个译码的阶段)。

ARM所有工作模式下都可以访问程序状态寄存器CPSR。CPSR包含条件码标志、中断禁止位、当前处理器模式以及其他状态和控制信息。

CPSR在每个异常模式下都有一个对应的物理寄存器——程序状态保存寄存器SPSR。当异常出现时,SPSR用于保存CPSR的值,以便异常返回后恢复异常发生时的工作状态。

MMU

MMU就是负责虚拟地址(virtual address)转化成物理地址(physical address)。
在这里肯定有人跟我一样的疑惑,既然有物理地址我们访问的时候访问物理地址不就完事了吗?为什么要有虚拟地址的存在,然后还要加个专门的硬件去转换,这就是多此一举吗?

其实加入了虚拟地址后有下面两个作用

1) 虚拟内存:有了虚拟内存,可以在处理器上运行比实际物理内存大的应用程序。

2) 内存保护:根据需要对特定的内存区块的访问进行保护,通过这一功能,我们可以将特定的内存块设置成只读、只写或是可同时读写。

ARM CPU上的地址转换过程涉及三个概念:虚拟地址(VA)(CPU内核对外发出VA),变换后的虚拟地址(MVA)(VA被转换为MVA供cache和MMU使用,在此将MVA转换为PA),物理地址(PA)(最后使用PA读写实际设备)。

  1. CPU看到的用到的只是VA,CPU不管VA最终是怎样到PA的。

  2. cache、MMU也是看不到VA的,它们使用的是MVA(VA到MVA的转换是由硬件自动完成的)。

  3. 实际设备看不到VA、MVA,读写设备使用的是PA物理地址。

I/D cache

Cache 是位于 CPU与主存储器DRAM之间的少量超高速静态存储器 SRAM(static RAM),其是为了解决 CPU 与主存之间速度匹配问题而设置的。

Cache又分为I-cache(用来存指令)和D-cache(用来存数据)
(instruction /command /order 指令)

为什么要让cache失效
同我们编写业务代码一样,对配置数据进行更改后需要清空缓存(或让其超时失效)

内存基本分类

1) SDRAM(2440常用)
2) DDR(6410常用)
3) DDR2(210常用)
4) DDR3/ DDR4

DRAM
Dynamic Random Access Memor),即动态随机存取存储器,最为常见的系统内存。DRAM 只能将数据保持很短的时间。为了保持数据,DRAM使用电容存储,所以必须隔一段时间刷新(refresh)一次,如果存储单元没有被刷新,存储的信息就会丢失。 (关机就会丢失数据)

表结构:

内存的内部结构如同一张表格,我们称为l-bank类似与下图,其中每个单元格中可以存放数据

LINUX架构_第3张图片

内存寻址

1) L-Bank

一方面由于技术、成本等原因,不可能只做一个全容量的L-Bank,而另一方面由于SDRAM的工作原理限制,单一的L-Bank将会造成非常严重的寻址冲突,大幅降低内存效率。所以人们在SDRAM内部分割成多个L-Bank。

因此我们在寻址时就要先确定是哪个L-Bank,然后再在这个选定的L-Bank中选择行列地址进行寻址。

2) 行地址(Row)

3) 列地址(Column)

上面已经说明了内存的寻址方法,其实其内部构造如下图
LINUX架构_第4张图片
内存芯片容量的计算

内存芯片的容量就是所有的L-Bank中的存储单元的总容量,那么我们可以得到总的存储单元数量

存储单元数量=行数×列数(一个L-Bank的存储单元数量)×L-Bank的数量

那么如何知道一个存储单元的容量呢?其实在内存芯片的文档中都会有说明,大家可以自己找找看。大家也可以参考其芯片命名进行计算,这里有篇博文简单分析了常见内存芯片的命名规则http://blog.chinaunix.net/uid-20964486-id-1831487.html

编程分析

我们的芯片通过存储控制器对内存进行访问,我们对内存的初始化起始就是对控制器的初始化,所以我们知道怎么对存储控制器进行初始化,
内存的位置:
LINUX架构_第5张图片
从上图可知210的DRAM分为两个区域,DRAM0和DRAM1总共为1.5G大小,分别通过DMC0和DMC1进行控制,我们要根据自己手头的板子进行确定。

Linux体系结构(linux系统构成)

Linux可以分为两部分,分别为用户空间和内核空间具体如下图:
LINUX架构_第6张图片

a) 用户空间包括:用户的应用程序、C库

b) 内核空间包括:系统调用接口、内核(狭义内核)、平台架构相关的代码

为什么要分为内核空间和用户空间?

我们在分析u-boot的时候就说到过,我们的cpu在不同的工作模式下可以访问的寄存器是不一样的,所以为了保护我们的操作系统,避免用户程序将内核搞崩,所以进行了内核空间和用户空间的划分。

Linux内核结构(广义内核)

Linux内核由七个部分构成,具体如下图:
LINUX架构_第7张图片

a) 系统调用接口(SCI):open、read、write等系统调用
b) 进程管理(PM):创建进程、删除进程、调度进程等
c) 内存管理(MM):内存分配、管理等
d) 虚拟文件系统(VFS):为多种文件系统提供统一的操作接口
e) 网络协议栈:提供各种网络协议
f) CPU架构相关代码(Arch):为的是提高至移植性
g) 设备驱动程序(DD):各种设备驱动,占到内核的70%左右代码

源码目录简介
其源码主要有以下目录(介绍重要目录):

a) Arch目录:存放处理器相关的代码。下设子目录,分别对应具体的CPU,每个子目录有boot,mm,以及kernel三个子目录,分别对应系统引导以及存储管理,和系统调用

b) Include目录:内核所需要的大部分头文件目录。与平台无关的在include/linux子目录下,与平台相关的则放在include相应的子目录中。

c) fs目录:存放各种文件系统的实现代码。

d) init目录:init子目录包含核心的初始化代码(不是系统的引导代码)。其包含两个文件main.c和version.c,可以用来研究核心如何工作。

e) ipc目录:包含核心进程间的通信代码。

f) kernel目录:包含内核管理的核心代码。与硬件相关代码放在arch/*/kernel目录下。

g) mm目录:包含了所有的内存管理代码。与硬件相关的内存管理代码位于arch/*/mm目录下。

h) scripts目录:包含用于配置核心的脚本文件。

i) lib目录:包含了核心的库代码,与硬件相关的库代码被放在arch/*/lib/目录下

动态加载内核模块

linux内核提供在运行时可进行扩展的特性,这意味着当系统启动并运行时,我们可以向内核添加或移除部分功能。

我们在运行时添加到内核中的代码就被成为动态可加载内核模块,我们简称为内核模块。

内核模块的相关操作

a) 加载内核模块:insmod
b) 卸载内核模块:rmmod
c) 查看内核模块:lsmod

模块声明

a) MODULE_LICENSE(“GPL”):内核可以识别的许可证有GPL(任意版本GNU通用公共许可证)、GPL v2等
b) MODULE_AUTHOR(“作者”):声明作者信息可以不用
c) MODULE_VERSION(“版本”):声明版本信息也可以不用
d) MODULE_DESCRIPTION(“功能描述”):声明模块功能,可不用
ref:https://www.cnblogs.com/wrjvszq/p/4260996.html

1、内核模块编写
2、Makefile编写
3、编译Makefile
4、加载
5、查看
6、卸载
printk函数为内核打印函数,其和printf函数功能类似,不过比printf多了打印权限一共有8个级别,printk的日志级别定义如下(在include/linux/kernel.h中):

1 #define KERN_EMERG 0 //紧急事件消息,系统崩溃之前提示,表示系统不可用
2 #define KERN_ALERT 1 //报告消息,表示必须立即采取措施
3 #define KERN_CRIT 2 //临界条件,通常涉及严重的硬件或软件操作失败
4 #define KERN_ERR 3 //错误条件,驱动程序常用KERN_ERR来报告硬件的错误
5 #define KERN_WARNING 4 //警告条件,对可能出现问题的情况进行警告
6 #define KERN_NOTICE 5 //正常但又重要的条件,用于提醒
7 #define KERN_INFO 6 //提示信息,如驱动程序启动时,打印硬件信息
8 #define KERN_DEBUG 7 //调试级别的消息

Linux地址映射

LINUX架构_第8张图片

用户空间(0~3G)

a) 空间简介

其从0x00000000到0xBFFFFFFF共3GB的线性地址空间,每个进程都有一个独立的3GB用户空间,当然这是虚拟的空间。

b) 如何转换为物理空间

这一部分虚拟空间到物理空间的转换方法通过MMU地址转换。

内核空间(3~4G)

其中0xC0000000到0xFFFFFFFF共1GB大小,内核空间又可以根据映射方式的不同分为下面四块,我们一一分析

a) 内核逻辑地址空间

l 空间简介

其从0xC0000000到high_memory(图中896MB的地方)最大为896MB(也就是说这块空间有可能不满,但最大为896MB),当然是虚拟空间。

注:在此注意一下896MB我们一会在分析。
如何转换为物理空间

这一部分虚拟地址与物理内存中对应的地址只差一个固定偏移量(3G),如果内存物理地址空间从0x00000000地址编址,那么这个固定偏移量就是PAGE_OFFSET(如上图)

b) Vmalloc空间

l 空间简介

其地址没有严格的界限,这段空间既可以访问到我们的高端内存,也可以访问到低端内存。(高端和低端一会解释)

l 如何转换为物理空间

不是通过简单的线性关系映射,在此不研究。
c) 永久内核映射

l 空间简介

其固定用来访问高端内存。

l 如何转换为物理空间

不是通过简单的线性关系映射,在此不研究。

d) 固定映射

l 空间简介

其在系统初始化期间永久映射I/O地址空间,或者特殊的寄存器。

1) 低端内存

内核逻辑地址空间所映射的物理内存就是低端内存(实际物理内存的大小,但是小于896MB)

2) 高端内存

低端内存地址之上的物理内存是高端内存(物理内存896MB之上)。

3) 896MB来由

Linux将内存分为内核空间和用户空间,其中内核空间中的0xC0000000~high_memory部分用来映射物理内存,但是我们还需要映射I/O空间和固定的寄存器,所以留出了high_memory~0xFFFFFFFF之间的地址来映射I/O空间和固定的寄存器,而在X86平台根据经验设定了这个high_memory为896MB。

Linux内存分配

通过上面的介绍我们对linux对内存的管理,以及地址的映射有了一个了解,下面我们来分析linux是如何进行内存分配的。
LINUX架构_第9张图片
通过上图我们可以分析出内存的分配过程

  1. 由malloc、fork等系统调用和kmalloc、vmalloc申请得到虚拟内存。

  2. 在我们使用该内存的时候,产生请页异常(kmalloc除外)

  3. 从空闲的页框分配物理内存,和虚拟地址建立映射。

注:kmalloc申请空间是不用经过请页异常的,返回的虚拟地址已经对应了物理内存。Kmalloc可以分配到连续的物理内存,vmalloc分配的是非连续的物理内存。