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()函数的汇编部分代码均通过调用上述函数实现。
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
%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 ' [email protected] '
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 功能号实现。
1
2
3
4
5
6
org 0a100H
push ax
mov ah,1
int 33
pop ax
ret
上面是 prg1.asm 的内容,其余同理,不再放出。
Makefile
和上一个实验完全相同,不再放出。
运行结果
如上图,进入操作系统后开始了「无敌风火轮」(右下角)。
如上图,使用exec
指令轮流运行我的四个程序,分别调用中断int 33
的四个功能号。按下 Ctrl+C 可以退出程序。程序检测到键盘输入,因此显示Ouch!Ouch!
风火轮仍然在转。 输入若干指令。
风火轮仍然在转。 继续输入若干指令,此时超出屏幕显示范围,自动滚屏。
风火轮仍然在转。
实验总结
得益于前两个实验,本次实验较为熟练地完成了任务。看着自己的操作系统不断地完善,心中还是很有成就感的。同时,由于踩过了许多寄存器保护的坑,这次在调用功能号前记得将其值压栈,于是很顺利没有出现问题。