8 %include "sconst.inc"
9
10 _NR_get_ticks equ 0 ; 要跟 global.c 中 sys_call_table 的定义相对应!
11 INT_VECTOR_SYS_CALL equ 0x90
12
13
14 ; 导出符号
15 global get_ticks
16
17
18 bits 32
19 [section .text]
20
21 ; ====================================================================================
22 ; get_ticks
23 ; ====================================================================================
24 get_ticks:
25 mov eax, _NR_get_ticks;用eax传递系统调用参数
26 int INT_VECTOR_SYS_CALL
27 ret
61 PUBLIC void init_prot()
62 {
63 init_8259A();
64
65 // 全部初始化成中断门(没有陷阱门)
......
98 init_idt_desc(INT_VECTOR_SYS_CALL, DA_386IGate, sys_call, PRIVILEGE_USER);
}
因为我们使用了eax来传递系统调用的参数,但是eax又被用于保存进程上下文中的运算,所以我们修改save函数,其中的eax换成esi:
save:
...
325 mov esi, esp ; esi = 进程表起始地址
326
327 inc dword [k_reenter] ; k_reenter++;
328 cmp dword [k_reenter], 0 ; if(k_reenter ==0)
329 jne .1 ; {
330 mov esp, StackTop ; mov esp, StackTop <-- 切换到内核栈
331 push restart ; push restart
332 jmp [esi + RETADR - P_STACKBASE] ; return;
333 .1: ; } else { 已经在内核栈,不需要再切换
334 push restart_reenter ; push restart_reenter
335 jmp [esi + RETADR - P_STACKBASE] ; return;
336 ; }
...
ret
342 sys_call:
343 call save
344
345 sti
346
347 call [sys_call_table + eax * 4]
348 mov [esi + EAXREG - P_STACKBASE], eax
349
350 cli
351
352 ret
28 PUBLIC t_sys_call sys_call_table[NR_SYS_CALL] = {sys_get_ticks};
23 typedef void* t_sys_call;注意,这是一个指针,不是一个函数指针
45 PUBLIC int sys_get_ticks()
46 {
disp_str("+");
47 return ticks;
48 }
47 /* proc.c */
48 PUBLIC int sys_get_ticks(); /* t_sys_call */
49
50 /* syscall.asm */
51 PUBLIC void sys_call(); /* t_pf_int_handler */
52 PUBLIC int get_ticks();
我们来理清调用关系:get_tricks >>sys_call(eax=!!) >> sys_get_tricks
70 void TestA()
71 {
72 while(1){
get_tricks();
73 disp_str("A");
74 disp_int(i++);
75 disp_str(".");
76 delay(1);
77 }
78 }
然后编译运行即可。
59 /* 初始化 8253 PIT */
60 out_byte(TIMER_MODE, RATE_GENERATOR);
61 out_byte(TIMER0, (t_8) (TIMER_FREQ/HZ) );
62 out_byte(TIMER0, (t_8) ((TIMER_FREQ/HZ) >> 8));
63 /* 初始化 8253 PIT 完毕 */
40 PUBLIC void milli_delay(int milli_sec)
41 {
42 int t = get_ticks();
43
44 while(((get_ticks() - t) * 1000 / HZ) < milli_sec) {}
45 }
76 void TestA()
77 {
78 while(1){
79 disp_str("A");
80 disp_int(get_ticks());
81 disp_str(".");
82 milli_delay(1000);
83 }
84 }
这里,比较巧妙的是,我们的计时函数放在用户态,如果将进程数量控制在1,那么就能得到一个比较精确的时间控制。
5.总结
5.1系统调用的由来:
在linux类型的系统中,系统调用是通过中断来实现的,软件中断。系统调用的过程如下:
系统调用使用方式:
因为中断向量数目十分有限,显然不能让一个系统调用仅仅对应一个中断号码,我们采用了中断号对应系统调用;而不同的系统调用,对应不同的调用号码的方式。这里,我们用eax传输系统调用号码。
比如,一个传统的调用方式:mov eax,3;int 80h(假设80h是系统调用对应的中断号)
但是,这里有一个问题,系统调用号码都不相同,而且参数不等,上层应用程序如何来使用这些系统调用呢。很显然,我们需要针对每个系统调用来起一个名字,给一个更加人性化的调用接口,这就是系统调用的由来。
5.2系统调用的添加方式:
如果是从0开始添加,我们参考上面的过程即可。我们总结一下如何在上面的基础上,添加新的系统调用:
1)NR_SYS_CALL 加一 :const.h
2)给sys_call_table 增加一个新成员sys_foo:global.h
3)sys_foo 函数体:where
4)sys_foo函数申明:proto.h
foo的函数申明:proto.h
5)_NR_foo的定义:syscall.asm
6)foo的函数体:syscall.asm
7)添加global foo:syscall.asm
8)如果参数个数发生变化,有所增加,需要修改sys_call:kernel.asm