system_call的汇编代码分析

张建帮 原创作品转载请注明出处 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

关于linux系统调用的具体流程,这一篇文章其实讲得比较清楚:链接在这里 。但是它只叙述了整体的过程,没有深入下去,因此这里我们将以 getpid() 系统调用为例,分析其中具体的函数调用的实现。

这里我们放上之前文章的系统调用过程的图片:

system_call的汇编代码分析_第1张图片

库函数中的 getpid() 调用 int $0x80 指令后,系统就从用户态切换到内核态,并跳转到了 0x80 中断向量号所对应的中断向量服务程序 system_call,该服务程序的代码位置是/linux-3.18.6/arch/x86/kernel/entry_32.S 文件中的 490 行处的 ENTRY(system_call)处。由于这部分的汇编代码涉及到比较多的知识,所以下面的代码是作了一些精简,只保留最核心的那部分代码:(引用自这里)

/* 代码文件路径:/linux-3.18.6/arch/x86/kernel/entry_32.S */
# system call handler stub
# 系统调用汇编代码的起点
ENTRY(system_call)
    # step 1: 
    SAVE_ALL                # 进入中断处理程序前,保存内核态下的现场
    ......
    cmpl $(NR_syscalls), %eax # 比较传入的系统调用号是否大于最大的系统调用号
    jae syscall_badsys  # 如果传入的系统调用号太大,则跳转到处理无效系统调用号的处理程序
# step 2: 
syscall_call:
    call *sys_call_table(,%eax,4) # 根据 eax 中传入的系统调用号来调用对应的系统调用服务程序,在我们的例子中就是调用 sys_getpid,4表示的是sys_call_table中每个元素的大小是4个字节
# step 3: 
syscall_after_call:
    movl %eax,PT_EAX(%esp)      # store the return value, 保存系统调用后返回的值到 eax 中
# step 4: 
syscall_exit: # 会检测要不要执行syscall_exit_work,正常情况会有一些需要处理的工作,比如当前进程有一些信号要处理,系统需要进行调度
    ......
    movl TI_flags(%ebp), %ecx
    testl $_TIF_ALLWORK_MASK, %ecx  # current->work
    jne syscall_exit_work
# step 5: 
restore_all:
restore_nocheck:
    RESTORE_REGS 4          # 恢复系统调用前的上下文
irq_return:
    INTERRUPT_RETURN    # 这是一个宏定义,本质上是一条 iret 指令
# system_call 结束位置
......
ENDPROC(system_call)

由于代码比较长,所以我们在上面的代码中加入了 “step n” 的注释,将代码分成 5 个步骤进行解析。下面通过一个流程图对内核源码中系统调用的处理过程进行一个概要性地描述。

system_call的汇编代码分析_第2张图片

引用自这里

从上图中我们可以看到:系统调用完成后并不是立刻返回到系统中断处理程序,而是首先要判断在执行系统调用函数的函数中,有没有信号要处理,要不要进行调度等,这些都处理完成后,才返回到上一级的的中断处理程序中。

你可能感兴趣的:(Linux内核分析)