在线环境(无需搭建环境即可复现)
在线实验指导书uCore2020,有些章节无法访问
文章目录
- lab1
- 练习一
- 1、操作系统镜像文件ucore.img生成过程
- `init.o等文件`的生成过程
- `bin/kernal`的生成过程
- `bin/sign`的生成过程
- `bin/bootblock`的生成过程
- `bin/ucore.img`的生成过程
- 2、被系统认为是符合规范的的硬盘主引导扇区的特征
- `<sys/stat.h>` 和 系统调用`stat()`
- 拓展
lab1
练习一
1、操作系统镜像文件ucore.img生成过程
# 详细展现make的编译执行过程
make V= > make.txt
init.o等文件
的生成过程
+ cc kern/init/init.c
gcc -Ikern/init/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/init/init.c -o obj/kern/init/init.o
-
-Ikern/init/:告诉GCC在编译过程中搜索头文件时,除了默认的目录外,还需要在kern/init/目录下查找。
-
-fno-builtin:这个参数告诉GCC不要使用内置函数。在某些情况下,这可以提高编译后的代码的可移植性,因为它避免了依赖特定编译器的内部实现。
-
-Wall:这个参数开启所有警告。在开发过程中,通过查看这些警告信息,可以帮助找到潜在的错误或不好的编程习惯。
-
-ggdb:这个参数告诉GCC生成调试信息,这对于调试程序时使用GDB调试器非常有用。
-
-m32:这个参数指定生成的是32位代码。这在构建针对32位系统的程序时非常有用。
-
-gstabs:这个参数告诉GCC使用STABS格式生成调试信息,这是GDB调试器支持的一种格式。
-
-nostdinc:这个参数告诉GCC不要使用标准的stdio.h等标准库头文件。这可能意味着你已经包含了特定的头文件,或者你不想使用标准库中的某些功能。
-
-fno-stack-protector:这个参数告诉GCC不要为代码段添加栈保护。这可能是在某些安全敏感的应用中,为了性能优化而做出的妥协。
-
-Ilibs/、-Ikern/debug/、-Ikern/driver/、-Ikern/trap/、-Ikern/mm/:这些参数分别指定了其他包含目录,用于包含特定目录下的头文件。
-
-c:这个参数告诉GCC只进行编译,不进行链接。这意味着只生成目标文件,而不会生成可执行文件。
-
kern/init/init.c:编译的源代码文件。
-
-o obj/kern/init/init.o:这是编译后的目标文件的输出路径。obj/目录通常用于存放编译后的目标文件。
bin/kernal
的生成过程
ld -m elf_i386 -nostdlib -T tools/kernel.ld -o bin/kernel obj/kern/init/init.o obj/kern/libs/readline.o obj/kern/libs/stdio.o obj/kern/debug/kdebug.o obj/kern/debug/kmonitor.o obj/kern/debug/panic.o obj/kern/driver/clock.o obj/kern/driver/console.o obj/kern/driver/intr.o obj/kern/driver/picirq.o obj/kern/trap/trap.o obj/kern/trap/trapentry.o obj/kern/trap/vectors.o obj/kern/mm/pmm.o obj/libs/printfmt.o obj/libs/string.o
-
-m elf_i386:指定了目标架构为Intel x86架构,使用ELF(Executable and Linkable Format)格式。
-
-nostdlib:表示不使用标准库,这意味着链接器不会自动包含任何标准库函数。
-
-T tools/kernel.ld:指定了一个链接脚本文件kernel.ld,其中包含了关于如何链接和布局各个目标文件的详细信息。
-
-o bin/kernel:指定了输出文件的名称为bin/kernel
目标文件(.o文件)的列表,它们包含了内核的各个部分:
obj/kern/init/init.o
:初始化模块的代码
obj/kern/libs/readline.o
:用于命令行输入处理的代码
obj/kern/libs/stdio.o
:标准输入输出库的代码
obj/kern/debug/kdebug.o
、obj/kern/debug/kmonitor.o
、obj/kern/debug/panic.o
:调试相关的代码
obj/kern/driver/clock.o
、obj/kern/driver/console.o
、obj/kern/driver/intr.o
、obj/kern/driver/picirq.o
:驱动程序相关的代码
obj/kern/trap/trap.o
、obj/kern/trap/trapentry.o
、obj/kern/trap/vectors.o
:中断处理相关的代码
obj/kern/mm/pmm.o
:内存管理模块的代码
obj/libs/printfmt.o
、obj/libs/string.o
:字符串处理和格式化函数的代码
ld工具会将上述*.o
文件链接到一起,合并成一个完整的内核映像文件
bin/sign
的生成过程
+ cc tools/sign.c
gcc -Itools/ -g -Wall -O2 -c tools/sign.c -o obj/sign/tools/sign.o
gcc -g -Wall -O2 obj/sign/tools/sign.o -o bin/sign
GCC首先会编译tools/sign.c文件生成目标文件obj/sign/tools/sign.o,然后会将这个目标文件链接成一个可执行文件bin/sign。这个可执行文件可以用来执行特定的签名操作,可能是用于对文件或数据进行数字签名的工具。
bin/bootblock
的生成过程
+ cc boot/bootasm.S
gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootasm.S -o obj/boot/bootasm.o
+ cc boot/bootmain.c
gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootmain.c -o obj/boot/bootmain.o
+ ld bin/bootblock
ld -m elf_i386 -nostdlib -N -e start -Ttext 0x7C00 obj/boot/bootasm.o obj/boot/bootmain.o -o obj/bootblock.o
Makefile中的命令对obj/bootblock
进一步处理得到bin/bootblock
bin/ucore.img
的生成过程
dd if=/dev/zero of=bin/ucore.img count=10000
dd if=bin/bootblock of=bin/ucore.img conv=notrunc
dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc
Linux系统中使用dd命令来创建和操作磁盘镜像
dd if=/dev/zero of=bin/ucore.img count=10000
- dd:这是用于复制文件的命令,它可以将数据从输入文件(if)复制到输出文件(of)。
- if=/dev/zero:指定输入文件为
/dev/zero
。这是一个特殊的文件,读取它会得到无限的0。 - of=bin/ucore.img:指定输出文件为当前目录下的bin/ucore.img。
- count=10000:指定要复制的块的数量,这里是10000块。每块的大小默认为512字节,所以这将创建一个大约5MB的文件。
dd if=bin/bootblock of=bin/ucore.img conv=notrunc
- if=bin/bootblock:指定输入文件为当前目录下的bin/bootblock,这通常是一个启动块或者引导扇区。
- of=bin/ucore.img:指定输出文件为bin/ucore.img,即之前创建的镜像文件。
- conv=notrunc:指定不截断输出文件,即保留 ucore.img 文件中超出 bin/bootblock 大小的部分。这条命令会将 bootblock 文件的内容写入到 ucore.img 的开头,而不改变 ucore.img 的其余部分。
dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc
- if=bin/kernel:指定输入文件为当前目录下的bin/kernel,这通常是操作系统的内核映像。
- of=bin/ucore.img:同上,输出文件是bin/ucore.img。
seek=1:这个选项告诉dd在输出文件中跳过1块(通常是512字节),从而开始写入的位置不是文件的开始处,而是从第二个块开始。 - conv=notrunc:同上,保持输出文件的原有大小,不截断。
这些命令通常用于构建一个包含引导扇区和内核的磁盘镜像文件,该文件可以被模拟器或者实际的硬件用来启动一个操作系统。在这个例子中,bin/ucore.img最终将包含一个引导块和内核,它们在磁盘上的位置分别是从起始处和第二个块开始。
2、被系统认为是符合规范的的硬盘主引导扇区的特征
sign.c
负责将obj/bootblock.out
加上主引导扇区特征(0x55,0x5A)后转化为bin/bootblock
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
int main(int argc, char *argv[])
{
// 结构体 stat:用于存储通过 stat 系统调用获取的文件状态信息
struct stat st;
// 确保能够正确地提供了所需的两个文件名参数!
if (argc != 3)
{
fprintf(stderr, "Usage: <input filename> <output filename>\n");
return -1;
}
// 使用 stat 函数获取输入文件的状态信息,并检查文件是否存在以及是否可以访问
if (stat(argv[1], &st) != 0)
{
fprintf(stderr, "Error opening file '%s': %s\n", argv[1], strerror(errno));
return -1;
}
// 'obj/bootblock.out' size: 472 bytes
printf("'%s' size: %lld bytes\n", argv[1], (long long)st.st_size);
// 如果输入文件的大小超过 510 字节,打印错误消息并退出。
if (st.st_size > 510)
{
fprintf(stderr, "%lld >> 510!!\n", (long long)st.st_size);
return -1;
}
// 分配一个 512 字节的缓冲区,并用 0 填充它
char buf[512];
memset(buf, 0, sizeof(buf));
// 打开输入文件进行二进制读取,并将文件内容读取到缓冲区中,读取完毕后关闭输入文件
FILE *ifp = fopen(argv[1], "rb");
int size = fread(buf, 1, st.st_size, ifp);
if (size != st.st_size)
{
fprintf(stderr, "read '%s' error, size is %d.\n", argv[1], size);
return -1;
}
fclose(ifp);
// 主引导记录的签名(0x55和0xAA) 在缓冲区的第 510 和 511 个字节位置分别设置值为 0x55 和 0xAA。
buf[510] = 0x55;
buf[511] = 0xAA;
// 打开输出文件进行二进制写入,并将缓冲区(512 字节)写入到输出文件中。
FILE *ofp = fopen(argv[2], "wb+");
size = fwrite(buf, 1, 512, ofp);
if (size != 512)
{
fprintf(stderr, "write '%s' error, size is %d.\n", argv[2], size);
return -1;
}
fclose(ofp);
// 打印成功信息
printf("build 512 bytes boot sector: '%s' success!\n", argv[2]);
return 0;
}
<sys/stat.h>
和 系统调用stat()
<sys/stat.h>
是一个在 Unix 和类 Unix 操作系统中常见的头文件,它定义了用于操作文件系统信息的结构和函数。
系统调用stat()
用于获取文件或文件系统状态信息来填充结构体stat。
//如果成功,返回 0;如果出错,返回 -1 并设置 errno。
int stat(const char *path, struct stat *buf);
以下是 sys/stat.h 中常见的结构和宏定义的简要介绍:
// 结构体 stat:用于存储通过 stat 系统调用获取的文件状态信息
struct stat {
dev_t st_dev; // 文件的设备号
ino_t st_ino; // 文件的 i-node 号
mode_t st_mode; // 文件的保护模式
nlink_t st_nlink; // 文件的链接数
uid_t st_uid; // 文件所有者的用户 ID
gid_t st_gid; // 文件所有者的组 ID
dev_t st_rdev; // 设备文件的设备号,如果是普通文件则为 0
off_t st_size; // 文件大小,字节数
blksize_t st_blksize; // 块大小
blkcnt_t st_blocks; // 块数量
time_t st_atime; // 最后访问时间
time_t st_mtime; // 最后修改时间
time_t st_ctime; // 最后状态改变时间
};
// 宏定义:sys/stat.h 还定义了一些宏,用于检查文件类型。
#define S_ISDIR(m) ((m) & S_IFDIR) // 判断是否为目录
#define S_ISCHR(m) ((m) & S_IFCHR) // 判断是否为字符设备
#define S_ISBLK(m) ((m) & S_IFBLK) // 判断是否为块设备
#define S_ISREG(m) ((m) & S_IFREG) // 判断是否为普通文件
#define S_ISFIFO(m) ((m) & S_IFIFO) // 判断是否为管道
#define S_ISLNK(m) ((m) & S_IFLNK) // 判断是否为符号链接
#define S_ISSOCK(m) ((m) & S_IFSOCK) // 判断是否为套接字
下面是一个使用 sys/stat.h 和 stat 函数的简单示例代码:
#include <stdio.h>
#include <sys/stat.h>
int main() {
struct stat st;
if (stat("example.txt", &st) == -1) {
perror("stat error");
return 1;
}
printf("File size: %ld bytes\\n", st.st_size);
printf("Last modified time: %s", ctime(&st.st_mtime));
return 0;
}
这段代码会尝试获取名为 “example.txt” 的文件状态信息,并打印文件大小和最后修改时间。如果获取失败,它会打印错误信息。
拓展
比较 ISO、BIN/CUE 和 IMG,常见磁盘映像文件解析
关于int main(int argc,char* argv[])详解
聊聊x86计算机启动发生的事?