【进程管理】模式切换

Linux内核将地址空间划分为用户空间和系统空间,用户程序只能访问用户空间,而系统程序对于用户空间和系统空间;由用户空间进入系统空间主要是通过系统调用和中断来进入的,对应用户空间切换到系统空间;X86对中断的支持非常复杂,linux内核只是使用了其中的一部分,很多机制不是必须的;

中断的分类

说明几点

(1)中断有两种,一种是由CPU外部硬件产生的,另一种是由CPU本身执行程序的过程中产生的;外部中断即我们所说的中断(interrupt),外部中断是异步的,由硬件产生,我们无法预测它什么时候发生;

(2)x86软件产生的中断是由“INT n”同步产生的,有程序产生,只要CPU执行了一条INT指令,就知道在开始执行下一条指令前就会进入中断服务程序,我们又称此类中断为“陷阱”;int 80为系统调用的陷阱号

(3)异常,是被动的,如页面异常,除数为0的异常;

(4)上述异常,陷阱,以及中断,都统称为中断,CPU的相应过程基本一致,在执行完当前指令后,根据中断源提供的中断向量,在内存中找到相应的服务程序入口并调用该服务程序;

(5)外部中断向量表是由软件或硬件设置好的,陷阱向量是在自陷指令INT n中发出的,各种异常向量是由CPU的硬件结构中预先规定好的;不同的情况也就根据不同的中断向量而分开下来了;

x86的中断实现

说明几点

(1)Intel CPU支持256个向量,在早起的实地址模式中,CPU从0开始到1K字节作为一个中断向量表,每一个表项占4个字节,由两个字节的段地址和两个字节的位移组成,这样的地址构成了中断服务程序的入口地址;但是这样的机制不能构成现代意义上的操作系统,即使把16位寻址改为32位也无济于事,因为缺少对PSW的处理,无法完成运行模式的切换;在保护模式中,中断向量表的表项由单纯的入口地址改成了类似于PSW加入口地址并且更为复杂的描述项,称为gate,意思就是必须通过这些门,才能进入相应的中断服务程序;这样的门不仅为中断所用,还为切换CPU的运行状态而设置;根据不同的用途和目的,门分为任务门,中断门,陷阱门,调用门(不是与中断向量表相联系的);

(3)任务门结构:TSS段选择码,通过GDT或LDT指向特殊的系统段中的一种,实际上是用来保存任务运行“现场”的 数据结构(CPU中所有与具体进程有关的寄存器内容,包括页面目录指针CR3)和三个堆栈指针;中断发生时,CPU在中断向量表中找到相应的表项,如果此项是个任务门,并通过了优先级的检查,CPU会将当前任务的运行现场保存在相应的TSS中,并将任务门所指向的TSS作为当前任务,将其内容装入到CPU的各个寄存器,从而完成一次任务切换;为此,又增设了任务寄存器TR,用来指向当前任务的TSS。在linux内核中,一个任务就是一个进程,但Task_struct存放了更多的信息,因此linux内核并不完全是通过任务门作为切换进程的唯一手段;

(4)除任务门,其他三种门的结构基本相同,类型码标识不同的门,中断门的类型是110,陷阱门的类型是111,调用门的类型码是100;任务门不需要段内位移,因为它不需指向某一个子程序的入口,TSS作为一个段来对待的,而其他的三个门指向一个子程序,所以得结合段选择码和段内位移;

(5)Intel在i386CPU中的实现一个非常复杂的优先级别检验机制,先是门的DPL和CPU的CPL相比,CPL小于等于DPL才能通过该门(通常是中断门),然后在将目标代码段描述项中的DPL与CPL再比较;通过门后,CPU的CPL优先级只能提高;两次比较中,任何一个失败,都将引起一次全面保护异常;

(6)进入中断服务程序中,CPU要将当前EFLAGS寄存器的内容以及返回地址(CS和EIP)压入堆栈中;若中断是由异常引起的,还要将出错原因压入堆栈中;根据目标代码中的运行级别的不同,分为0,1,2,分别对应三个额外的堆栈指针,若中断发生时的CPL和目标代码中的DPL不同,就要切换堆栈指针;

