之前原本想间接看 Padavan 的源码,前面发现好多 Linux 零碎函数十分含糊,还是须要零碎的学习一下。当初就从零开始学习,在这记录一下学习的轨迹。之前为了编译 Padavan 的零碎,装置了一个 Ubuntu 16.4 的零碎,刚好这外面比拟洁净,尽管有一些常识之前都理解过了,我还是依照开发指南介绍的一步步来做吧。在这里举荐一个 Linux 笔记,外面有罕用的 Linux 命令,很不便。
一、Linux 基本操作
- 创立编程的文件夹,W22 代表第 22 周,现打算每周更新一篇。
mkdir -r C_Program//W22
-
配置 vim 的一些参数。无心中发现在 github 外面搜 vim config 会有很多我的项目能够把 vim 配置的很花哨,我临时还是依照简略的来吧,这里举荐一个 VIM 配置,我的配置如下:
" 在家目录下创立 .vimrc 文件,这样这个配置不会影响其余用户 vi ~/.vimrc " 配置如下 set tabstop=4 "设置 softtabstop 有一个益处是能够用 Backspace 键来一次" 删除 4 个空格. softtabstop 的值为正数, 会应用 shiftwidth " 的值, 两者保持一致, 不便对立缩进. set softtabstop=-1 " 主动缩进时, 缩进长度为 4 set shiftwidth=4 set noexpandtab set nu set autoindent
-
来实现第一个 hello world
cheney@ubuntu:~/C_Program/W22$ gcc hellow_world.c -o hellow_world cheney@ubuntu:~/C_Program/W22$ ls hellow_world hellow_world.c cheney@ubuntu:~/C_Program/W22$ ./hellow_world Hello World!
二、Makefile
Makefile 记录一些重点吧,这个之前也也些许理解,举荐一个视频 [linux 从零到精通] gcc 和 Makefile
- 命令列表中的每条命令必须以 TAB 键开始,不能应用空格!
执行过程
- make 命令会在当前目录下查找以 Makefile(makefile 其实也能够)命名的文件。
- 当找到 Makefile 文件当前就会依照 Makefile 中定义的规定去编译生成最终的指标文件。
- 当发现指标文件不存在,或者指标所依赖的文件比指标文件新 (也就是最初批改工夫比指标文件晚) 的话就会执行前面的命令来更新指标。
- 自动化变量
自动化变量就是这种变量会把模式中所定义的一系列的文件主动的挨个取出,直至所有的合乎模式的文件都取完,自动化变量只应该呈现在规定的命令中- 伪指标
个别的指标名都是要生成的文件,而伪指标不代表真正的指标名,在执行 make 命令的时候通过指定这个伪指标来执行其所在规定的定义的命令。- 函数
Makefile 中的函数是曾经定义好的,咱们间接应用,不反对咱们自定义函数
# Makefile 根本规格
main: main.o input.o calcu.o
gcc main.o input.o calcu.o -o main
main.o: main.c
gcc -c main.c
input.o: input.c
gcc -c input.c
calcu.o: calcu.c
gcc -c calcu.c
clean:
rm -rf main.o input.o calcu.o
rm -rf main
(1) Makefile 变量
- =(替换, 以最初面前面呈现的为最终值)
- +=(追加,能够在原变量上减少)
- :=(恒等于, 常量)
- ?= (若之前没有赋值,则赋值)
-
$(var) (援用变量)
# Makefile 变量
TAR = main
obj = main.o input.o calcu.o
CC := gcc
$(TAR): $(OBJ)
$(CC) $(OBJ) -o $(TAR)
main.o: main.c
$(CC) -c main.c -o main.o
input.o: input.c
$(CC) -c input.c -o input.o
calcu.o: calcu.c
$(CC) -c calcu.c -o input.o
clean:
rm -rf $(tar) $(obj)
(2)Makefile 模式规定
- % 任意的 (%.c %.o 任意的.c 或者.o)
- * 所有的 (.c .o 所有的.c 或者 .o)
# Makefile 模式规定
TAR = main
obj = main.o input.o calcu.o
CC := gcc
$(TAR): $(OBJ)
$(CC) $(OBJ) -o $(TAR)
%.o: %.c
$(CC) -c %.c -o %.o #这句不能真执行, 通配符“%”只能用在规定中,只有在规定中它才会开展,如果在变量定义和函数应用时,通配符不会主动开展
clean:
rm -rf $(tar) $(obj)
(3)Makefile 自动化变量
- $@ 规定中的指标汇合,在模式规定中,如果有多个指标的话,“$@”示意匹配模
式中定义的指标汇合。 - $^ 所有依赖文件的汇合,应用空格离开,如果在依赖文件中有多个反复的文件,
“$^”会去除反复的依赖文件,值保留一份。 - $^ 依赖文件汇合中的第一个文件,如果依赖文件是以模式 (即“%”) 定义的,那么
“$<”就是合乎模式的一系列的文件汇合。
# Makefile 自动化变量
TAR = main
obj = main.o input.o calcu.o
CC := gcc
$(TAR): $(OBJ)
$(CC) $(OBJ) -o $(TAR)
%.o: %.c
$(CC) -c $^ -o $@
clean:
rm -rf $(tar) $(obj)
(4)Makefile 伪指标
在下面的 Makefile 中,若执行的目录下有一个 clean 的文件,那他的依赖为空,而如果 clean 又没有任何变动的话,上面的语句就不会执行。为了让其执行,能够应用伪指标。
# Makefile 自动化变量
TAR = main
obj = main.o input.o calcu.o
CC := gcc
$(TAR): $(OBJ)
$(CC) $(OBJ) -o $(TAR)
.PHONY : clean #应用伪指标,让 clean 前面的语句肯定执行
%.o: %.c
$(CC) -c $^ -o $@
clean:
rm -rf $(tar) $(obj)
(4)Makefile 条件判断
-
ifeq / ifneq 判断相等 / 不等
- ifeq(var1, var2)
- ifeq ‘var1’, ‘var2’
- ifeq “var1”, “var2”
- ifeq “var1”, ‘var2’
-
ifdef/ifndef 判断是否定义
- ifdef var
(5)Makefile 函数
函数应用办法 $(fun var1,var2 …) 或者 ${fun var1,var2 …}
# $(subst <from>, <to>, text) 实现字符替换
eg: $(subst zzk,zhangsan,my name is lisi) #--> my name is zhangsan
# $(patsubst <pattern>,<replacement>,<text>) 模式字符串替换
eg: $(patsubst %.c,%.o,a.c b.c c.c) #--> a.o b.o c.o
# $(dir<names...>) 获取 names 中的目录局部
eg: $(dir</src/a.c>) #--> /src
# $(notdir<names...>) 获取 names 中的文件名
eg: $(notdir</src/a.c>) #--> a.c
# $(foreach<var>, <list>, <test>) 把参数 <list> 中的单词逐个取出放到参数 <var> 所指定的变量中,而后再执行 <text> 所蕴含的表达式
eg:
names := a b c d
file := $(foreach n, names, $(n).o) #--> a.o b.o c.o d.o
# $(wildcard pattern) 相当于通配符,变量定义和函数应用中应用
eg: $(wildcard *.c) #--> 获取当前目录下所有.c 文件
三、穿插工具链
(1) 穿插编译器介绍
穿插编译器是一种 GCC 编译器,运行在 X86 架构的 PC 上,这个 GCC 编译器是编译 ARM 架构代码的,也就是编译进去的可执行文件是在 ARM 芯片上运行的。
这里举荐的穿插工具链是 Linaro 公司出品的收费工具链,下载地址为 这里。学习指南中抉择的是 arm-linux-gnueabihf,我简略查了一下不同穿插编译器的 区别。
- ABI 是二进制利用程序接口(Application Binary Interface (ABI) for the ARM Architecture),利用二进制接口形容了应用程序(或者其余类型)和操作系统之间或其余应用程序的低级接口。
- EABI 是 嵌入式 ABI。
接下来两个 gcc-arm-linux-gnueabi 和 gcc-arm-linux-gnueabihf 不同只是在于 gcc 的选项 -mfloat-abi 的默认值不同。gcc 的选项 -mfloat-abi 有三种值 soft,softfp,hard(其中后两者都要求 arm 里有 fpu 浮点运算单元,soft 与后两者是兼容的,但 softfp 和 hard 两种模式互不兼容)
- soft 不必 fpu 进行浮点计算,即便有 fpu 浮点运算单元也不必, 而是应用软件模式。
- softfp armel 架构 (对应的编译器为 gcc-arm-linux-gnueabi) 采纳的默认值,用 fpu 计算,然而传参数用一般寄存器传,这样中断的时候,只须要保留一般寄存器,中断负荷小,然而参数须要转换成浮点的再计算。
- hard armhf 架构 (对应的编译器 gcc-arm-linux-gnueabihf) 采纳的默认值,用 fpu 计算,传参数也用 fpu 中的浮点寄存器传,省去了转换, 性能最好,然而中断负荷高。
(2) 穿插编译器装置
- 将穿插编译器下载到电脑,下载地址为 这里,我装的虚拟机是 64 位版本,下载 gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz,若装置的 32 位版本,则装置 gcc-linaro-4.9.4-2017.01-i686_arm-linux-gnueabihf.tar.xz。
- 依照我之前的文章,将下载的穿插编译器共享到 Ubuntu 零碎。
- 执行命令创立文件夹
sudo mkdir /usr/local/arm
- 将共享到 Ubuntu 的穿插编译器拷贝到刚创立的文件夹
sudo cp gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz /usr/local/arm
- 执行命令解压缩
sudo tar -vxf gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf.tar.xz
- 接下来批改环境变量,执行
sudo vi /etc/profile
,在关上的文件最初一行增加export PATH=$PATH:/usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/bin
, 实现后保留退出即可。 - 而后是装置两个相干的库
sudo apt install lsb-core lib32stdc++6
- 接下来输出
arm-linux-gnueabihf-gcc -v
,能够查看到其版本号等信息则示意装置胜利,如果提醒没有该命令,可能是因为更新的环境变量后须要重启能力配置胜利,重启一下即可。
四、Cortex-A7 MPCore 架构
这一部分先简略理解一下将要应用的内核。
(1) Cortex-A7 MPCore 次要个性
次要个性如下:
- SIMDv2 扩大整形和浮点向量操作。
- 提供了与 ARM VFPv4 体系结构兼容的高性能的单双精度浮点指令,反对全功能的 IEEE754。
- 反对大物理扩大(LPAE),最高能够拜访 40 位存储地址,也就是最高能够反对 1TB 的内存。
- 反对硬件虚拟化。
- 反对 Generic Interrupt Controller(GIC)V2.0。
- 反对 NEON,能够减速多媒体和信号处理算法。
(2) Cortex-A7 MPCore 运行模式
除了 User(USR)用户模式以外,其它 8 种运行模式都是特权模式。这几个运行模式能够通过软件进行任意切换,也能够通过中断或者异样来进行切换。大多数的程序都运行在用户模式,用户模式下是不能拜访零碎所有资源的,要想拜访这些受限的资源就必须进行模式切换。然而用户模式是不能间接进行切换的,用户模式下须要借助异样来实现模式切换,当要切换模式的时候,应用程序能够产生异样,在异样的处理过程中实现处理器模式切换。当中断或者异样产生当前,处理器就会进入到相应的异样模式种,每一种模式都有一组寄存器供异样处理程序应用,这样的目标是为了保障在进入异样模式当前,用户模式下的寄存器不会被毁坏。
模式 | 形容 |
---|---|
User(USR) | 用户模式,非特权模式,大部分程序运行的时候就处于此模式。 |
FIQ | 疾速中断模式,进入 FIQ 中断异样 |
IRQ | 个别中断模式。 |
Supervisor(SVC) | 超级管理员模式,特权模式,供操作系统应用。 |
Monitor(MON) | 监督模式?这个模式用于平安扩大模式。 |
Abort(ABT) | 数据拜访终止模式,用于虚拟存储以及存储保护。 |
Hyp(HYP) | 超级监督模式?用于虚拟化扩大。 |
Undef(UND) | 未定义指令终止模式。 |
System(SYS) | 零碎模式,用于运行特权级的操作系统工作 |
(3) Cortex-A 寄存器组
Cortex-A7 有 9 种运行模式,每一种运行模式都有一组与之对应的寄存器组。每一种模式可见的寄存器包含 15 个通用寄存器 (R0~R14)、一两个程序状态寄存器(CPSR 以后程序状态寄存器 和 SPSR 备份程序状态寄存器) 和一个程序计数器 PC。在这些寄存器中,有些是所有模式所共用的同一个物理寄存器,有一些是各模式本人所独立领有的。
各个模式所领有的寄存器如下表,表中浅色字体的是与 User 模式所共有的寄存器,蓝绿色背景的是各个模式所独有的寄存器。能够看出,在所有的模式中,低寄存器组 (R0~R7) 是共享同一组物理寄存器的,只是一些高寄存器组在不同的模式有本人独有的寄存器。
通用寄存器
通用寄存器分三类,
①、未备份寄存器,即 R0~R7。
②、备份寄存器,即 R8~R14。
③、程序计数器 PC,即 R15。
Ⅰ、未备份寄存器
未备份寄存器指的是 R0~R7 这 8 个寄存器,因为在所有的处理器模式下这 8 个寄存器都是同一个物理寄存器,在不同的模式下,这 8 个寄存器中的数据就会被毁坏。所以这 8 个寄存器并没有被用作非凡用处。
Ⅱ、备份寄存器
- 备份寄存器中的 R8~R12 这 5 个寄存器有两种物理寄存器。FIQ 模式下中断处理程序能够应用 R8fiq~R12fiq 寄存器,因为 FIQ 模式下的 R8~R12 是独立的,因而中断处理程序能够不必执行保留和复原中断现场的指令,从而减速中断的执行过程。
- 备份寄存器 R13(堆栈指针)一共有 8 个物理寄存器,只有用户模式 (User) 和零碎模式 (Sys) 共用的,初始化时须要初始化每个模式对应的堆栈指针。
- 备份寄存器 R14(链接寄存器)一共有 7 个物理寄存器,只有用户模式 (User)、零碎模式(Sys) 和超级监督模式 (Hyp) 所共有的。每种处理器模式应用 R14(LR)来寄存以后子程序的返回地址;当异样产生当前,该异样模式对应的 R14 寄存器被设置成该异样模式将要返回的地址,R14 也能够当作一般寄存器应用。
Ⅲ、程序计数器 PC
程序计数器 R15 也叫做 PC,R15 保留着以后执行的指令地址值加 8 个字节,这是因为 ARM 的流水线机制导致的。ARM 处理器 3 级流水线:取指 -> 译码 -> 执行,这三级流水线循环执行,比方以后正在执行第一条指令的同时也对第二条指令进行译码,第三条指令也同时被取出寄存在 R15(PC)中。对于 32 位的 ARM 处理器,每条指令是 4 个字节,以正在执行的指令为参考点,R15 (PC)值 = 以后执行的程序地位 + 8 个字节。
程序状态寄存器
所有的处理器模式都共用一个 CPSR 物理寄存器,因而 CPSR 能够在任何模式下被拜访。除了 User 和 Sys 这两个模式以外,其余 7 个模式每个都装备了一个专用的物理状态寄存器,叫做 SPSR(备份程序状态寄存器),当特定的异常中断产生时,SPSR 寄存器用来保留以后程序状态寄存器 (CPSR) 的值,当异样退出当前能够用 SPSR 中保留的值来复原 CPSR。
CPSR 和 SPSR 的构造如图:
- N(bit31):当两个补码示意的有符号整数运算的时候,N=1 示意运算对的后果为正数,N=0 示意后果为负数。
- Z(bit30):Z=1 示意运算后果为零,Z=0 示意运算后果不为零;对于 CMP 指令,Z=1 示意进行比拟的两个数大小相等。
- C(bit29):在加法指令中,当后果产生了进位,则 C=1,示意无符号数运算产生上溢,其它状况下 C=0。在减法指令中,当运算中产生借位,则 C=0,示意无符号数运算产生下溢,其它状况下 C=1。对于蕴含移位操作的非加 / 减法运算指令,C 中蕴含最初一次溢出的位的数值,对于其它非加 / 减运算指令,C 位的值通常不受影响。
- V(bit28):对于加 / 减法运算指令,当操作数和运算后果示意为二进制的补码示意的带符号数时,V=1 示意符号位溢出,通常其余位不影响 V 位。
- Q(bit27):仅 ARM v5TE_J 架构反对,示意饱和状态,Q=1 示意累积饱和,Q=0 示意累积不饱和。
- IT[1:0](bit26:25):和 IT[7:2](bit15:bit10)一起组成 IT[7:0],作为 IF-THEN 指令执行状态。
-
J(bit24):仅 ARM_v5TE-J 架构反对,J=1 示意处于 Jazelle 状态,此位通常和 T(bit5)位一起示意以后所应用的指令集,如下表所示:
J | T | 形容 |
---|---|---|
0 | 0 | ARM |
0 | 1 | Thumb |
1 | 1 | ThumbEE |
1 | 0 | Jazelle |
- GE[3:0](bit19:16):SIMD 指令无效,大于或等于。
- IT[7:2](bit15:10):参考 IT[1:0]。
- E(bit9):大小端管制位,E=1 示意大端模式,E=0 示意小端模式。
- A(bit8):禁止异步中断位,A=1 示意禁止异步中断。
- I(bit7):I=1 禁止 IRQ,I=0 使能 IRQ。
- F(bit6):F=1 禁止 FIQ,F=0 使能 FIQ。
- T(bit5):控制指令执行状态,表明本指令是 ARM 指令还是 Thumb 指令,通常和 J(bit24)一起表明指令类型,参考 J(bit24)位。
- M[4:0]:处理器模式管制位,含意如下表所示:
M[4:0] | 处理器模式 |
---|---|
10000 | User 模式 |
10001 | FIQ 模式 |
10010 | IRQ 模式 |
10011 | Supervisor(SVC) 模式 |
10110 | Monitor(MON)模式 |
10111 | Abort(ABT)模式 |
11010 | Hyp(HYP)模式 |
11011 | Undef(UND)模式 |
11111 | System(SYS)模式 |