分析system_call中断处理过程

最第一版本的MenuOS只支持version和help命令,显然这并不能知足咱们的需求。咱们如今来为它添加一个fork命令和fork-asm命令,其做用是测试fork的系统调用。linux

要增长一个命令也并不难,只须要~/LinuxKernel/linux-3.18.6/menu/test.c里的main函数中添加下面一行,而后添加它的实现(须要定义在main函数前面)就能够了。shell

MenuConfig("fork","Test system call fork",Fork);

最后一个参数Fork是一个函数指针,也就是咱们对它的定义:函数

int Fork(int argc, char *argv[])
{
    pid_t fpid;
    int count = 0;
    fpid = fork();
    printf("Now pid = %d\n", fpid);
    if(fpid < 0)
        printf("Error in fork!");
    else if(fpid == 0){
        printf("I am the child process, my process id is: %d\n", getpid());
        count++;
    }
    else{
        printf("I am the parent process, my process id is: %d\n", getpid());
        count++;
    }
    printf("Now count = %d\n", count);
    return 0;
}

一样的方法,也能够添加ForkAsm函数与命令(在个人上一篇博文《Linux下嵌入汇编代码调用API  using fork()》中就有Fork()与ForkAsm()的实现,只要改下函数名就行了)。测试

如今,咱们来经过gdb调试一下咱们刚刚添加的命令从调用到运行结束的过程:spa

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S

打开另一个终端,启动gdb,咱们来连接内核并添加断点:指针

(gdb) file linux-3.18.6/vmlinux # 在gdb界面中targe remote以前加载符号表
(gdb) target remote:1234 # 创建gdb和gdbserver之间的链接,按c让qemu上的Linux继续运行
(gdb) break start_kernel # 断点的设置能够在target remote以前,也能够在以后
(gdb) break sys_fork     # sys_fork是fork的系统调用入口

如今咱们按c让内核启动,启动完成后咱们在help里看一看fork和fork-asm有没有被加进去:1调试

能够看到,如今这两个命令应该均可以用了,咱们来试一试fork-asm:2rest

能够看到,系统调用sys_fork()时就到了咱们设置的断点,咱们来继续单步调试:3code

接下来咱们分析一下system_call的具体调用过程,详细代码见/kernel/entry_32.Sorm

这段汇编代码较为复杂,仍是来看一下简化版本的吧:

.macro INTERRUPT_RETURN  ; 中断返回
    iret
.endm
.macro SAVE_ALL          ; 保护现场
    ...
.macro RESTORE_INT_REGS
    ...
.endm
 
ENTRY(system_call)
    SAVE_ALL
syscall_call:
    call *sys_call_table(,%eax,4)
    movl %eax, PT_EAX(%esp)  ; store the return value
syscall exit:
    testl $_TIF_ALLWORK_MASK, %ecx # current->work
    jne syscall_exit_work
restore_all:
    RESTORE_INT_REGS
irq_return:
    INTERRUPT_RETURN      ; 到这里就算执行完了
ENDPROC(system_call)
 
syscall_exit_work:
    testl  $_TIF_WORK_SYSCALL_EXIT, %ecx
    jz work_pending
END(syscall_exit_work)
 
work_pending:
    testb $_TIF_NEED_RESCHED, %cl
    jz work_notifysig
work_resched:
    call schedule
    jz restore_all
work_notifysig:
    ...                  ; deal with pending signals
END(work_pending)

画了一个流程图,见笑见笑~


总结:在系统调用结束返回(iret)以前,可能再次进行系统调度(call_schedule),调度过程当中还可能发生进程上下文与中断上下文之间的切换。系统完成这一次调用后,会继续检查任务队列,以后才执行iret返回。

陈政/arc001    原创做品转载请注明出处  《Linux内核分析》MOOC课程

相关文章
相关标签/搜索