(7)在Linux内核中,当中断发生在用户空间时,运行级别为3,而内核中的中断服务程序运行级别为0,所以会引起堆栈的变换,而若中断发生在系统空间时,则不会更换堆栈;

系统调用

说明几点

(1)外部中断是CPU被动的,异步的进入系统空间的一种手段,而系统调用就是CPU主动、同步的进入系统空间的手段;软件设计人员确切的知道执行指令后就一定会进入系统空间;相比之下,中断有很大的不可预测性,但是它们都使CPU的运行状态从用户态转入系统态,当然中断有可能发生在系统空间运行时,而系统调用只发生在用户空间;其实最大的原因在于CPU运行状态的变化,就是所谓的保护模式

(2)Linux系统调用通过中断指令“INT 0x80”来完成,所有系统调用都要进入系统空间,在完成了所需要的服务后从系统空间返回,比如sethostname()是这样一个系统调用,设置主机名;将%eax存入0x4a(通过寄存器转入系统调用号),然后调用“INT 0x80”;如果用堆栈来传系统调用号,用户空间到系统空间要涉及到堆栈的切换,用户堆栈变为系统堆栈,虽然可以从用户空间读,但麻烦;从系统调用返回时,出错可设置好出错代码以及返回值;

模式切换

说明几点

(1)当外部中断发生时,CPU根据中断控制器取得中断向量,根据中断向量从中断向量表IDT中找到相应的表项,该表项对应的是个中断门;这样CPU就根据中断门的设置而达到了该通道的总服务程序的入口,假定为IRQ0x03_interrupt。由于中断是用户空间发生的,运行级别CPL为3,中断服务程序属于内核,其运行级别DPL为0;所以,CPU要从寄存器TR所指的当前TSS中取出内核(0级)的堆栈指针,并把堆栈切换到内核堆栈,即当前进程的系统空间堆栈;而每次从系统空间返回时要返回到用户空间时堆栈一定回到其原点;也就是说CPU进入IRQ0x03_interrupt时,堆栈中除寄存器EFLAGS的内容以及返回地址,一无所有;穿过中断门(非陷阱门)后,中断是被关闭,因为通过中断门时CPU会自动将中断关闭的

(2)对于系统调用,CPU穿过陷阱门和发生中断穿过中断门的过程是相同的,外部中断穿过中断门,是不需要检查中断门规定的级别,而INT指令穿过中断门或陷阱门时,要核对所规定的准入级别和CPU当前运行的级别;系统调用设置的陷阱门的准入级别DPL为3;寄存器IDTR指向当前的中断向量表IDT,而IDT表中对应表项0x80的表项就是为INT 0x80设置的陷阱门,其中的函数指针是system_call();

(3)

中断进入的公有代码如下:

//所有的中断都共享该代码,在这之前会将中断请求号的数值压入堆栈,这样可以确定中断源的来源,比如0x03-256,减去负数主要是为了区分系统调用
//进入中断以后,CPU会禁止中断
	.p2align CONFIG_X86_L1_CACHE_SHIFT
common_interrupt:
	addl $-0x80,(%esp)	/* Adjust vector into the [-256,-1] range */
	SAVE_ALL           //保护现场,主要是保存一些寄存器
	TRACE_IRQS_OFF
	movl %esp,%eax
	call do_IRQ        //执行中断处理程序
	jmp ret_from_intr  //恢复现场
ENDPROC(common_interrupt)
	CFI_ENDPROC

系统调用的入口

ENTRY(system_call)
	RING0_INT_FRAME			# can't unwind into user space anyway
	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)
	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         //恢复现场后会调度

异常的入口(以page_fault为例子)

ENTRY(page_fault)
	RING0_EC_FRAME
	pushl_cfi $do_page_fault
	ALIGN
error_code:
	/* the function address is in %gs's slot on the stack */
	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
	cld
	movl $(__KERNEL_PERCPU), %ecx
	movl %ecx, %fs
	UNWIND_ESPFIX_STACK
	GS_TO_REG %ecx
	movl PT_GS(%esp), %edi		# get the function address
	movl PT_ORIG_EAX(%esp), %edx	# get the error code
	movl $-1, PT_ORIG_EAX(%esp)	# no syscall to restart
	REG_TO_PTGS %ecx
	SET_KERNEL_GS %ecx
	movl $(__USER_DS), %ecx
	movl %ecx, %ds
	movl %ecx, %es
	TRACE_IRQS_OFF
	movl %esp,%eax			# pt_regs pointer
	call *%edi
	jmp ret_from_exception         //返回
	CFI_ENDPROC
