1、系统调用初始化
在系统启动时,会在sched_init(void)函数中调用set_system_gate(0x80,&system_call),设置中断向量号0x80的中断描述符:
#define set_system_gate(n,addr) _set_gate(&idt[n],15,3,addr)
其中15表示此中断号对应的是陷阱门,注意,这个中断向量不是中断门描述符。比如硬盘中断(hd_interrupt)或定时器中断(timer_interrupt)等硬件类的中断才设置为中断门描述符。陷阱门是可被中断的。关于陷阱门与中断门的区别,及陷阱门中DPL为0或3原因,请参考书本《linux 0.12》的14.19的解释,还有第四章部分内容,这些已经解释的很清楚了。
Int 0x80的输入输出参数说明:
输入参数:eax=功能号(比如2为fork系统调用)
用功能对应sys_call_table[]的下标,比如sys_call_table[2]表示fork系统调用函数。
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
返回值:EAX=sys_fork函数的返回值
2、system_call的实现
当调用_system_call函数时,系统就从用户态进入了内核态。注意,特权变化了!对于x86系统,因为所有的寄存器都只有一个物理寄存器(ARM就不一要样了),因为内核态与用户态共享所有寄存器(段、通用、栈寄存器),比如SS、ESP、eflags、CS、EIP这五个寄存器。为了能够从内核态返回到调用处继续执行,当前现场,即相关寄存器的内容都需要被保存起来。
那么,这些现场信息保存到那里呢?为什么不能保存到用户态堆栈,如果保存了用户态堆栈,那么,这些栈内存区域,用户程序就可以必改定,那么,程序就很容易被攻击了,直接修改CS:EIP对应的栈内存,那么,你懂的^_^。
现场信息是保存在当前进程的内核态堆栈中,由于已经进入了内核态,在_system_call函数中执行push操作,此时被push的数据是存储也就在内核栈中。SS、ESP、eflags、CS、EIP这五个寄存器的内容是怎么进入内核栈的呢?是程序从用户态进入了内核态时,即在执行_system_call函数中的指令之前,硬件已经自动把SS、ESP、eflags、CS、EIP五个寄存器压入了内核栈,然后,根据函数的需要,再保存相关通用、段之类寄存器。当从内核返回用户态时,这五个寄存器会自动从内核栈中恢复。
_system_call部分代码分析:
push %ds
push %es
push %fs
pushl %eax # save the orig_eax
pushl %edx
pushl %ecx # push %ebx,%ecx,%edx asparameters
pushl %ebx # to the system call
movl $0x10,%edx // ds、es此时指向当前进程的内核态数据段
mov %dx,%ds
mov %dx,%es
movl $0x17,%edx //即使没这二行也行吧,fs本来就指向当前进程的用户态数据段
mov %dx,%fs //因为在fork进程时,fs已经在copy_process函数中设置了。
call_sys_call_table(,%eax,4) //根据EAX传入的功能号,即可调用相关系统函数
pushl %eax //系统调用函数的返回值入栈
关于进程状态的变化,参考书上的说明,这部分,理解的还不够,后续再分析???
当在中断处理函数(陷阱门)中执行时,是可被中断(中断门)的,因为eflags标志中的TF被设置为允许中断的。因而有可能在时钟中断函数(do_timer)中,本进程的时间片可能被修改为0。所以,当从系统调用相关功能号对应函数返回时,需要检查当前进程是否还在就绪态,或时间片是否用完,并确认是否需要重新执行调度程序。
2: movl _current,%eax
cmpl $0,state(%eax) # state
jne reschedule
cmpl $0,counter(%eax) # counter
je reschedule
//系统调用返回时,会处理当前任务的信号,进程的信号识别与信号处理,仅在系统调用或时钟中断(每10ms)返回时。就能处理信号,优先级还是蛮高的,至少在进程执行流中,到少每10m就能处理信号。
ret_from_sys_call:
movl _current,%eax
cmpl _task,%eax # task[0] cannot havesignals
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
movlblocked(%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 %ecx
testl %eax, %eax
jne 2b # see if we need toswitchtasks, or do more signals
3: popl %eax
popl %ebx
popl %ecx
popl %edx
addl $4, %esp # skip orig_eax
pop %fs
pop %es
pop %ds
iret //此指令会将内核栈中的数据弹出到这5个寄存器SS、ESP、eflags、CS、EIP。
from:http://www.voidcn.com/blog/maowenl/article/p-1845345.html
最新评论