• 进入"运维那点事"后,希望您第一件事就是阅读“关于”栏目,仔细阅读“关于Ctrl+c问题”,不希望误会!

Linux系统原理之进程管理

系统管理 彭东稳 9年前 (2015-09-01) 27259次浏览 已收录 0个评论

进程的概念

Linux是一个多用户多任务的操作系统,多用户是指多个用户可以在同一时间使用同一个linux系统。多任务是指在Linux下可以同时执行多个任务,更详细的说,Linux采用了分时管理的方法,所有的任务都放在一个队列中,操作系统根据每个任务的优先级为每个任务分配合适的时间片,每个时间片很短,用户根本感觉不到是多个任务在运行,从而使所有的任务共同分享系统资源,因此Linux可以在一个任务还未执行完时,暂时挂起此任务,又去执行另一个任务,过一段时间以后再回来处理这个任务,直到这个任务完成,才从任务队列中去除。这就是多任务的概念。

上面说的是单CPU多任务操作系统的情形,在这种环境下,虽然系统可以运行多个任务,但是在某一个时间点,CPU只能执行一个进程,而在多CPU多任务的操作系统下,由于有多个CPU,所以在某个时间点上,可以有多个进程同时运行。

进程的的基本定义是:进程是程序执行的实体,在自身的虚拟地址空间运行的一个独立的程序。从操作系统的角度来看,所有在系统上运行的东西,都可以称为一个进程。需要注意的是,程序和进程是不同的,进程虽然有程序产生,但是它并不是程序。程序是由指令加数据组成,它可以启用一个或多个进程,同时,程序只占用磁盘空间,而不占用系统运行资源;而进程包含计数器和全部CPU寄存器的值,进程占用系统内存空间,是动态的、可变的,关闭进程,占用的内存资源随之释放。

例如:用户在Linux上打开一个文件、就会产生一个打开文件的进程,关闭文件,进程也随机关闭。如果在系统上启动一个服务,例如启动tomcat服务,就会产生一个对应的java的进程。而如果启动apache服务,许多用户来同时请求httpd服务,apache服务器将会创建有多个httpd进程来对其进行服务。

进程管理是操作系统的最重要的功能之一。有效率的进程管理能保证一个程序平稳而高效地运行。Linux的进程管理与UNIX的进程管理相似。它包括进程调度、中断处理、信号、进程优先级、上下文切换、进程状态、进度内存等。在本节中,我们将描述Linux进程管理的基本原理的实现。它将更好地帮助你理解Linux内核如何处理进程及其对系统性能的影响。 

进程的特点

进程是现代操作系统的一个最基本的概念,如果用比较学术的话语来描述进程,那么应该是:进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活动的实体,它不只是程序代码,还包括当前的活动。通过程序计数器的值和cpu寄存器的内存来表示。

通俗表示:

1)进程是一个实体,每一个进程都有它自己独立的地址空间。一般情况下要包括:代码区、数据区和堆栈(这些都是干嘛的,后面有介绍)。

2)进程是一个“执行中的程序”。程序是一个没有生命的实体,CPU赋予了程序有时限的生命,这样它就成为了一个“活”的实体,我们称它为进程。

3)虽然程序的“生命”是CPU赋予的,但是这个机会却是人给的,人往往需要使用另外一个进程才能让程序执行起来。那么让程序执行的进程,我们称它为父进程。

进程描述符

内核对进程的优先级、进程的状态、地址空间等采用进程描述符的方式表示。在Linux内核中,进程用相当大的一个称为task_struct的结构表示。(可以在include/linux/sched.h中找到定义)。每个进程都有一个进程描述符,记录以下重要信息:进程标识符、进程当前状态、栈地址空间、内存地址空间、文件系统、打开的文件、信号量等。那么我们先看看Linux内核3.0版本的task_struct结构体的定义吧(删除了不必要的字段,只保留了重要的字段)。

进程的生命周期

每一个进程都有其生命周期,例如创建、运行、终止和消除。这些阶段会在系统启动和运行中重复无数次。因此,进程的生命周期对于其性能的分析是非常重要的。下图展示了经典的进程生命周期。

Linux系统原理之进程管理

