Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >system_call到iret过程分析

system_call到iret过程分析

作者头像
De4dCr0w
发布于 2019-02-27 06:10:54
发布于 2019-02-27 06:10:54
1.1K00
代码可运行
举报
运行总次数:0
代码可运行

基础知识

中断的分类

(1)中断有两种,一种是由CPU外部硬件产生的,另一种是由CPU本身执行程序的过程中产生的;外部中断即我们所说的中断(interrupt),外部中断是异步的,由硬件产生,我们无法预测它什么时候发生; (2)x86软件产生的中断是由“INT n”同步产生的,由程序产生,只要CPU执行了一条INT指令,就知道在开始执行下一条指令前就会进入中断服务程序,我们又称此类中断为“陷阱”;int 80为系统调用的陷阱号; (3)异常,是被动的,如页面异常,除数为0的异常; 因此系统调用是中断中的陷阱的一种,系统调用只发生在用户空间,必然会发生用户栈和内核栈的切换。

中断的过程

  在linux内核启动过程中,start_kernel中trap_init()函数初始化了中断门,通过set_system_intr_gate->set_gate进行设置,通过write_idt_entry将中断信息写进中断描述符表IDT,中断描述符表(Interrupt Descriptor Table,IDT)是一个系统表,它与每一个中断或异常向量相联系,每一个向量在表中存放的是相应的中断或异常处理程序的入口地址,当处于实模式下时,IDT 被初始化并由 BIOS 程序所使用。然而,一旦 Linux 开始接管,IDT 就被移到另一个区域,并进行第二次初始化。    当中断发生时,通过中断描述符表IDT获取中断服务程序入口地址,调用相应的中断服务程序,而int 0x80的中断服务程序就是system_call

实验过程

修改test.c文件

将fork和fork-asm函数添加到test.c文件中,如下图:

运行效果如下:

system_call代码分析

system_call代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ENTRY(system_call)
	RING0_INT_FRAME			# can't unwind into user space anyway
	ASM_CLAC
	pushl_cfi %eax			# save orig_eax
	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)
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

下面我们看看SAVE_ALL执行了哪些操作,对fork系统调用一文中我们对linux-0.11内核版本的进行分析,了解到system_call会保存用户态堆栈的相关寄存器,下面就是对应的保存操作

SAVE_ALL代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.macro SAVE_ALL
	cld
	PUSH_GS
	pushl_cfi %fs
	/*CFI_REL_OFFSET fs, 0;*/
	pushl_cfi %es
	/*CFI_REL_OFFSET es, 0;*/
	pushl_cfi %ds
	/*CFI_REL_OFFSET ds, 0;*/
	pushl_cfi %eax
	CFI_REL_OFFSET eax, 0
	pushl_cfi %ebp
	CFI_REL_OFFSET ebp, 0
	pushl_cfi %edi
	CFI_REL_OFFSET edi, 0
	pushl_cfi %esi
	CFI_REL_OFFSET esi, 0
	pushl_cfi %edx
	CFI_REL_OFFSET edx, 0
	pushl_cfi %ecx
	CFI_REL_OFFSET ecx, 0
	pushl_cfi %ebx
	CFI_REL_OFFSET ebx, 0
	movl $(__USER_DS), %edx
	movl %edx, %ds                                                  
	movl %edx, %es
	movl $(__KERNEL_PERCPU), %edx
	movl %edx, %fs
	SET_KERNEL_GS %edx
.endm

  我们通过syscall_call进行系统调用(这部分已经在fork系统调用一文中阐述过了)后,在syscall_after_call中进行返回,返回的结果保存在eax寄存器中。然后顺序执行到syscall_exit,这部分首先关闭中断,保证不被其它中断和信号打扰。然后判断是否响应其它中断或信号,如果所有标志都没设置,就直接restore_all,恢复原来进程的执行,如果有的话就进入syscall_exit_work。然后判断是否还有任务,如果有就跳转到work_pending。

work_pending代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
        ...
	jz restore_all

  在work_pending中先判断NEED_RESCHED位,如果置位了就执行work_resched段代码,被动调度当前进程,调度完还会继续判断是否还有任务,是否还有调度进程,这里是一个循环处理,直到判断没置位,就继续处理当前进程未处理的信号,最后会跳转到resume_userspace,恢复到用户态。

resume_userspace

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ENTRY(resume_userspace)
	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 on
					# int/exception return?
	jne work_pending
	jmp restore_all