END(page_fault)

进程切换判断

	# userspace resumption stub bypassing syscall exit tracing
	ALIGN
	RING0_PTREGS_FRAME
ret_from_exception:
	preempt_stop(CLBR_ANY)
ret_from_intr:      //返回现场
	GET_THREAD_INFO(%ebp)
check_userspace:
	movl PT_EFLAGS(%esp), %eax	# mix EFLAGS and CS
	movb PT_CS(%esp), %al
	andl $(X86_EFLAGS_VM | SEGMENT_RPL_MASK), %eax
	cmpl $USER_RPL, %eax      //如果发生在用户空间(系统调用和发生在用户空间的外部中断)
	jb resume_kernel		# not returning to v8086 or userspace   //需要调度

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)

#ifdef CONFIG_PREEMPT
ENTRY(resume_kernel)
	DISABLE_INTERRUPTS(CLBR_ANY)
	cmpl $0,TI_preempt_count(%ebp)	# non-zero preempt_count ?  //是否允许抢占
	jnz restore_all    //不允许,就恢复
need_resched:
	movl TI_flags(%ebp), %ecx	# need_resched set ?
	testb $_TIF_NEED_RESCHED, %cl
	jz restore_all        //恢复
	testl $X86_EFLAGS_IF,PT_EFLAGS(%esp)	# interrupts off (exception path) ?
	jz restore_all
	call preempt_schedule_irq   //调度
	jmp need_resched
END(resume_kernel)
#endif
	CFI_ENDPROC

说明几点:

(1)在用户空间发生的外部中断以及异常以及系统调用会在返回用户空间,进行进程调度;通过cmpl $USER_RPL, %eax 来判断是否发生在用户空间;

保护现场如下:

.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

恢复现场如下:

//恢复现场
restore_all:
	TRACE_IRQS_IRET
restore_all_notrace:
	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
restore_nocheck:
	RESTORE_REGS 4			# skip orig_eax/error_code
irq_return:
	INTERRUPT_RETURN
.section .fixup,"ax"
ENTRY(iret_exc)
	pushl $0			# no error code
	pushl $do_iret_error
	jmp error_code
.previous
.section __ex_table,"a"
	.align 4
	.long irq_return,iret_exc
.previous
时间: 2024-08-02 08:47:26

【进程管理】模式切换的相关文章

进程管理

一.进程 1.概念 内核的功用:进程管理.文件系统.网络功能.内存管理.驱动程序.安全功能等 Process: 运行中的程序的一个副本,是被载入内存的一个指令集合 进程ID(Process ID,PID)号码被用来标记各个进程 UID.GID.和SELinux语境决定对文件系统的存取和访问权限, 通常从执行进程的用户来继承 存在生命周期 task struct:Linux内核存储进程信息的数据结构格式 task list:多个任务的的taskstruct组成的链表 进程创建: init:第一个进

Linux运维之进程管理

一.进程概念 进程是内核的一个功能,在Linux中,运行一个程序或命令可以出发一个事件而驱动一个PID,在linux系统中,系统只识别二进制程序文件,我们可以通过执行系统上的二进制程序来运行程序,进而产生进程.在linux系统中第一个进程是init程序,它是系统开机第一个加载的程序,用来支撑系统的正常运行的一个程序,内核启动的一个用户级进程.     1.进程优先级 进程优先级被分为系统优先级和实时优先级 系统优先级:数字越小,优先级越高 0-139(老版本操作系统如4,5) 各有140个运行队

进程管理工具

在我们了解进程以后,我接下来要掌握进程的管理工具如:pstree , ps , pgrep , pidof , top ,htop , glance , pmap , vmstat , dstat , kill , pkill , uptime 等:Linux 系统各进程的相关信息均保存在/proc/PID 目录下的各文件中: 一,pstree pstree:是以树状的形式来显示进程的关系: pstree [option] -p : 连进程编号一块显示出来; -a : 显示每个程序的完整指令,包

