如果这篇博客帮助到你,可以请我喝一杯咖啡~
CC BY 4.0 (除特别声明或转载文章外)
实验题目
系统调用
实验目的
- 理解系统调用的实现方法。
- 实现原型操作系统中一些基本的系统调用。
- 设计并实现一测试系统调用的用户程序,利用系统调用实现用户界面和内部功能。
- 在原型操作系统上建立一个初步 C 语言开发环境,理解操作系统与高级语言之间的关系。
实验方案
实验环境
软件
- Windows 10, 64-bit (Build 17763) 10.0.17763
- Windows Subsystem for Linux [Ubuntu 18.04.2 LTS]:WSL 是以软件的形式运行在 Windows 下的 Linux 子系统,是近些年微软推出来的新工具,可以在 Windows 系统上原生运行 Linux。
- gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04):C 语言程序编译器,Ubuntu 自带。
- NASM version 2.13.02:汇编程序编译器,通过
sudo apt install nasm
安装在 WSL 上。 - Oracle VM VirtualBox 6.0.4 r128413 (Qt5.6.2):轻量开源的虚拟机软件。
- VSCode - Insiders v1.33.0:好用的文本编辑器,有丰富的插件。
- hexdump for VSCode 1.7.2: VSCode 中一个好用的十六进制显示插件。
- GNU Make 4.1:安装在 Ubuntu 下,一键编译并连接代码,生成最终的文件。
大部分开发环境安装在 WSL 上,较之于双系统、虚拟机等其他开发方案,更加方便,也方便直接使用 Linux 下的一些指令。
硬件
开发环境配置
所用机器型号为 VAIO Z Flip 2016
- Intel(R) Core(TM) i7-6567U CPU @3.30GHZ 3.31GHz
- 8.00GB RAM
虚拟机配置
- 处理器内核总数:1
- RAM:4MB
实验过程
实验代码
bootloader.asm
和上一个实验中的代码完全相同,不再放出。
kernel.asm
操作系统内核的汇编部分代码,提供int 33
中断和 1~4 的四个 ah 功能号在屏幕的四个象限上显示自定义信息,检测到Ctrl + C
时返回。
同时,提供了如下的全局函数供 C 语言部分调用。
_getch
从屏幕上无回显地读入一个字符。_getCursor
返回屏幕光标的位置。_setCursor
设置屏幕光标的位置。_putC
向光标位置写入一个字符。_pageUP
屏幕内容向上滚动。_loadProgram
加载用户程序。
getch(),gets(),putch(),puts(),scanf()和 printf()函数的汇编部分代码均通过调用上述函数实现。
%macro print 5 ; string, length, x, y, color
pusha
push ax
push bx
push cx
push dx
push bp
push ds
push es
mov ax, 0b800H
mov gs, ax
mov ax, cs
mov ds, ax
mov bp, %1
mov ax, ds
mov es, ax
mov cx, %2
mov ax, 1300H
mov dh, %3
mov dl, %4
mov bx, %5
int 10H
pop es
pop ds
pop bp
pop dx
pop cx
pop bx
pop ax
popa
%endmacro
%macro setIVT 2
push es
push ds
push si
pusha
mov ax, 0000H
mov es, ax
mov ax, %1
mov bx, 4
mul bx
mov si, ax
mov ax, %2
mov [es:si], ax
add si, 2
mov ax, cs
mov [es:si], ax
popa
pop si
pop ds
pop es
%endmacro
bits 16
UserPrgOffset equ 0a100H
PrgSectorOffset equ 0
extern terminal
global _start
global _getch
global _getCursor
global _setCursor
global _putC
global _pageUP
global _loadProgram
_start:
setIVT 8, int8
setIVT 33, int33
call terminal
ret
_getCursor:
push ebp
mov ebp, esp
push ebx
sub esp, 4
mov eax, 768
mov edx, 0
mov ebx, edx
int 0x10
mov eax, edx
mov dword [ebp-8], eax
mov eax, dword [ebp-8]
add esp, 4
pop ebx
pop ebp
ret
_getch:
mov ah, 01H
int 16H
jz _getch
mov ah, 00H
int 16H
ret
_setCursor:
push ebp
mov ebp, esp
push ebx
mov eax, 512
mov ecx, 0
mov edx, dword [ebp+8]
mov ebx, ecx
int 0x10
pop ebx
pop ebp
ret
_putC:
push ebp
mov ebp, esp
push ebx
mov eax, dword [ebp+8]
or ah, 9
mov edx, dword [ebp+12]
mov ecx, 1
mov ebx, edx
int 0x10
pop ebx
pop ebp
ret
_pageUP:
push ebp
mov ebp, esp
mov eax, dword [ebp+8]
or ah, 6
mov ecx, 0
mov edx, 184fh
int 0x10
pop ebp
ret
_loadProgram:
push ebp
mov ebp, esp
push ax
push bx
push cx
push dx
push es
mov ax, cs
mov es, ax
mov bx, UserPrgOffset
mov ah, 2
mov al, 2
mov dl, 0
mov dh, 1
mov ch, 0
mov cl, byte [ebp+8]
add cl, PrgSectorOffset
int 13H
call UserPrgOffset
pop es
pop dx
pop cx
pop bx
pop ax
mov esp, ebp
pop ebp
ret
int8:
cli
pusha
push eax
call draw_slash
mov al, 20H
out 20H, al
out 0a0H, al
pop eax
popa
sti
iret
int33:
cmp ah,1
jne prg2
mov word[n], 12
mov word[m], 30
mov word[top], 2
mov word[left], 40
mov word[length], 8
mov word[msg], msg1
call show
iret
prg2:
cmp ah,2
jne prg3
mov word[n], 12
mov word[m], 30
mov word[top], 2
mov word[left], 0
mov word[length], 10
mov word[msg], msg2
call show
iret
prg3:
cmp ah,4
jne prg4
mov word[n], 12
mov word[m], 20
mov word[top], 13
mov word[left], 0
mov word[length], 20
mov word[msg], msg3
call show
iret
prg4:
mov word[n], 12
mov word[m], 14
mov word[top], 13
mov word[left], 40
mov word[length], 26
mov word[msg], msg4
call show
iret
draw_slash:
print bar,1,24,78,7
cmp byte[bar],'|'
jne rslash
mov byte[bar],'/'
ret
rslash:
cmp byte[bar],'/'
jne hslash
mov byte[bar],'-'
ret
hslash:
cmp byte[bar],'-'
jne lslash
mov byte[bar],'\'
ret
lslash:
mov byte[bar],'|'
ret
show:
dec dword[cnt]
jnz show
mov dword[cnt],99999999
mov word ax, [t]
mov word bx, [n]
add bx, bx
sub bx, 2
xor dx, dx
div bx
cmp dx, [n]
jb xok
sub bx, dx
mov dx, bx
xok:
add dx, [top]
mov word[x], dx
mov word ax, [t]
mov word bx, [m]
add bx,bx
sub bx,2
xor dx, dx
div bx
cmp dx, [m]
jb yok
sub bx, dx
mov dx, bx
yok:
add dx,[left]
mov word [y],dx
inc word[t]
print [msg],[length],[x],[y],[x]
mov ah, 01H
int 16H
jz show
print msgouch,10,[x],[y],[x]
mov ah, 00H
int 16H
cmp ax, 2e03H
jne show
ret
datadef:
cnt dd 1
t dw 0
x dw 1
y dw 0
n dw 12
m dw 32
top dw 2
left dw 40
length dw 8
msg dw 1
msg1 db ' wu-kan '
msg2 db ' 17341163 '
msg3 db ' wu.kan@foxmail.com '
msg4 db ' https://wu-kan.github.io '
msgouch db 'Ouch!Ouch!'
bar db '|'
kerner.c
操作系统内核 C 语言部分的代码。和上一实验相比没有改变,这里不再放出。
link.ld
将wukos.asm
和kernel.c
两个文件编译出来的内容连接起来。和上一个实验中的完全相同,不再放出。
prg1.asm~prg4.asm
直接调用 int 33 中断和 ah 功能号实现。
org 0a100H
push ax
mov ah,1
int 33
pop ax
ret
上面是 prg1.asm 的内容,其余同理,不再放出。
Makefile
和上一个实验完全相同,不再放出。
运行结果
如上图,进入操作系统后开始了「无敌风火轮」(右下角)。 如上图,使用exec
指令轮流运行我的四个程序,分别调用中断int 33
的四个功能号。按下 Ctrl+C 可以退出程序。程序检测到键盘输入,因此显示Ouch!Ouch!
风火轮仍然在转。 输入若干指令。
风火轮仍然在转。 继续输入若干指令,此时超出屏幕显示范围,自动滚屏。
风火轮仍然在转。
实验总结
得益于前两个实验,本次实验较为熟练地完成了任务。看着自己的操作系统不断地完善,心中还是很有成就感的。同时,由于踩过了许多寄存器保护的坑,这次在调用功能号前记得将其值压栈,于是很顺利没有出现问题。