当一个进程创建一个新的进程,进程的创建进程(父进程)调用一个fork()系统调用。当fork()系统调用被调用,它得到该新创建进程(子进程)的进程描述并调用一个新的进程id。它复制该值到父进程进程描述到子进程中。此时整个的父进程的地址空间是没有被复制的;父子进程共享相同的地址空间。

exec()系统调用复制新的程序到子进程的地址空间。因为父子进程共享地址空间,写入一个新的程序的数据会引起一个分页错误。在这种情况下,内存会分配新的物理内存页给子进程。

这个推迟的操作叫作写时复制。子进程通常运行他们自己的程序而不是与父进程运行相同的程序。这个操作避免了不必要的开销,因为复制整个地址空间是一个非常缓慢和效率低下的操作,它需要使用大量的处理器时间和资源。

当程序已经执行完成,子进程通过调用exit()系统调用终止。exit()系统调用释放进程大部分的数据并通过发送一个信号通知其父进程。此时,子进程是一个被叫作僵尸进程的进程(参阅page 7的“Zombie processes”)。

子进程不会被完全移除直到其父进程知道其子进程的调用wait()系统调用而终止。当父进程被通知子进程终止,它移除子进程的所有数据结构并释放它的进程描述。

进程内存段

进程使用其自身的内存区域来执行工作。工作的变化根据情况和进程的使用而决定。进程可以拥有不同的工作量特性和不同的数据大小需求。进程必须处理各种数据大小。为了满足需求,Linux内核为每个进程使用动态申请内存的机制。进程内存分配的数据结构如下图所示。

Linux系统原理之进程管理

代码段(TEXT) :存放CPU执行的机器指令。通常代码区是共享的,即其它执行程序可调用它。假如机器中有数个进程运行相同的一个程序,那么它们就可以使用同一个代码段。

数据段(DATA): 存放已初始化的全局变量,静态变量(包括全局和局部的),常量。全局变量和函数只能在当前文件中被调用。

