操作系统原理、实现与实践(一)

习题

  1. 空白表示任务之间的交互消耗的时间;分时可以使得整体的利用效率更高,某些操作可能需要I/O高耗时,轮转会提高效能;

  2. MBR应该在0柱面、0磁头、0扇区,占一个扇区

    (1)应该放在每个操作系统的第一个扇区;

    (2)引导用户选择操作系统的代码,不同操作系统的位置

    (3)从BIOS找到MBR记录,然后从MBR记录以及用户的选择情况,跳转到指定的操作系统启动代码;

  3. 磁道 1 的上盘面;

    这样设计的原因遵循了CHS(柱面-磁头-扇区)的寻址方式,而且通过上下两个分区能够尽可能减少磁头的移动,节省时间;

  4. 节点关系:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Image
    -- boot/bootsect
    --boot/bootsect.s
    -- boot/setup
    --boot/setup.s
    -- tools/system
    -- boot/head.s
    -- boot/main.s
    -- $ (ARCHIVES) $(DRIVERS)
    -- tools/build
    -- tools/build.c

    命令序列:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    //编译bootsect
    $(AS86) -o boot/bootsect.o boot/bootsect.s
    $(LD86) -s -o boot/bootsect boot/bootsect.o
    //编译setup
    $(AS86) -o boot/setup.o boot/setup.s
    $(LD86) -s -o boot/setup boot/setup.o
    //编译system中的模块
    $(AS) -c -o boot/head.o boot/head.s
    $(AS) -c -o init/main.o boot/main.s
    ...
    //链接system
    $(LD) $(LDFLAGS) boot/head.o init/main.o
    $(ARCHIVES) $(DRIVERS)
    //编译tools
    $(CC) $(CFLAGS) -o tools/build tools/build.c
  5. 读入system模块的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    id = open(argv[3], O_RDONLY | O_BINARY, 0); //读取system模块
    for (i = 0; (c = read(id, buf, sizeof(buf))) > 0; i += c) {
    write(1, buf, c);
    } //写入system模块
    memset(buf, 0, sizeof(buf));//缓冲区至0
    // 用0填充到SYSSIZE的大小
    while (i < SYSSIZE) {
    c = SYSSIZE - i;
    if (c > sizeof(buf)) {
    c = sizeof(buf);
    }
    write(1, buf, c);
    i += c;
    }

实践项目

(1)把Loading system…改为Loading system edited by Leo…

此时字符串的长度变为31个字符,再加上原有的换行符等,总长度为37,对应的CX的的值也要更改为#37;编译后界面显示更改后的字符串,如图所示:

image-20250817212116164

(2) 背景知识:

  • BIOSint 0x10中断有很多的功能号,表格如下:
寄存器 解释
AH 0x13 写入字符串
AL 0x01 写入字符串并移动光标
BH / 显示页码
BL / 字符属性
CX / 字符长度
ES:BP / 指向写入字符串的地址
  • 由于函数调用依赖于栈进行参数传递和返回地址保存,所以在进行函数调用之前,必须先设置好栈,即设置合理的堆栈段寄存器ss和堆栈指针寄存器sp。bootsect.s已经将ss:sp设置为0x9000:0xff00,保证大于启动过程中bootsect.s+setup.s+SYSTEM的所占扇区的和;调用函数需要把当前寄存器的值push进去,然后pop出来再返回,值不会改变;

  • 调用BIOSint 0x10获取当前光标号:

    寄存器 解释
    AH 0x03 功能号
    BH 0 指定要查询的显示页码
    CH / 返回光标的起始扫描线
    CL / 返回光标的结束扫描线
    DH / 返回光标所在的行号
    DL / 返回光标所在的列号
  • 换行包含两个操作:回车(CR)和换行(LF),起源于电传打字机,而现代操作系统做了简化;

操作步骤:

a. 设置好ss/sp堆栈,将某些操作设置为函数,方便调用;

1
2
3
4
! repeat set stack, although it is done in bootsect.s
mov ax, #INITSEG
mov ss, ax
mov sp, #0xFF00

b . 在进入setup模块时,打印Now we are in SETUP

1
2
3
4
5
6
7
8
9
10
mov ax, #SETUPSEG
mov es, ax
call read_cursor
mov cx, #19
mov bx, #0x000c ! red
mov bp, #msg
mov ax, #0x1301 ! write string, move cursor
int 0x10

call print_nl

其中两个函数read_cursorprint_nl分别为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
print_nl:
push ax
push bx
mov ax, #0x0e0d ! CR
int 0x10
mov ax, #0x0e0A ! LR
int 0x10
pop bx
pop ax
ret

read_cursor:
push ax
push bx
push cx
mov ah, #0x03 ! read cursor pos
xor bh, bh
int 0x10
pop cx
pop bx
pop ax
ret

(3) 接(2)的背景知识

  • 获取扩展内存容量将调用BIOSint 0x15中断,功能号为0x88,表格如下:
寄存器 解释
AH 0x88 获取扩展内存容量,返回参数是AX,以KB为单位

在确认disk信息之后,补充:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
	mov ax, #INITSEG
mov ds, ax
mov ax, #SETUPSEG
mov es, ax


! print memory size
call read_cursor
mov cx, #13
mov bx, #0x000c ! red
mov bp, #memory
mov ax, #0x1301
int 0x10

mov ax, [2]
add ax, #1024
mov memsize, ax
mov bp, #memsize
call print_hex

call read_cursor
mov cx, #2
mov bx, #0x0007
mov bp, #memory+13
mov ax, #0x1301
int 0x10
call print_nl

其中[2]是一个二进制数,需要进行转化为十六进制的字符打印出来;调用print_hex函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
!以16进制方式打印栈顶的16位数
print_hex: //打印'0','x'字符
push ax
push bx
push cx
push dx
mov ax, #0x0e30 ! 0
int 0x10
mov ax, #0x0e78 ! x
int 0x10
mov cx, #4
mov dx, (bp)
print_digit: //打印数值
rol dx, #4
mov ax, #0x0e0f
mov bl, #0x0f ! bright green color
and al, dl
add al,#0x30
cmp al, #0x3a
jl outp ! if less,是一个不大于9的数字
add al, #0x07 ! 是a~f,要加7
outp:
int 0x10
loop print_digit
pop dx
pop cx
pop bx
pop ax
ret

(4)在跳转到SYSTEM模块之前 (jmpi 0,8) ,加入一个死循环:

1
2
dead_loop:
jmp dead_loop
image-20250819183114943