END(ret_from_exception)

  在系统调用或中断,异常返回到用户态之前内核都会检查是否有信号在当前进程中挂起,然后转而去处理这些信号。

具体的从system_call开始到iret结束之间的整个过程如下图:

参考资料: http://www.2cto.com/os/201404/292864.html http://blog.csdn.net/yaozhenguo2006/article/details/7313956 理解系统调用的原理(二)

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
《Linux内核分析》之分析system_call中断处理过程实验总结
先占个位置,在实验楼做实验,刚做完一半忘了延续时间,结果之前写的代码神马的全没了。让我先去角落哭会,总结明天再写。2015-04-04
WindCoder
2018/09/20
1.6K0
《Linux内核分析》之分析system_call中断处理过程实验总结
Linux内核之旅/张凯捷——系统调用分析(1)
In computing, a system call is the programmatic way in which a computer program requests a service from the kernel of the operating system it is executed on. This may include hardware-related services (for example, accessing a hard disk drive), creation and execution of new processes, and communication with integral kernel services such as process scheduling. System calls provide an essential interface between a process and the operating system.
Linux阅码场
2019/10/08
1.7K0
Linux内核之旅/张凯捷——系统调用分析(1)
Linux syscall过程分析(万字长文)
为了安全,Linux 中分为用户态和内核态两种运行状态。对于普通进程,平时都是运行在用户态下,仅拥有基本的运行能力。当进行一些敏感操作,比如说要打开文件(open)然后进行写入(write)、分配内存(malloc)时,就会切换到内核态。内核态进行相应的检查,如果通过了,则按照进程的要求执行相应的操作,分配相应的资源。这种机制被称为系统调用,用户态进程发起调用,切换到内核态,内核态完成,返回用户态继续执行,是用户态唯一主动切换到内核态的合法手段(exception 和 interrupt 是被动切换)。
秃头哥编程
2019/08/23
15K1
原 浅谈Linux进程调度过程
在操作系统运行过程中,由于CPU bound和I/O bound,进行进程的调度自然是常事。进行进程调度时,操作系统使用某些特定算法(如FIFO、SCBF、轮转法等)在进程队列中选出一个进程作为下一个运行的进程,调用schedule。进行进程调用的时机有以下几种: 中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule(); 内核线程可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行
不高不富不帅的陈政_
2018/05/18
1.7K0
进程切换内核源码分析
(1)进程状态转换的时刻:进程终止、进程睡眠,这些过程会主动调用调度程序进行进程调度。 (2)当前进程时间片用完时 (3)进程从中断、异常及系统调用返回到用户态时
De4dCr0w
2019/02/27
1.3K0
linux信号处理源码分析(基于linux0.11)
linux的信号处理时机在系统调用结束后。这里以fork系统调用函数为例子讲解这个过程。下面是fork函数的定义。
theanarkh
2019/09/02
4.7K2
linux信号处理源码分析(基于linux0.11)
linux0.11系统调用过程和fork源码解析
所以执行fork函数就会执行system_call函数,但是在这之前,还有些事情需要做,就是保存现场。下面是操作系统执行系统调用前,在内核栈里保存的寄存器,这个压入的寄存器和iret中断返回指令出栈的寄存器是对应的。其中ip指向的是调用系统调用返回后的下一句代码。
theanarkh
2019/04/24
1.5K0
linux0.11系统调用过程和fork源码解析
INT0中断_中断请求寄存器
大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。 Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺 当进程执行系统调用时,先调用系统调用库中定义某个函数,该函数通常被展开成前面提到的_syscallN的形式通过INT 0x80来陷入核心,其参数也将被通过寄存器传往核心。 在这一部分,我们将介绍INT 0x80的处理函数system_call。 思考一下就会发现,在调用前和调用后执行态完全不相同:前者是在用户栈上执行用户态程序,后者在核心栈上执行核心态代码。那么,为了保证在核心内部执行完系统调用后能够返回调用点继续执行用户代码,必须在进入核心态时保存时往核心中压入一个上下文层;在从核心返回时会弹出一个上下文层,这样用户进程就可以继续运行。 那么,这些上下文信息是怎样被保存的,被保存的又是那些上下文信息呢?这里仍以x86为例说明。 在执行INT指令时,实际完成了以下几条操作: (1) 由于INT指令发生了不同优先级之间的控制转移,所以首先从TSS(任务状态段)中获取高优先级的核心堆栈信息(SS和ESP); (2) 把低优先级堆栈信息(SS和ESP)保留到高优先级堆栈(即核心栈)中; (3) 把EFLAGS,外层CS,EIP推入高优先级堆栈(核心栈)中。 (4) 通过IDT加载CS,EIP(控制转移至中断处理函数) 然后就进入了中断0x80的处理函数system_call了,在该函数中首先使用了一个宏SAVE_ALL,该宏的定义如下所示: #define SAVE_ALL / cld; / pushl %es; / pushl %ds; / pushl %eax; / pushl %ebp; / pushl %edi; / pushl %esi; / pushl %edx; / pushl %ecx; / pushl %ebx; / movl (__KERNEL_DS),%edx; / movl %edx,%ds; / movl %edx,%es; 该宏的功能一方面是将寄存器上下文压入到核心栈中,对于系统调用,同时也是系统调用参数的传入过程,因为在不同特权级之间控制转换时,INT指令不同于CALL指令,它不会将外层堆栈的参数自动拷贝到内层堆栈中。所以在调用系统调用时,必须先象前面的例子里提到的那样,把参数指定到各个寄存器中,然后在陷入核心之后使用SAVE_ALL把这些保存在寄存器中的参数依次压入核心栈,这样核心才能使用用户传入的参数。 下面给出system_call的源代码: ENTRY(system_call) pushl %eax # save orig_eax SAVE_ALL GET_CURRENT(%ebx) cmpl (NR_syscalls),%eax jae badsys testb
全栈程序员站长
2022/09/30
7870
Linux 内核0.11 系统调用详解(下)
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014688145/article/details/50615579
用户1147447
2019/05/26
3.9K1
系统调用(int 0x80)详解
在系统启动时,会在sched_init(void)函数中调用set_system_gate(0x80,&system_call),设置中断向量号0x80的中断描述符:
全栈程序员站长
2022/09/30
1.6K0
fork系统调用过程分析
fork函数通过系统调用创建一个与原来进程几乎完全相同的进程,一个进程调用fork函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。
De4dCr0w
2019/02/27
2.1K0
C++对象模型-Non-Staitc成员函数
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.10)
chinchao.xyz
2022/04/25
3100
Linux内核21-Linux内核的中断处理过程
如前所述,我们知道异常的处理还是比较简单的,就是给相关的进程发送信号,而且不存在进程调度的问题,所以内核很快就处理完了异常。
Tupelo
2022/08/15
2.6K0
Linux内核21-Linux内核的中断处理过程
C++对象模型-ObjectSliced
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.10)
chinchao.xyz
2022/04/25
3790
C++对象模型-ObjectSliced
linux0.11中断处理源码初探
所以idt的内容是一个单位是8字节,长度是256的数组。linux0.11分为中断、系统、陷阱门。系统在启动的时候设置idt。
theanarkh
2019/04/23
1.8K0
Linux系统调用原理
系统调用 跟用户自定义函数一样也是一个函数,不同的是 系统调用 运行在内核态,而用户自定义函数运行在用户态。由于某些指令(如设置时钟、关闭/打开中断和I/O操作等)只能运行在内核态,所以操作系统必须提供一种能够进入内核态的方式,系统调用 就是这样的一种机制。
用户7686797
2020/11/12
4.3K0
Linux中断 - IDT
中断描述符表简单来说说是定义了发生中断/异常时,CPU按这张表中定义的行为来处理对应的中断/异常。
扫帚的影子
2020/02/18
6.8K0
Linux中断 - IDT
C++对象模型-关于对象
从这篇博客开始真正介绍C++对象模型,前边BB了那么多没用的,终于开始了C++对模型的分析。关于C++对象模型的介绍,我将根据《深度探索C++对象模型》这本书,其书中的每一章,对应一篇博客,博客内容为自己对这本书的理解和补充吧。
chinchao.xyz
2022/04/25
4560
C++对象模型-关于对象
《Linux内核分析》之计算机是如何工作的 实验总结
马马虎虎学完了Python课程,一直想学下linux,看到里面有个linux的就选上了。当初没细看,如今听完第一节课有点傻眼,竟然糊里糊涂给自己找了一科汇编语言的课程,静心看下去庆幸自己还知道点堆栈的知识并出现了轻微的自虐倾向。闲话少说,现开正题。注:本文具有总结兼作业性质,如有雷同,纯属巧合。
WindCoder
2018/09/20
1.1K0
《Linux内核分析》之计算机是如何工作的 实验总结
C++对象模型-virtual继承
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.10)
chinchao.xyz
2022/04/25
4540
相关推荐
《Linux内核分析》之分析system_call中断处理过程实验总结
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验