Linux内核分析(五)
来源:互联网 发布:ssm项目源码和视频讲解 编辑:程序博客网 时间:2024/06/10 11:41
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的位置进行分析:
# system call handler stubENTRY(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_worksyscall_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_reschedwork_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-spacerestore_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#endifrestore_nocheck: RESTORE_REGS 4 # skip orig_eax/error_codeirq_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_syscall1 i386 exit sys_exit2 i386 fork sys_fork stub32_fork3 i386 read sys_read4 i386 write sys_write5 i386 open sys_open compat_sys_open6 i386 close sys_close7 i386 waitpid sys_waitpid sys32_waitpid8 i386 creat sys_creat9 i386 link sys_link10 i386 unlink sys_unlink11 i386 execve sys_execve stub32_execve12 i386 chdir sys_chdir13 i386 time sys_time compat_sys_time14 i386 mknod sys_mknod15 i386 chmod sys_chmod16 i386 lchown sys_lchown1617 i386 break18 i386 oldstat sys_stat19 i386 lseek sys_lseek compat_sys_lseek20 i386 getpid sys_getpid21 i386 mount sys_mount compat_sys_mount22 i386 umount sys_oldumount23 i386 setuid sys_setuid1624 i386 getuid sys_getuid1625 i386 stime sys_stime compat_sys_stime26 i386 ptrace sys_ptrace compat_sys_ptrace27 i386 alarm sys_alarm28 i386 oldfstat sys_fstat29 i386 pause sys_pause30 i386 utime sys_utime compat_sys_utime31 i386 stty32 i386 gtty33 i386 access sys_access34 i386 nice sys_nice35 i386 ftime36 i386 sync sys_sync37 i386 kill sys_kill38 i386 rename sys_rename39 i386 mkdir sys_mkdir40 i386 rmdir sys_rmdir41 i386 dup sys_dup42 i386 pipe sys_pipe43 i386 times sys_times compat_sys_times44 i386 prof45 i386 brk sys_brk46 i386 setgid sys_setgid1647 i386 getgid sys_getgid1648 i386 signal sys_signal49 i386 geteuid sys_geteuid1650 i386 getegid sys_getegid1651 i386 acct sys_acct52 i386 umount2 sys_umount53 i386 lock54 i386 ioctl sys_ioctl compat_sys_ioctl55 i386 fcntl sys_fcntl compat_sys_fcntl6456 i386 mpx57 i386 setpgid sys_setpgid58 i386 ulimit59 i386 oldolduname sys_olduname60 i386 umask sys_umask61 i386 chroot sys_chroot62 i386 ustat sys_ustat compat_sys_ustat63 i386 dup2 sys_dup264 i386 getppid sys_getppid65 i386 getpgrp sys_getpgrp66 i386 setsid sys_setsid67 i386 sigaction ... ...
=========== 王杰 原创作品转载请注明出处==============
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
- Linux内核分析(五)
- Linux内核分析(五)
- Linux 内核分析五
- Linux内核分析(五)之有始有终
- Linux内核分析:实验五
- Linux内核分析:实验五
- Linux内核分析实验五
- Linux内核分析(五):系统调用深入分析
- Linux内核分析(五)----字符设备驱动实现
- Linux内核源码分析--文件系统(五、Inode.c)
- Linux内核分析(五)系统调用过程解析
- 网易云课堂 Linux内核分析(五)
- Linux kernel 分析之五:内核启动-内核解压缩
- Linux内核多线程(五)
- Linux内核详解(五)
- Linux内核分析之五——分析系统调用(system_call)的执行机制
- [网易云课堂]Linux内核分析(五)—— 分析system_call中断处理过程
- linux内核list.h头文件分析(五)——hlist分析
- IOS沙盒机制
- 上拉加载的时候 添加数据
- javax.servlet.ServletException: java.lang.NoClassDefFoundError: count/Counter即找不到JavaBean文件
- java.io.File源码探索
- do...while(0)的妙用====>忍不住转载了
- Linux内核分析(五)
- 多态以及类的结构(简介)
- 如何搭建简易的Wiki系统
- 让EditText不能自动获取焦点
- js如何判断一个对象{}是否为空对象,没有任何属性
- Linux后台网络编程中select/poll/epoll的比较分析
- visual studio 2012 配置opencv3.0.0
- 编写windows服务及其安装方法
- JAVA的字节流和字符流简介