操作系统学习---进程管理(二)

要点: 基础:进程描述及控制 策略:进程调度 实现:互斥与同步 避免:死锁与饥饿 解决:几个经典问题 进程的引入 程序的顺序执行 源代码程序,目标程序和可执行程序 程序执行:编辑,编译,链接,执行 程序的结构:顺序,分支,循环结构 程序执行的特征:顺序性,封闭性,可再现性 程序并发执行 多道程序设计技术:多个程序并发执行 程序并发执行时的特征:间断性,非封闭性,不可再现性 并发执行引发的问题: 协调各程序的执行顺序:输入数据还未全部输入内存时,计算必须等待 多个执行程序共享系统资源,程序之间可能

七、Linux脚本进阶和进程管理

一.进程 进程:init(1)-->系统的第一个进程,通过fork调用其他进程,自身由内核发起. 通过pstree可以查看进程之间的父子关系. [[email protected] ~]# pstree init─┬─NetworkManager─┬─dhclient │                └─{NetworkManager} ├─abrtd ├─acpid ├─atd ├─auditd───{auditd} ├─crond ├─cupsd ├─dbus-daemon───{dbus

9.8_Linux进程管理和计划任务

进程管理篇 进程概念 内核的功用:进程管理.文件系统.网络功能.内存管理.驱动程序.安全功能等 Process: 运行中的程序的一个副本,是被载入内存的一个指令集合 进程ID(Process ID,PID)号码被用来标记各个进程 UID.GID.和SELinux语境决定对文件系统的存取和访问权限, 通常从执行进程的用户来继承 存在生命周期 task struct:Linux内核存储进程信息的数据结构格式 task list:多个任务的的task struct组成的链表 进程创建: init:第一

linux进程管理及计划任务

什么是进程? 在Linux系统当中:触法任何一个事件时,系统都会将它定义成为一个进程,并且给予这个进程一个ID,称为PID,同时依据触发这个进程的用户与相关属性关系,给予这个PID一组有效的权限设置. 进程与程序: 程序(program):通常为二进制程序放置在存储媒介中,以物理文件的形式存在. 进程(process):程序被触发后,执行者的权限与属性.程序的程序代码与所需数据等都会被加载到内存中,操作系统并给予这个内存内的单元一个标识符(PID),可以说,进程就是一个正在运行中的程序. 进程I

操作系统原理学习笔记--进程管理

标签: 数据结构primitive存储算法io作业 2012-09-01 16:50 6603人阅读 评论(1) 收藏 举报  分类: [OS](4)  版权声明:本文为博主原创文章,未经博主允许不得转载. 进程管理 要点: 基础:进程描述及控制 策略:进程调度 实现:互斥与同步 避免:死锁与饥饿 解决:几个经典问题 进程的引入 程序的顺序执行 源代码程序,目标程序和可执行程序 程序执行:编辑,编译,链接,执行 程序的结构:顺序,分支,循环结构 程序执行的特征:顺序性,封闭性,可再现性 程序并发

linux进程管理工具一

内核最强大功能之一就是进程管理,对于system administrator来说,这部分熟练度对于系统调优起着决定性作用.现在我们一起来了解一下linux的进程管理工具吧,在介绍工具之前,有些基本概念要澄清: task struct:Linux内核存储进程信息的固定格式多个任务的的task struct组件的链表:task list MMU:Memory Management Unit  实现线性地址和物理地址的转换(根据task structure)虚拟内存集:可以被换出到swap的地址空间

【操作系统】知识点总结之进程管理与调度

1.中央处理器 1.1 CPU:处理器由运算器.控制器.一系列的寄存器以及高速缓存构成 运算器实现指令中的算术和逻辑运算,是计算机计算的核心 控制器负责控制程序运行的流程,包括取指令.维护CPU状态.CPU与内存的交互等等 寄存器是指令在CPU内部作处理的过程中暂存数据.地址以及指令信息的存储设备.在计算机的存储系统中它具有最快的访问速度.包括用户可见寄存器,控制寄存器. 用户可见寄存器 高级语言编译器通过算法分配并使用之,以减少程序访问主存次数 包括通用寄存器.数据寄存器.地址寄存器 数据寄存