说明
前言
在Linux0.11中的系统调用是通过int0x80号中断和eax传递的功能号来调用的
对于所有系统调用的实现函数,内核把它们按照系统调用功能号顺序排列成一张函数指针表,然后在中断int 0x80的中断处理过程中根据用户提供的功能号调用对应系统调用函数进行处理
对于系统调用int0x80的中断处理过程,可以把它看作是一个接口程序,实际上每个系统调用功能的处理过程基本上都是通过调用相应的C函数进行的
作用
本程序主要实现系统调用system_call中断int0x80的入口处理过程以及信号检测处理,同时给出了两个系统功能的底层接口,分别是sys_execve和sys_fork
执行流程
代码解析
# 定义子进程停止或结束的信号
SIG_CHLD = 17
# 函数返回时,不同寄存器在堆栈中的位置
EAX = 0x00
EBX = 0x04
ECX = 0x08
EDX = 0x0C
FS = 0x10
ES = 0x14
DS = 0x18
EIP = 0x1C
CS = 0x20
EFLAGS = 0x24
OLDESP = 0x28
OLDSS = 0x2C
# 任务结构中变量的偏移值
state = 0 # 进程状态码
counter = 4 # 任务运行时间片
priority = 8 # 任务优先级(与数字正相关)
signal = 12 # 信号位图,每个比特位代表一种信号
sigaction = 16 # 表明sigaction结构长度必须是16byte
blocked = (33*16) # 受阻信号位图的偏移量
# sigaction结构中的偏移量
sa_handler = 0 # 信号处理过程中的句柄(描述符)
sa_mask = 4 # 信号屏蔽码
sa_flags = 8 # 信号集
sa_restorer = 12 # 恢复函数指针
nr_system_calls = 72 # Linux0.11内核中的系统调用总数
# 申明外部可调用的符号
.globl system_call,sys_fork,timer_interrupt,sys_execve
.globl hd_interrupt,floppy_interrupt,parallel_interrupt
.globl device_not_available, coprocessor_error
# 错误的系统调用号
.align 2
bad_sys_call:
movl $-1,%eax
iret
# 重新执行调度程序入口
.align 2
reschedule:
pushl $ret_from_sys_call
jmp schedule
# 系统调用入口点
.align 2
system_call:
# 检查系统调用号是否出错
cmpl $nr_system_calls-1,%eax
ja bad_sys_call
# 寄存器值保存
push %ds
push %es
push %fs
# 系统调用的参数传递
pushl %edx
pushl %ecx
pushl %ebx
# 使得ds、es指向内核空间的数据段
movl $0x10,%edx
mov %dx,%ds
mov %dx,%es
# 使得fs指向局部数据段
movl $0x17,%edx # fs points to local data space
mov %dx,%fs
# 函数调用,sys_call_table为一个指针数组
# sys_call_table(,%eax,4)为函数入口
# sys_call_table(,%eax,4) = sys_call_table + %eax*4
call sys_call_table(,%eax,4)
# 将系统调用返回值入栈
pushl %eax
# 检查当前进程的时间片是否已经用完,未用完则继续执行,否则进行进程切换
movl current,%eax
cmpl $0,state(%eax) # state
jne reschedule
cmpl $0,counter(%eax) # counter
je reschedule
# 判断是否为task0,如果是,则不进行信号处理
ret_from_sys_call:
movl current,%eax # task[0] cannot have signals
cmpl task,%eax
je 3f
# 检查进程是否为用户任务,若不是则直接退出,因为任务在内核态执行时不可抢占
cmpw $0x0f,CS(%esp) # was old code segment supervisor ?
jne 3f
cmpw $0x17,OLDSS(%esp) # was stack segment = 0x17 ?
jne 3f
# 下面的代码用于处理当前任务的信号
movl signal(%eax),%ebx
movl blocked(%eax),%ecx
notl %ecx
andl %ebx,%ecx
bsfl %ecx,%ecx
je 3f
btrl %ecx,%ebx
movl %ebx,signal(%eax)
incl %ecx
pushl %ecx
call do_signal
popl %eax
3: popl %eax
popl %ebx
popl %ecx
popl %edx
pop %fs
pop %es
pop %ds
iret
# 外部的基于硬件的异常,当协处理器检测到自己发生错误时,就会通过ERROR引脚通知CPU
.align 2
coprocessor_error:
push %ds
push %es
push %fs
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax
mov %ax,%fs
pushl $ret_from_sys_call
jmp math_error
# 设备不存在的中断处理
.align 2
device_not_available:
push %ds
push %es
push %fs
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax
mov %ax,%fs
pushl $ret_from_sys_call
clts # clear TS so that we can use math
movl %cr0,%eax
testl $0x4,%eax # EM (math emulation bit)
je math_state_restore
pushl %ebp
pushl %esi
pushl %edi
call math_emulate
popl %edi
popl %esi
popl %ebp
ret
# 时钟中断处理
.align 2
timer_interrupt:
push %ds # save ds,es and put kernel data space
push %es # into them. %fs is used by _system_call
push %fs
pushl %edx # we save %eax,%ecx,%edx as gcc doesn't
pushl %ecx # save those across function calls. %ebx
pushl %ebx # is saved as we use that in ret_sys_call
pushl %eax
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax
mov %ax,%fs
incl jiffies
movb $0x20,%al # EOI to interrupt controller #1
outb %al,$0x20
movl CS(%esp),%eax
andl $3,%eax # %eax is CPL (0 or 3, 0=supervisor)
pushl %eax
call do_timer # 'do_timer(long CPL)' does everything from
addl $4,%esp # task switching to accounting ...
jmp ret_from_sys_call
# sys_execve系统调用
.align 2
sys_execve:
lea EIP(%esp),%eax
pushl %eax
call do_execve
addl $4,%esp
ret
# sys_fork系统调用
.align 2
sys_fork:
call find_empty_process
testl %eax,%eax
js 1f
push %gs
pushl %esi
pushl %edi
pushl %ebp
pushl %eax
call copy_process
addl $20,%esp
1: ret
# 硬盘中断处理程序
hd_interrupt:
pushl %eax
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax
mov %ax,%fs
movb $0x20,%al
outb %al,$0xA0 # EOI to interrupt controller #1
jmp 1f # give port chance to breathe
1: jmp 1f
1: xorl %edx,%edx
xchgl do_hd,%edx
testl %edx,%edx
jne 1f
movl $unexpected_hd_interrupt,%edx
1: outb %al,$0x20
call *%edx # "interesting" way of handling intr.
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
# 软盘驱动器中断处理程序
floppy_interrupt:
pushl %eax
pushl %ecx
pushl %edx
push %ds
push %es
push %fs
movl $0x10,%eax
mov %ax,%ds
mov %ax,%es
movl $0x17,%eax
mov %ax,%fs
movb $0x20,%al
outb %al,$0x20 # EOI to interrupt controller #1
xorl %eax,%eax
xchgl do_floppy,%eax
testl %eax,%eax
jne 1f
movl $unexpected_floppy_interrupt,%eax
1: call *%eax # "interesting" way of handling intr.
pop %fs
pop %es
pop %ds
popl %edx
popl %ecx
popl %eax
iret
# 并行口中断处理程序
parallel_interrupt:
pushl %eax
movb $0x20,%al
outb %al,$0x20
popl %eax
iret