BSS:(BSS)   存放未初始化的全局变量;的数据在程序开始执行之前被初始化为

 代码区所在的地址空间最低,往上依次是数据区和区,并且数据区和区在内存中是紧挨着的。可执行程序在运行时又多出了两个区域:栈段()和堆段(

堆区(HEAD):用于存放进程运行中被动态分配的内存段,位于和栈中间的地址位。由程序员申请分配和释放()。堆是从低地址位向高地址位增长,采用链式存储结构。频繁地造成内存空间的不连续,产生碎片。当申请堆空间时库函数按照一定的算法搜索可用的足够大的空间。因此堆的效率比栈要低的多。

栈区(STACK):由编译器自动释放,存放函数的参数值,局部变量等。每当一个函数被调用时,该函数的返回类型和一些调用的信息被存储到栈中。然后这个被调用的函数再为它的自动变量和临时变量在栈上分配空间。每调用一个函数一个新的栈就会被使用。栈区是从高地址位向低地址位增长的,是一块连续的内在区域,最大容量是由系统预先定义好的,申请的栈空间超过这个界限时会提示溢出,用户能从栈中获取的空间较小。

进程的状态

进程描述符中state字段描述进程当前的状态,它由一组标志组成,其中每个标志描述一种可能的进程状态。下面分别对这些状态进程描述。

Linux系统原理之进程管理

状态图解释

Stopped:进程被暂停,通常是通过接收一个信号,已经停止的进程不会再到CPU上运行。

Ready:已经准备好的进程执行,正在等待系统将CPU分配给它。

executing:正在执行的进程,处于运行状态。

Uninterruptible:不可中断的睡眠进程,正在等待一个事件或资源,且此进程不可以被信号中断。

Interruptible:可中断的睡眠进程,可以被信号中断。

Zombie:僵死的进程,就是正常运行也结束了,但是就是不释放内存空间;是由于父进程还没有发布wait4()系统调用返回有关死亡进程的信息。

僵尸进程

当一个进程接收到一个信号而终止,它在结束自己之前,通常需要一些时间来结束所有的任务(例如关闭打开的文件)。在这个通常非常短暂的时间内,该进程就是一个僵尸进程。进程已经完成所有的关闭任务后,它会向父进程报告其即将终止。有些时候,一个僵尸进程不能把自己终止,这将会引导它的状态显示为z(zombie)。

使用kill命令来关闭这样的一个进程是不可能的,因为该进程已经被认为已经死掉了。如果你不能清除僵尸进程,你可以结束其父进程,然后僵尸进程也随之消失。但是,如果父进程为init进程,你不能结束它。init进程是一个非常重要的进程,因此可能需要重启系统来清除僵尸进程。

进程的调度

进程作为程序的实体始终运行在系统中,并且进程占用系统资源。所以进程调度算法优劣会严重影响到系统的性能。为提高系统性能设计进程调度算法的原则,应该遵循:进程响应尽量快,后台进程的吞吐量尽量大,尽量避免进程“饿死”现象,底优先级和高优先级进程需要尽可能调和。

Linux进程调度的目的就是调度程序运行时,要在所有可运行状态的进程中选择最值得运行的进程投入运行。每个进程的task_struct结构中有policy、priority、counter和rt_priority这4项是选择进程的依据。其中policy为调度策略,用于区分普通进程和实时进程,实时进程由于普通进程运行;priority是进程(包括实时和普通)的静态优先级;counter是进程剩余的时间片,它的起始值就是priority的值;因为counter用于计算一个处于可运行状态的进程值的运行的程度,所以counter也被看作是进程动态优先级。rt_priority是实时进程特有的优先级别,用于实时进程间的选择。

进程调度算法

那么Linux是如何调度进程是非常重要的,Linux内核依据进程的优先级(实时优先级)和进程是属于cpu敏感还是I/O敏感型的分配不同长短的时间片。 可以说进程的调度策略是动态智能化的。Linux内核使用一个O(1)的算法代替以前的O(n)的CPU调度算法。O(1)指的是一种静态的算法,意味着选择一个进程并执行所花费的时间是一个常数,不管进程的数量的大小。

新的调度算法的扩展性非常好,通过为进程的不同的优先级创建对应的数组,即一个优先级对应一个数组,相同的优先级用队列存储,并有一个二进制位图来常量时间从数组中查找到将要调度的优先级队列。巧妙的用空间换取时间,最大的提高调度性能。不管进程的数量或者处理器的数量是多少,系统的开销都是非常少的。该算法使用两个进程优先级数组:active(活动的),expired(过期的)。

调度器根据进程的优先级和优先拦截率为进程分配时间片,然后进程以优先级顺序放置到active数组内。当进程时间片耗尽,进程申请一个新的时间片并放置到expired数组内。当active数组中的所有进程的时间片耗尽,这两个数组进行切换,重新运行该算法。对于一般的交互式进程(相对于实时进程),拥有高优先级的进程通常比低优先级的进程得到更长的时间片和更多的计算时间,但这并不表示低优先级的进程会被完全忽略(饿死)。该算法的优势是为拥有大量线程和进程并拥有多处理器的企业级环境提升Linux内核的扩展性。该O(1)的新CPU调度器是为内核2.6设计的,但是现在已经移植到2.4系列中。下图说明了Linux CPU如何调度工作。

Linux系统原理之进程管理

新调度器的另一个显著改进是支持非一致性内存架构(NUMA)和对称多线程处理器(SMP),例如Intel超线程技术。

改进后的NUMA支持确保只有某个节点过载时,负载平衡才会跨越某个NUMA节点。这个机制确保了在NUMA系统相对比较缓慢的扩展链接流量的最小化。尽管每个调度节拍时负载平衡会遍历调度域群组中的处理器,但只有在节点过载并请求负载平衡时,负载才会跨越调度域转移。

以下为常见的进程调度算法:

1)时间片轮转调度算法(round-robin):SCHED_RR用于实时进程。系统使每个进程依次地按时间片轮流执行的方式。

2)优先权调度算法:SCHED_NORMAL用于非实时进程。每次系统都会选择队列中优先级最高的进程运行。Linux 采用抢占式的调度策略,即系统中当前运行的进程永远是可运行进程中优先权最高的进程。

3)FIFO(先进先出)调度算法:SCHED_FIFO用于实时进程,采用FIFO调度算法选择的实时进程必须是运行时间较短的进程,因为这种进程一旦获得CPU就只有等到它运行完或因等待资源主动放弃CPU时,其他进程才能获得运行机会。

进程的分类

Linux在执行进程调度的时候,对不同类型的进程采取的策略也不同,一般按照进程的功能和运行的程序分类,进程可划分为两大类:

