Linux内核系统调用原理

本文深入解析了X86架构下Linux系统调用的实现机制,包括寄存器的作用、0x80中断的功能及系统调用的基本原理。详细介绍了%eax寄存器用于传递系统调用号,%ebx、%ecx、%edx等寄存器用于传递参数,以及0x80中断触发内核中的system_call函数,调用sys_call_table中对应的系统调用。

最近在研究X86汇编,经过研究发现在Linux中使用汇编调用诸如read/write/open/close etc的系统调用都是如下格式:

# this is for testing write
.section .data
output:
        .asciz "the test string\n"
output_len:
        .int .-output

.section .text
.globl main
main:
        movl $4, %eax
        movl $1, %ebx
        movl $output, %ecx
        movl output_len, %edx
        int $0x80

        pushl $0
        call exit

以上格式说明:
1. %eax中传递的是系统调用号。
2. %ebx、%ecx、%edx…依次为传递的是系统调用的参数。
3. 使用系统调用时候开启0x80中断。

根据上面的格式说明抛出如下疑问:

1. 各个寄存器在系统调用的作用。
2. 0x80中断在linux系统调用时的作用是什么?
3. 系统调用的原理是什么?

1.各个寄存器在系统调用的作用

在X86系统中有 %eax, %ebx, %ecx, %edx, %esi, %edi, %esp, %eip, %eflags, %crx寄存器等,所谓寄存器就是硬件中CPU可以直接运行并且可以操作的数字,寄存器在运算中的作用就是通过寄存器可以操作内存中的地址, 它的重要性在内存之上。
系统运行时候只有各个寄存器协调作用CPU才算一个完整的CPU,才能够发挥作用。

1.1 %eax寄存器的作用

eax寄存器的作用就是传递给内核正确的系统调用号,并且内核识别到正确的系统调用号之后才能够正确的调用诸如read、write、open、close之类的系统调用

1.2 其余寄存器的作用

除了eax寄存器之外的其余寄存器我们叫做其它寄存器,比如:ebx、ecx、edx等等等
他们的作用是给在1.1节中的传入eax寄存器中的系统调用号传递参数。
比如在文章开始的时候写的一个例子:
ebx传递数字1,代表传递给write的第一个参数为1,标准输出
ecx传递$output地址,write的第二个参数,为一个指针,在本处要显示输出的地址。
edx要写入的字符长度。

2. 0x80中断在Linux中系统调用的作用

int 0x80中断在CPU硬件层次手动触发一次中断,本中断为软中断。中断原理如下:在中断触发的时候,根据中断向量表中的地址判断中断要执行什么代码。
所谓的中断向量表就是我们的内核在Linux系统启动的时候分配的一段内存,内存的每四个字节代表触发一个中断系统要执行什么操作,例如触发0x80中断,CPU就会自动执行这段地址+0x80*4处的一个函数调用(system_call)。
也就是每触发一次0x80中断执行最先执行的就是system_call内核调用,system_call函数原型为一段汇编如下:

ENTRY(system_call)
        RING0_INT_FRAME                 # can't unwind into user space anyway
        pushl %eax                      # save orig_eax
        CFI_ADJUST_CFA_OFFSET 4
        SAVE_ALL
        GET_THREAD_INFO(%ebp)
                                        # system call tracing in operation / emulation
        testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
        jnz syscall_trace_entry
        cmpl $(nr_syscalls), %eax
        jae syscall_badsys
syscall_call:
        call *sys_call_table(,%eax,4)
        movl %eax,PT_EAX(%esp)          # store the return value
syscall_exit:
        LOCKDEP_SYS_EXIT
        DISABLE_INTERRUPTS(CLBR_ANY)    # make sure we don't miss an interrupt
                                        # setting need_resched or sigpending
                                        # between sampling and the iret
        TRACE_IRQS_OFF
        movl TI_flags(%ebp), %ecx
        testl $_TIF_ALLWORK_MASK, %ecx  # current->work
        jne syscall_exit_work

以上汇编函数的作用:

  1. 保存中断发生时候各个寄存器的值。
  2. 调用相关的系统调用,系统调用的所有函数存放在sys_call_table全局数组中。这个数组存放的是sys_write,sys_read, sys_open, sys_close等的系统调用的地址。
    ps:搜索这些地址只有原型没有定义,因为这些系统调用大部分是由宏SYSCALL_DEFINE定义,故而没有定义,例如搜索sys_write时候应该查找SYSCALL_DEFINE中的定义,这些才是函数原型。

3. 系统调用的基本原理

通过以上两节的系统调用的分析我们可以把系统调用的阶段分为如下三个阶段:

1. 系统启动阶段。
2. 系统调用system_call阶段
3. system_call返回阶段

其中system_call返回阶段暂且不赘述。

3.1系统启动阶段

在系统启动阶段Linux内核在内核中分配了系统调用的函数地址,并且把这个函数地址放入:sys_call_table中的全局数组中,以便系统触发0x80中断时候找到各个系统调用的地址。

3.2 系统调用system_call阶段

系统调用system_call 阶段就是保存中断发生时的寄存器状态和寄存器值,并且调用sys_call_table中保存的相应的(%eax*4的偏移)函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值