linux0.11系统调用的执行过程是怎样的? (1) 答:当执行系统调用函数时,系统调用函数会执行int 0x80中断命令,同时将系统调用号放入eax寄存器中,并将要传递给系统的参数放入ebx,ecx,edx中。中断处理程序会执行system_call()函数。 (2) system_call()函数首先保存原段寄存器,在将调用参数压入栈中。然后将ds(在保护模式下,ds装的是段选择符)、es指向内核数据段,cs段会在中断产生时由中断门的段选择符赋值为内核代码段,并将原段选择符保存到栈中。然后调用对应的功能函数。当从功能函数返回时,内核会查看当前任务运行状态,如果不在就绪态就去执行调度程序。如果在就绪态,但其时间片用完,则也去执行调度程序。当任务继续执行时则继续对信号进行处理,然后退回到系统调用函数。 代码大概如下 #### int 0x80 --linux 系统调用入口点(调用中断int 0x80,eax 中是调用号)。 .align 2 _system_call: cmpl $nr_system_calls-1,%eax # 调用号如果超出范围的话就在eax 中置-1 并退出。 ja bad_sys_call 5.5 system_call.s 程序 push %ds # 保存原段寄存器值。 push %es push %fs pushl %edx # ebx,ecx,edx 中放着系统调用相应的C 语言函数的调用参数。 pushl %ecx # push %ebx,%ecx,%edx as parameters pushl %ebx # to the system call movl $0x10,%edx # set up ds,es to kernel space mov %dx,%ds # ds,es 指向内核数据段(全局描述符表中数据段描述符)。 mov %dx,%es movl $0x17,%edx # fs points to local data space mov %dx,%fs # fs 指向局部数据段(局部描述符表中数据段描述符)。 # 下面这句操作数的含义是:调用地址 = _sys_call_table + %eax * 4。参见列表后的说明。 # 对应的C 程序中的sys_call_table 在include/linux/sys.h 中,其中定义了一个包括72 个 # 系统调用C 处理函数的地址数组表。 call _sys_call_table(,%eax,4) pushl %eax # 把系统调用号入栈。 movl _current,%eax # 取当前任务(进程)数据结构地址??eax。 # 下面97-100 行查看当前任务的运行状态。如果不在就绪状态(state 不等于0)就去执行调度程序。 # 如果该任务在就绪状态但counter[??]值等于0,则也去执行调度程序。 cmpl $0,state(%eax) # state jne reschedule cmpl $0,counter(%eax) # counter je reschedule # 以下这段代码执行从系统调用C 函数返回后,对信号量进行识别处理。 ret_from_sys_call: linux0.11 中系统调用中断0x80在哪儿初始化的? // 设置系统调用中断门。 set_system_gate (0x80, &system_call); #define _set_gate(gate_addr,type,dpl,addr) / __asm__ ( "movw %%dx,%%ax/n/t" / // 将偏移地址低字与选择符组合成描述符低4 字节(eax)。 "movw %0,%%dx/n/t" / // 将类型标志字与偏移高字组合成描述符高4 字节(edx)。 "movl %%eax,%1/n/t" / // 分别设置门描述符的低4 字节和高4 字节。 "movl %%edx,%2": :"i" ((short) (0x8000 + (dpl << 13) + (type << 8))), "o" (*((char *) (gate_addr))), "o" (*(4 + (char *) (gate_addr))), "d" ((char *) (addr)), "a" (0x00080000)) #define set_system_gate(n,addr) / _set_gate(&idt[n],15,3,addr) 设置段描述符函数。 // 参数:gate_addr -描述符地址;type -描述符中类型域值;dpl -描述符特权层值; // base - 段的基地址;limit - 段限长。(参见段描述符的格式) |