●系统进程:可以执行内存资源分配和进程切换等管理工作;而且,该进程的运行不受用户的干预,即使是root用户也不能干预系统进程的运行。

●用户进程:通过执行用户程序、应用程序或内核之外的系统程序而产生的进程,此类进程可以在用户的控制下运行或关闭。

针对用户进程,又可以分为交互进程、批处理进程和守护进程四类。

1)交互进程:由一个shell终端启动的进程,在执行过程中,需要与用户进行交互操作,可以运行于前台,也可以运行在后台。

2)批处理进程:该进程是一个进程集合,负责按顺序启动其他的进程。

3)守护进程:守护进程是一直运行的一种进程,经常在Linux系统启动时启动,在系统关闭时终止。它们独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。例如httpd进程,一直处于运行状态,等待用户的访问。还有经常用的crond进程,这个进程类似与windows的计划任务,可以周期性的执行用户设定的某些任务。

4)实时进程:这类进程对调度有非常严格的要求,这种类型的进程不能被低优先级进程阻塞。并且在很短的时间内做出反应,典型的实时进程有音频应用程序,机器人控制等。

进程优先级

Linux系统中每一个普通进程都有一个静态优先级,这个值会被调度器用来作为参考来调度进程。正在内核中调度的优先级区间为0-139,数字越小优先级越高;其中100-139用户可以控制,0-99内核调整的。一个新的进程总是从它的父进程继承此值。Linux进程优先级还包括动态优先级、实时优先级等。各个进程优先级描述如下:

●静态优先级(priority):被称为“静态”是因为它不随时间而改变,只能由用户进程修改。它指明了在被迫和其他进程竞争CPU之前该进程所应该被允许的时间片最大值(20)

●动态优先级(counter):counter即系统为每个进程运行而分配的时间片。Linux用它来表示进程的动态优先级。当进程有CPU时,counter就随着时间不端减小。当它递减为0时,标记该进程将重新调度。它指明了在当前时间片中所剩余的时间量,最初为20。

●实时优先级(rt_priority):它的变化范围是从0-99,任何实时进程的优先级都高于普通的进程。

从Linux2.6内核开始引入了一种机制保证了不管进程队列有多长从中挑取一个进程的时间是相同的;在编程中衡量一个算法面对不同队列长度的性能如何有一种标准叫大O标准O:O(1),O(N),O(logn),O(N^2),O(2^N)

调整优先级就是调整进程的nice值,其中100—139对应-20—-19

Linux进程树

Linux中的进程就是一个链表,进程和子进程有父子关系,也属于管理和被管理的关系,当父进程终止时,子进程也随之而终止。但子进程终止,父进程并不一定终止。比如httpd服务器运行时,我们可以杀掉其子进程,父进程并不会因为子进程的终止而终止。

一个进程创建新进程称为创建了子进程(Child Process)。相反地,创建子进程的进程称为父进程。所有进程追溯其祖先最终都会落到进程号为1的进程身上,这个进程叫做init进程,是内核自举后第一个启动的进程。init进程扮演终结父进程的角色。因为init进程永远不会被终止,所以系统总是可以确信它的存在,并在必要的时候以它为参照。如果某个进程在它衍生出来的全部子进程结束之前被终止,就会出现必须以init为参照的情况。此时那些失去了父进程的子进程就都会以init作为它们的父进程。如果执行一下ps -af命令,可以列出许多父进程ID为1的进程来。Linux提供了一条ps tree命令,允许用户查看系统内正在运行的各个进程之间的继承关系。直接在命令行中输入ps tree即可,程序会以树状结构方式列出系统中正在运行的各进程之间的继承关系。

init进程

整个Linux系统的所有进程也是一个树形结构。树根是系统自动构造的,即在内核态下执行的0号进程,它是所有进程的祖先。由0号进程创建1号进程(内核态),1号负责执行内核的部分初始化工作及进行系统配置,并创建若干个用于高速缓存和虚拟主存管理的内核线程。随后,1号进程调用execve()运行可执行程序init,并演变成用户态1号进程,即init进程。

