本系列是博主参考B站课程学习开发一个RISC-V的操作系统的学习笔记,计划从RISC-V的底层汇编指令学起,结合C语言,在Ubuntu 20.04上开发一个简易的操作系统。一个目的是通过实践操作学习和了解什么是操作系统,第二个目的是为之后学习RISC-V的集成电路设计打下一定基础。本系列持续不定期更新,分享出来和大家一同交流进步。
博主是微电子科学与工程专业的学生,对软件和操作系统难免有理解不到位的地方。如有谬误敬请不吝告知,不胜感激。
参考课程及文章:
【Bilibili】[完结] 循序渐进,学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春
嵌入开发是一种比较综合性的技术,它不单指纯粹的软件开发技术,也不单是一种硬件配置技术;它是在特定的硬件环境下针对某款硬件进行开发,是一种系统级别的与硬件结合比较紧密的软件开发技术。
一般来说,我们在主机(Host PC)上对程序进行编辑和编译,通过特定的手段将主机和目标板(Target Board)进行连接,例如WIFI、互联网、有线连接等,使程序在特定的目标板上运行。程序运行在特定的硬件上,操作系统运行的机器也当然要运行在没有操作系统的硬件上。编写操作系统同样是嵌入式开发的一种。
参与编译和运行的机器根据其角色可以分成以下三类:
所以,我们可以对本地编译和交叉编译两种工作环境作如下定义:
例如,如果要查看gcc实际是什么,可以执行如下操作:
$ whereis gcc
gcc: /usr/bin/gcc /usr/lib/gcc /usr/share/gcc /mnt/c/Program Files (x86)/mingw64/bin/gcc.exe /usr/share/man/man1/gcc.1.gz
$ ls -l /usr/bin/gcc
lrwxrwxrwx 1 root root 5 Mar 20 2020 /usr/bin/gcc -> gcc-9
$ ls -l /usr/bin/gcc-9
lrwxrwxrwx 1 root root 22 Oct 24 2022 /usr/bin/gcc-9 -> x86_64-linux-gnu-gcc-9
$ ls -l /usr/bin/x86_64-linux-gnu-gcc-9
-rwxr-xr-x 1 root root 1158288 Oct 24 2022 /usr/bin/x86_64-linux-gnu-gcc-9
可以看到,执行gcc
后,程序实际执行的程序是x86_64-linux-gnu-gcc-9
。GCC被多层符号变量封装在一起了,供用户使用。
GNU 交叉编译工具链(Toolchain)
例子:
GDB即GNU 项目调试器,用于查看另一个程序在执行过程中正在执行的操作,或该程序崩溃时正在执行的操作。
被调试的程序可能与 GDB 在同一台计算机上执行,也可能在另一台计算机(远程)上或者在模拟器上执行。GDB 支持调试多种语言:譬如:Assembly,C,Go,Rust,…
$ gcc -g test.c
$ gdb a.out
(gdb) b 6
(gdb) r
(gdb) p xxx
(gdb) s/n/c
QEMU 是一套由 (Fabrice Bellard) 编写的以 GPL 许可证分发源码的计算机系统模拟软件,在 GNU/Linux 平台上使用广泛。
QEMU 有两种主要运作模式:
make是一种自动化工程管理工具。当工程文件量很大的时候,在Linux系统中每一次编译文件都要手动输入命令。如果文件有一千个,一万个,那我们每次编译输入的指令就及其庞大,对开发效率的影响很大(当然,在这里我们可以对每个文件先编译而不连接,生成很多的*.o
文件,在编译时将所有的*.o
文件连接,但这样的方法远没有编写Makefile优雅)。所以,我们可以编写一个每次编译自动执行的脚本文件,这个文件满足一定的格式,这就是Makefile格式。Makefile配合make,用于描述构建工程过程中所管理的对象以及如何构造工程的过程。make找到Makefile有如下两种方式:
-f
,例如使用make -f Makefile
来编译工程Makefile由一条或多条规则(rule)组成,这是make中最核心的一点。每一条规则由如下的三要素构成:
一个简单的Makefile规则如下:
target...:prerequisites...
command...
...
例如,对于目标hello
,其依赖于文件hello.c
,我们在此基础上添加指令,可以编写如下的Makefile文件:
hello: hello.c
gcc hello.c -o hello
Makefile中还有其他的元素,例如缺省规则、伪规则、行注释等。它们的格式如下:
# 缺省规则,当make的缺省规则(默认规则)不满足当前工程的需求时,可以重写缺省规则以覆盖原有的默认规则
.DEFAULT_GOAL := all
all :
# 伪规则,它的作用是有同名的文件与make clean操作冲突,产生歧义,-f为强制删除
.PHONY : clean
clean:
rm -f *.o
对于一个工程,假设其含有main.c
、file1.c
、file2.c
,则可以编写如下的Makefile文件。它的好处是当单独修改工程中的某个文件,重新编译时只会编译修改过的文件,可以大大节省编译时间。
CC = gcc
TARGET = hello
OBJ = main.o file1.o file2.o
$(TARGET) : $(OBJ)
$(CC) -o $(TARGET) $(OBJ)
main.o: main.c
$(CC) -c main.c
file1.o: file1.c
$(CC) -c file1.c
file2.o: file2.c
$(CC) -c file2.c
或者采用以下省略写法。注意,省略写法使Makefile文件编写更简单,但是可以说可读性极差,笔者认为应该谨慎使用,不要出错。但是采用省略的灵活写法带来的优势也是很大的,如果按如下的写法,那么在工程中每次添加新的.c
文件时,仅需要在OBJ
后添加对应的.o
即可。
$@
:代指目标,即冒号:
之前的内容$^
:代指所有的依赖%.o
:所有的.o
文件,%.c
同理$<
:依赖中的第一个CC = gcc
TARGET = hello
OBJ = main.o file1.o file2.o
CCFLAGS = -c -Wall
$(TARGET) : $(OBJ)
$(CC) -o $@ $^
%.o: %.c
$(CC) $(CCFLAGS) $< -o $@
.PHONY : clean
clean:
rm -f *.o
甚至可以有更加灵活的写法,采用如下的写法,每一次添加新的.c
文件后甚至都不需要更改Makefile文件了:
CC = gcc
TARGET = hello
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))
CCFLAGS = -c -Wall
$(TARGET) : $(OBJ)
$(CC) -o $@ $^
%.o: %.c
$(CC) $(CCFLAGS) $< -o $@
.PHONY : clean
clean:
rm -f *.o
原创笔记,码字不易,欢迎点赞,收藏~ 如有谬误敬请在评论区不吝告知,感激不尽!博主将持续更新有关嵌入式开发、机器学习方面的学习笔记。