Linux内核分析(五)
2016-03-22 18:34
465 查看
Linux 内核分析——【实验五:系统调用运行机制】
前文提到系统调用是通过int 0x80来产生的,所以从本质上来说它是一种中断。那么什么是中断呢?中断被定义为一个事件,该事件改变了处理器执行的指令顺序。在linux系统下设置了256个中断,每个中断由0~255之间的数来标识,系统调用对应的就是0x80。
首先,我们总结一下系统调用的执行过程:
1、程序调用libc库中封装的系统调用函数。
2、调用中断int 0x80 陷入内核。
3、在内核中执行system_call函数(实际上是一段汇编代码),将系统调用号(eax)和可以所有相关寄存器保存到内核堆栈中(由SAVE_ALL完成),然后根据系统调用号在系统调用表中查找到对应的系统调用服务例程。
4、执行该服务例程。
5、执行完毕后,转入ret_from_sys_call 例程,从系统调用返回
接着,我们通过一个简单的例子,来了解系统调用函数在进入system_call到iret的执行过程。代码如下:
运行qemu并使用gdb进行调试,步骤如下:
将断点设在system_call位置,可以看到system_call在一个entry_32.S的汇编文件中。继续执行,但gdb并没有在system_call的位置停止,而是直接运行结束了。
下面,我们直接打开entry_32.S文件,找到system_call的位置进行分析:
系统调用函数的运行流程如下:
注意:系统调用的初始化工作,是在内核启动之初/linux-3.18.6/init/main.c文件中
start_kernel() —> trap_init() —> set_system_trap_gate(SYSCALL_VECTOR, &system_call);
附录:
查看系统调用号:linux-3.18.6/arch/x86/syscalls/syscall_32.tbl
=========== 王杰 原创作品转载请注明出处==============
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
前文提到系统调用是通过int 0x80来产生的,所以从本质上来说它是一种中断。那么什么是中断呢?中断被定义为一个事件,该事件改变了处理器执行的指令顺序。在linux系统下设置了256个中断,每个中断由0~255之间的数来标识,系统调用对应的就是0x80。
首先,我们总结一下系统调用的执行过程:
1、程序调用libc库中封装的系统调用函数。
2、调用中断int 0x80 陷入内核。
3、在内核中执行system_call函数(实际上是一段汇编代码),将系统调用号(eax)和可以所有相关寄存器保存到内核堆栈中(由SAVE_ALL完成),然后根据系统调用号在系统调用表中查找到对应的系统调用服务例程。
4、执行该服务例程。
5、执行完毕后,转入ret_from_sys_call 例程,从系统调用返回
接着,我们通过一个简单的例子,来了解系统调用函数在进入system_call到iret的执行过程。代码如下:
运行qemu并使用gdb进行调试,步骤如下:
将断点设在system_call位置,可以看到system_call在一个entry_32.S的汇编文件中。继续执行,但gdb并没有在system_call的位置停止,而是直接运行结束了。
下面,我们直接打开entry_32.S文件,找到system_call的位置进行分析:
# system call handler stub ENTRY(system_call) #系统调用处理入口(内核态) RING0_INT_FRAME # can't unwind into user space anyway ASM_CLAC pushl_cfi %eax # save orig_eax #保存eax,也就是调用号 SAVE_ALL # 保存寄存器 GET_THREAD_INFO(%ebp) # 获取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) # 在系统调用表中的调用相应的服务例程,eax为调用号,4字节对齐 syscall_after_call: 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 syscall_exit_work: testl $_TIF_WORK_SYSCALL_EXIT, %ecx jz work_pending # 测试是否退出前还有工作要处理,如果有的话跳转到work_pending TRACE_IRQS_ON # 开启系统中断跟踪 ENABLE_INTERRUPTS(CLBR_ANY) # could let syscall_trace_leave() call # 允许中断 # schedule() instead movl %esp, %eax call syscall_trace_leave jmp resume_userspace # 恢复用户空间 END(syscall_exit_work) work_pending: testb $_TIF_NEED_RESCHED, %cl # 是否有需要继续调度的相关信号 jz work_notifysig # 跳转到处理信号相关的代码处 work_resched: call schedule # 时间调度, 进程调度的时机在这里处理 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 andl $_TIF_WORK_MASK, %ecx # is there any work to be done other 是否还有其他工作要处理 # than syscall tracing? jz restore_all #如果没有的话就恢复中断上下文,也就是恢复进入之前保存的寄存器相关内容 testb $_TIF_NEED_RESCHED, %cl jnz work_resched work_notifysig: # deal with pending signals and # notify-resume requests #ifdef CONFIG_VM86 testl $X86_EFLAGS_VM, PT_EFLAGS(%esp) movl %esp, %eax jne work_notifysig_v86 # returning to kernel-space or # vm86-space restore_all: TRACE_IRQS_IRET # 恢复中断跟踪 restore_all_notrace: #ifdef CONFIG_X86_ESPFIX32 movl PT_EFLAGS(%esp), %eax # mix EFLAGS, SS and CS # Warning: PT_OLDSS(%esp) contains the wrong/random values if we # are returning to the kernel. # See comments in process.c:copy_thread() for details. movb PT_OLDSS(%esp), %ah movb PT_CS(%esp), %al andl $(X86_EFLAGS_VM | (SEGMENT_TI_MASK << 8) | SEGMENT_RPL_MASK), %eax cmpl $((SEGMENT_LDT << 8) | USER_RPL), %eax CFI_REMEMBER_STATE je ldt_ss # returning to user-space with LDT SS #endif restore_nocheck: RESTORE_REGS 4 # skip orig_eax/error_code irq_return: INTERRUPT_RETURN
系统调用函数的运行流程如下:
注意:系统调用的初始化工作,是在内核启动之初/linux-3.18.6/init/main.c文件中
start_kernel() —> trap_init() —> set_system_trap_gate(SYSCALL_VECTOR, &system_call);
附录:
查看系统调用号:linux-3.18.6/arch/x86/syscalls/syscall_32.tbl
# 32-bit system call numbers and entry vectors # # The format is: # <number> <abi> <name> <entry point> <compat entry point> # # The abi is always "i386" for this file. # 0 i386 restart_syscall sys_restart_syscall 1 i386 exit sys_exit 2 i386 fork sys_fork stub32_fork 3 i386 read sys_read 4 i386 write sys_write 5 i386 open sys_open compat_sys_open 6 i386 close sys_close 7 i386 waitpid sys_waitpid sys32_waitpid 8 i386 creat sys_creat 9 i386 link sys_link 10 i386 unlink sys_unlink 11 i386 execve sys_execve stub32_execve 12 i386 chdir sys_chdir 13 i386 time sys_time compat_sys_time 14 i386 mknod sys_mknod 15 i386 chmod sys_chmod 16 i386 lchown sys_lchown16 17 i386 break 18 i386 oldstat sys_stat 19 i386 lseek sys_lseek compat_sys_lseek 20 i386 getpid sys_getpid 21 i386 mount sys_mount compat_sys_mount 22 i386 umount sys_oldumount 23 i386 setuid sys_setuid16 24 i386 getuid sys_getuid16 25 i386 stime sys_stime compat_sys_stime 26 i386 ptrace sys_ptrace compat_sys_ptrace 27 i386 alarm sys_alarm 28 i386 oldfstat sys_fstat 29 i386 pause sys_pause 30 i386 utime sys_utime compat_sys_utime 31 i386 stty 32 i386 gtty 33 i386 access sys_access 34 i386 nice sys_nice 35 i386 ftime 36 i386 sync sys_sync 37 i386 kill sys_kill 38 i386 rename sys_rename 39 i386 mkdir sys_mkdir 40 i386 rmdir sys_rmdir 41 i386 dup sys_dup 42 i386 pipe sys_pipe 43 i386 times sys_times compat_sys_times 44 i386 prof 45 i386 brk sys_brk 46 i386 setgid sys_setgid16 47 i386 getgid sys_getgid16 48 i386 signal sys_signal 49 i386 geteuid sys_geteuid16 50 i386 getegid sys_getegid16 51 i386 acct sys_acct 52 i386 umount2 sys_umount 53 i386 lock 54 i386 ioctl sys_ioctl compat_sys_ioctl 55 i386 fcntl sys_fcntl compat_sys_fcntl64 56 i386 mpx 57 i386 setpgid sys_setpgid 58 i386 ulimit 59 i386 oldolduname sys_olduname 60 i386 umask sys_umask 61 i386 chroot sys_chroot 62 i386 ustat sys_ustat compat_sys_ustat 63 i386 dup2 sys_dup2 64 i386 getppid sys_getppid 65 i386 getpgrp sys_getpgrp 66 i386 setsid sys_setsid 67 i386 sigaction ... ...
=========== 王杰 原创作品转载请注明出处==============
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
相关文章推荐
- Linux Kernel 4.0 RC5 发布!
- 基于Linux调试工具strace与gdb的常用命令总结
- gdb调试命令的使用及总结
- 如何使用GDB调试PHP程序
- Linux C中库函数与系统调用的区别详细解析
- 三种方法实现Linux系统调用
- MAC下安装gdb
- 使用gdb调试python
- 调试的艺术
- Vim + Gdb 程序的完美集合
- GDB的使用,重点讲解图像化 gdb -tui 方式
- linux偶发性崩溃的程序该怎么调试 coredump gdb
- 使用gdb在Android Emulator中进行调试
- android中使用gdbserver调试c程序
- GDB + gdbserver 远程调试android native code
- Android下用gdb远程调试
- 【C/C++】Linux下使用system()函数一定要谨慎
- GDB调试命令
- GDB中应该知道的几个调试方法