init进程按照配置文件/etc/initab的要求,完成系统启动工作,创建编号为1号、2号…的若干终端注册进程getty。每个getty进程设置其进程组标识号,并监视配置到系统终端的接口线路。当检测到来自终端的连接信号时,getty进程将通过函数execve()执行注册程序login,此时用户就可输入注册名和密码进入登录过程,如果成功,由login程序再通过函数execv()执行shell,该shell进程接收getty进程的pid,取代原来的getty进程。再由shell直接或间接地产生其他进程。

进程上下文切换

在进程运行过程中,进程的运行信息被保存于处理器的寄存器和它的缓存中。正在执行的进程加载到寄存器中的数据集被称为上下文。为了切换进程,运行中进程的上下文将会被保存,接下来的运行进程的上下文将被被恢复到寄存器中。进程描述和内核模式堆栈的区域将会用来保存上下文。这个切换被称为上下文切换。过多的上下文切换是不受欢迎的,因为处理器每次都必须清空刷新寄存器和缓存,为新的进程制造空间。它可能会引起性能问题。

在OS中,凡要进行中断处理和执行系统调用时,都将涉及到进程上下文的保存和恢复问题,此时系统所保存或恢复的上下文都是属于同一个进程的。而在进程调度之后,内核所应执行的是进程上下文的切换,即内核是把当前进程的上下文保存起来,而所恢复的则是进程调度程序所选中的进程的上下文,以使该进程能恢复执行。

上下文切换由Linux内核完成将当前正在运行的进程上下文信息从寄存器中卸载下来到进程堆栈中暂存,然后Load新的进程到寄存器,高速缓存中开始执行。所以说上下文切换频繁是最影响程序的性能的因素之一。如下图:

Linux系统原理之进程管理

降低上下文切换频率是提高进程性能的关键; 对于我们的网络服务器来说,大量的客户请求随时都大量涌入服务器,cpu频繁响应网络中断会对我们的应用程序性能产生极大的影响。 如何避免频繁的上下文切换呢? 除了提高进程的优先级外; smp内核提供了亲和度这一技术,将网络中断亲和到一个CPU上去。 这样频繁的中断就不会干扰其他CPU上的应用进程的执行。

中断处理

中断处理是优先级最高的任务之一。中断通常由I/O设备产生,例如网络接口卡、键盘、磁盘控制器、串行适配器等等。中断处理器通过一个事件通知内核(例如,键盘输入、以太网帧到达等等)。它让内核中断进程的执行,并尽可能快地执行中断处理,因为一些设备需要快速的响应。它是系统稳定的关键。当一个中断信号到达内核,内核必须切换当前的进程到一个新的中断处理进程。这意味着中断引起了上下文切换,因此大量的中断将会引起性能的下降。

在Linux的实现中,有两种类型的中断。硬中断是由请求响应的设备发出的(磁盘I/O中断、网络适配器中断、键盘中断、鼠标中断)。软中断被用于处理可以延迟的任务(TCP/IP操作,SCSI协议操作等等)。你可以在/proc/interrupts文件中查看硬中断的相关信息。

在多处理器的环境中,中断被每一个处理器处理。绑定中断到单个的物理处理中能提高系统的性能。更多的细节,“CPU的中断处理亲和力”。

进程间通信

Linux是一个多处理操作系统,进程拥有独立的权限和单一职责。如果系统中某个进程发生崩溃,他不会影响到另外一个进程的运行。每一个进程都运行在各自的虚拟地址空间中,只有通过核心控制下可靠的进程通信机制,他们之间才能发生通信。进程通信机制包括管道、信号、套接字、共享内存等等方式。

Linux下的进程通信手段基本上是从Unix平台上的进程通信手段继承而来的。而对Unix发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间通信方面的侧重点有所不同。前者对Unix早期的进程间通信手段进行了系统的改进和扩充,形成了“system V IPC”,通信进程局限在单个计算机内;后者则跳过了该限制,形成了基于套接口(socket)的进程间通信机制。Linux则把两者继承了下来。并称之为Linux IPC进程间通信。

Linux下进程间通信的几种主要手段简介:

信号:信号是Linux系统中最为原始的一种进程间通信机制,是硬件的中断机制在软件层次上的一种模拟。那么在行为上,一个进程收到一个信号与CPU收到一个终端请求就是一模一样的了。而且也是通过一个数字来区分不同事件的,中断管这个叫中断请求号,那么信号就是信号请求号了。同时信号也是各种进程间通信机制汇总唯一的异步通信机制。一个进程不必通过任何操作来等待信号的到达。而且进程也不可能知道信号在上面时候回到达。掌握并能理解好信号机制,可以为学习Linux内核模块编程打下良好的基础。

管道(pipe)

Linux中管道是一种使用非常频繁的进程间通信机制,从本质上说,管道也是一种文件,只是它和普通文件的大小不同。以为管道实际上只是内存中的一个固定大小的缓冲区。为4k(64位系统为1M字节)。当这个缓冲区满的时候,再向管道吸入数据就会被阻塞,知道缓冲区中的内容被读取出来有了空闲的地方,写操作才能继续进行。于此相反,如果这个缓冲区空的时候,从管道中读取数据也会被阻塞,直到缓冲区有内容之后,与此相反,如果这个缓冲区空的时候,从管道中读取数据也会被阻塞,直到缓冲区有内容之后,读操作才能继续进行。需要注意,管道是单向的,即只能向一端写,从另一端读;管道中的数据也是一次性的,即一旦有人读取,其他人就读不到了。也正因为这些特点,数据就像水在管道中流淌一样,从水管流向水龙头,水龙头关上了,水管的水也就不流了。管道也因此得名。

I/O重定向

在命令中使用管道其实还隐含了I/O重定向的应用,即将管道前端进程的标准输出重定向到管道后端进程的标准输入中。这样的机制也可以通过程序来完成。而且这种i/O重定向机制也不仅仅是只能使用管道的,利用其它方法也能完成,比如套接字。

报文(Message)队列(消息队列)

消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。

共享内存

使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。

套接口(Socket)

更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。

进程地址空间

如果要说进程地址空间,那就不得不说Linux的内存管理机制,本博客在系统原理部分有一章就是说内核内存管理机制的。

Linux的虚拟地址空间为0~4GB,虚拟地址4GB空间被Linux内核分为内核空间和用户空间两部分。将最高的1GB(从虚拟机地址0xC0000000到0xFFFFFFF)留给内核使用,称为“内核空间”。将较低的3GB留给用户进程使用,称为“用户空间”。因为每个进程可以通过系统调用进入内核,因此,Linux内核空间被系统的所有进程共享,实际上对于某个进程来说,它仍然可以拥有4GB的虚拟空间。

其中,很重要的一点是虚拟地址空间,并不是实际的地址空间。在为进程分配地址空间时。根据进程需要的空间进行分配,4GB仅仅是最大限额而已,并非一次性将4GB分配给进程。一般一个进程的地址空间总是小于4GB的,可以通过查看/proc/pid/maps文件来获悉某个具体进程的地址空间。但进程的地址空间并不对应实际的物理页,Linux采用Lazy的机制来分配实际的物理页(“Demand paging”和“写时复制的技术”),从而提高实际内存的使用率,即每个虚拟内存页并不一定对应于物理页。虚拟页和物理页的对应是通过映射机制来实现的,即通过页表进行映射到实际的物理页。因为每个进程都有自己的页表,因此可以保证不同进程的相同虚拟地址可以映射到不同的物理页,从而为不同的进程都可以同时拥有4GB的虚拟地址空间提供了可能。

内核是系统中优先级最高的部分,内核函数申请动态内存时系统不会推迟这个请求;而进程申请内存空间时,进程的可执行文件被装入后,进程不会立即对所有的代码进程访问。因此内核总是尽量推迟给用户进程分配动态空间。内核分配空间时,可以通过alloc_pages从分区页框分配器中获得页框;通过kmalloc()函数使用slab分配器为专用或通过对象分配块;通过vmalloc()或vmalloc32()函数获得一块非连续的内存区。与进程地址空间有关的全部信息都包含在一个叫做内存描述符(memory desvriptor)的数据结构中,其结构类型为mm_structs进程描述符的mm字段,就是指向这个结构的。

1.创建进程地址空间

copy_mm()函数通过建立新进程的所有页表和内存描述符,来创建进程的地址空间。

2.删除进程地址空间

内核调用exit_mm()函数释放进程的地址空间。


如果您觉得本站对你有帮助,那么可以支付宝扫码捐助以帮助本站更好地发展,在此谢过。
喜欢 (1)
[资助本站您就扫码 谢谢]
分享 (0)

您必须 登录 才能发表评论!