进程管理及进程间通讯.ppt
《进程管理及进程间通讯.ppt》由会员分享,可在线阅读,更多相关《进程管理及进程间通讯.ppt(131页珍藏版)》请在三一办公上搜索。
1、第5章 进程管理及进程间通讯,本章介绍了Linux 进程的管理、调度以及 Linux 系统支持的进程间通讯机制,并对某些通信手段的内部实现机制进行了分析。本章还讨论了 Linux 核心的一些基本任务和机制,将Linux内核中为使内核其他部分能有效工作的用于同步的几种机制集中起来分析,强调了它们之间在实现和使用上的不同。,5.1 Linux 进程和线程,一个大型的应用系统,往往需要众多进程协作。进程是操作系统理论的核心与基础,许多概念都和进程相关。进程的标准定义是:进程是可并发执行的程序在一个数据集合上的运行过程。换句话说,在自身的虚拟地址空间运行的一个单独的程序称作一个进程。在Linux系统中
2、,当一个程序开始执行后,在开始执行到执行完毕退出这段时间里,它在内存中的部分就被称作一个进程。进程与程序是有区别的,程序只是一些预先设定好的代码和数据,进程是一个随时都可能发生变化的、动态的、使用系统运行资源的程序。程序是静态的,而进程是动态的。一个程序可以启动多个进程。和进程联系在一起的不仅有进程的指令和数据,而且还有当前的指令指针、所有的 CPU 寄存器以及用来保存临时数据的堆栈等,所有这些都随着程序指令的执行在变化。,Linux操作系统包括三种不同类型的进程,每种类型的进程都有自己的特点和属性。(1)交互进程由shell启动的进程。交互进程既可以在前台运行,也可以在后台运行。(2)批处理
3、进程这种进程和终端没有联系,是一个进程序列。(3)监控进程(也称守护进程)Linux系统启动时启动的进程,并在后台运行。上述三种进程各有各的作用,使用场合也有所不同。,5.1.1 Linux 进程管理的数据结构,Linux是一个多任务的操作系统,在同一个时间内,可以有多个进程同时执行。由于单CPU计算机实际上在一个时间片断内只能执行一条指令,Linux使用了一种称为“进程调度(process scheduling)”的机制。首先为每个进程指派一定的运行时间,然后依照某种规则,从众多进程中挑选一个投入运行,其他的进程暂时等待,当正在运行的那个进程时间耗尽,或执行完毕退出,或因某种原因暂停,Lin
4、ux就会重新进行调度,挑选下一个进程投入运行。因为每个进程占用的时间片都很短,在用户的角度看,就好像多个进程同时运行一样。,进程在运行过程中,要使用许多计算机资源,例如 CPU、内存、文件等。同时可能会有多个进程使用同一个资源,因此操作系统要跟踪所有的进程及其所使用的系统资源,以便能够管理进程和资源。在Linux中,每个进程在创建时都会被分配一个数据结构,称为进程控制块(Process Control Block,PCB)。PCB中包含了很多重要的信息,供系统调度和进程本身执行使用,其中最重要的是进程ID(process ID,PID),进程ID也被称作进程标识符,是一个非负的整数,在Linu
5、x操作系统中唯一地标志一个进程。在最常使用的i386架构(即PC使用的架构)上,PID的变化范围是一个非负整数0-32767,这也是所有可能取到的进程ID。每个进程的进程ID各不相同。可使用ps命令看看当前系统中有多少进程在运行。除标题外,每一行都代表一个进程。在各列中,PID一列代表了各进程的进程ID,command一列代表了进程的名称或在shell中调用的命令行。,Linux 中的每个进程有自己的虚拟地址空间,操作系统的一个最重要的基本管理目的,就是避免进程之间的互相影响。但有时用户也希望能够利用两个或多个进程的功能完成同一任务,为此,Linux 提供许多机制,利用这些机制,进程之间可以进
6、行通讯并共同完成某项任务,这种机制称为“进程间通讯(Interprocess Communication,IPC)”。信号和管道是常见的两种 IPC 机制,但 Linux 也提供其他 IPC 机制。一般来说,Linux下的进程包含以下几个关键要素:有一段可执行程序;有专用的系统堆栈空间;内核中有它的控制块(进程控制块),描述进程所占用的资源,这样,进程才能接受内核的调度;具有独立的存储空间。,Linux 内核利用一个数据结构task_struct来代表一个进程,代表进程的数据结构指针形成了一个 task 数组(在Linux 中,任务和进程是两个相同的术语),这种指针数组有时也成为指针向量。这个
7、数组的大小默认为 512,表明在 Linux 系统中能够同时运行的进程最多可有 512。当建立新进程的时候,Linux 为新的进程分配一个 task_struct 结构,然后将指针保存在 task 数组中。task_struct 结构中包含了许多字段,按照字段功能,可分成如下几类:,(1)标识号。系统通过进程标识号唯一识别一个进程,但进程标识号并不是进程对应的 task_struct 结构指针在 task 数组中的索引号。另外,一个进程还有自己的用户和组标识号,系统通过这两个标识号判断进程对文件或设备的访问权。(2)状态信息。一个 Linux 进程可有如下几种状态:运行、等待、停止和僵死。(3
8、)调度信息。调度程序利用该信息完成进程之间的切换。(4)有关进程间通讯的信息。系统利用这一信息实现进程间的通讯。(5)进程链信息。在 Linux 系统中,除初始化进程之外,任何一个进程都具有父进程。每个进程都是从父进程中“克隆”出来的。进程链则包含进程的父进程指针、和该进程具有相同父进程的兄弟进程指针以及进程的子进程指针。另外,Linux 利用一个双向链表记录系统中所有的进程,这个双向链表的根就是 init 进程。利用这个链表中的信息,内核可以很容易地找到某个进程。,(6)时间和定时器。系统在这些字段中保存进程的建立时间,以及在其生命周期中所花费的 CPU 时间,这两个时间均以 jiffies
9、 为单位。该时间由两部分组成,一是进程在用户模式下花费的时间,二是进程在系统模式下花的时间。Linux 也支持和进程相关的定时器,应用程序可通过系统调用建立定时器,当定时器到期,操作系统会向该进程发送 sigalrm信号。(7)文件系统信息。进程可以打开文件系统中的文件,系统需要对这些文件进行跟踪。系统使用这类字段记录进程所打开的文件描述符信息。另外,还包含指向虚拟文件系统(Virtual File Systems,VFS)两个索引节点的指针,这两个索引节点分别是进程的主目录以及进程的当前目录。索引节点中有一个引用计数器,当有新的进程指向某个索引节点时,该索引节点的引用计数器会增加计数。未被引
10、用的索引节点的引用计数为 0,因此,当包含在某个目录中的文件正在运行时,就无法删除这一目录,因为这一目录的引用计数大于0。,(8)和进程相关的上下文信息。如前所述,进程可被看成是系统状态的集合,随着进程的运行,这一集合发生变化。进程上下文就是用来保存系统状态的 task_struct 字段。当调度程序将某个进程从运行状态切换到暂停状态时,会在上下文中保存当前的进程运行环境,包括 CPU 寄存器的值以及堆栈信息;当调度程序再次选择该进程运行时,则会从进程上下文信息中恢复进程的运行环境。,5.1.2 标识符信息,和所有的 Unix 系统一样,Linux 使用用户标识符和组标识符判断用户对文件和目录
11、的访问许可。Linux 系统中的所有文件或目录均具有所有者和许可属性,Linux 据此判断某个用户对文件的访问权限。,对一个进程而言,系统在 task_struct 结构中记录如表5.1 所示的4对标识符。,5.1.3 进程状态信息,Linux 中的进程有4种状态,如表5.2 所示。,5.1.4 文件信息,如图5.1 所示,系统中的每个进程有两个数据结构用于描述进程与文件相关的信息。其中,fs_struct 描述了上面提到的指向VFS 两个索引节点的指针,即 root 和 pwd。另外,这个结构还包含一个 umask 字段,它是进程创建文件时使用的默认模式,可通过系统调用修改这一默认模式。另一
12、个结构为files_struct,它描述了当前进程所使用的所有文件信息。从图中可以看出,每个进程能够同时拥有 256 个打开的文件,fs0 到 fs255 就是指向这些 file 结构的指针。文件的描述符实际就是 fs 指针数组的索引号。,图5.1 进程的文件信息,在 file 结构中,f_mode 是文件的打开模式,只读、只写或读写;f_pos 是文件的当前位置;f_inode 指向 VFS 中该文件的索引节点;f_op 包含了对该文件的操作例程集。利用 f_op,可以针对不同的文件定义不同的操作函数,例如一个用来向文件中写数据的函数。Linux 利用这一抽象机制,实现了管道这一进程间通讯机
13、制。这种抽象方法在 Linux 内核中很常见,通过这种方法,可使特定的内核对象具有类似 C+对象的多态性。Linux 进程启动时,有三个文件描述符被打开,它们是标准输入、标准输出和错误输出,分别对应 fs 数组的三个索引,即 0、1和2。如果启动时进行输入输出重定向,则这些文件描述符指向指定的文件而不是标准的终端输入/输出。每当进程打开一个文件时,就会利用files_struct 的一个空闲 file 指针指向打开的文件描述结构 file。对文件的访问通过 file 结构中定义的文件操作例程和虚拟文件系统(Virtual File System,VFS)的索引节点信息来完成。,5.1.5 虚拟
14、内存,进程的虚拟内存包含了进程所有的可执行代码和数据。运行某个程序时,系统要根据可执行映像中的信息,为进程代码和数据分配虚拟内存;进程在运行过程中,可能会通过系统调用动态申请虚拟内存或释放已分配的内存,新分配的虚拟内存必须和进程已有的虚拟地址链接起来才能使用。Linux 进程可以使用共享的程序库代码或数据,因此,共享库的代码和数据也需要链接到进程已有的虚拟地址中。Linux系统利用了需求分页机制来避免对物理内存的过分使用。因为进程可能会访问当前不在物理内存中的虚拟内存,这时操作系统将通过对处理器的页故障处理装入内存页。系统为此需要修改进程的页表,以便标志虚拟页是否在物理内存中,同时,Linux
15、 还需要知道进程地址空间中任何一个虚拟地址区域的来源和当前所在位置,以便能够装入物理内存。,图5.2 进程的虚拟内存示意,Linux 采用了比较复杂的数据结构跟踪进程的虚拟地址。在进程的 task_struct 结构中包含一个指向 mm_struct 结构的指针。进程的 mm_struct 则包含装入的可执行映像信息以及进程的页表指针。该结构还包含有指向 vm_area_struct 结构的几个指针,每个 vm_area_struct 代表进程的一个虚拟地址区域。图5.2 是某个进程的虚拟内存简化布局以及相应的进程数据结构。从图中可以看出,系统以虚拟内存地址降序排列 vm_area_struc
16、t。每个虚拟内存区域可能来源不同,有的可能来自映像,有的可能来自共享库,而有的则可能是动态分配的内存区。因此,Linux 利用了虚拟内存处理例程(vm_ops)来抽象对不同来源虚拟内存的处理方法。,在进程的运行过程中,Linux 要经常为进程分配虚拟地址区域,或者因为从交换文件中装入内存而修改虚拟地址信息,因此,vm_area_struct 结构的访问时间就成了性能的关键因素。除链表结构外,Linux 还利用 AVL(Adelson-Velskii and Landis)树组织 vm_area_struct。通过这种树结构,Linux 可以快速定位某个虚拟内存地址,但在该树中插入或删除节点需要
17、花费较多的时间。当进程利用系统调用动态分配内存时,Linux 首先分配一个 vm_area_struct 结构,并链接到进程的虚拟内存链表中,当后续的指令访问这一内存区域时,因为 Linux 尚未分配相应的物理内存,因此处理器在进行虚拟地址到物理地址的映射时会产生页故障,当 Linux 处理这一页故障时,就可以为新的虚拟内存区分配实际的物理内存。,5.1.6 时间和定时器,Linux 保存一个指向当前正在运行的进程的task_struct 结构的指针,即 current。每当产生一次实时时钟中断(又称时钟周期),Linux 就会更新 current 所指向的进程的时间信息,如果内核当前代表该进
18、程执行任务(例如进程调用系统调用时),那么系统就把进程在系统模式下花费的时间作为时间记录,否则将进程在用户模式下花费的时间作为时间记录。,除了为进程记录其消耗的 CPU 时间外,Linux 还支持和进程相关的间隔定时器。当定时器到期时,会向定时器的所属进程发送信号。进程可使用三种不同类型的定时器来给自己发送相应的信号,如表5.3 所示。,Linux 对 Virtual 和 Profile 定时器的处理是相同的,在每个时钟中断,定时器的计数值减 1,直到计数值为 0 时发送信号。Real 定时器的处理比较特殊。,5.1.7 关于线程,和进程概念紧密相关的概念是线程。线程可看成是进程中指令的不同执
19、行路线。例如,常见的字处理程序中,主线程处理用户输入,而其他并行运行的线程在必要时可在后台保存用户的文档。与进程相关的基本要素有:代码、数据、堆栈、文件 I/O和虚拟内存信息等,因此,系统对进程的处理要花费更多的开支,尤其在进行进程调度时。利用线程则可以通过共享这些基本要素而减轻系统开支,因此,线程也被称为“轻量级进程”。许多流行的多任务操作系统均支持线程。,线程有“用户线程”和“内核线程”之分。所谓用户线程是指不需要内核支持而在用户程序中实现的线程,这种线程甚至在象 DOS 这样的操作系统中也可实现,但线程的调度需要用户程序完成,类似于 Windows 3.x 的协作式多任务。另外一种则需要
20、内核的参与,由内核完成线程的调度。这两种模型各有其优缺点:用户线程不需要额外的内核开支,但是当一个线程因 I/O 而处于等待状态时,整个进程就会被调度程序切换为等待状态,其他线程得不到运行的机会;而内核线程则没有这个限制,但却占用了更多的系统开支。Linux 支持内核空间的多线程,读者也可以从 Internet 上下载一些用户级的线程库。,Linux 的内核线程和其他操作系统的内核实现不同。大多数操作系统单独定义线程,从而增加了内核和调度程序的复杂性;而 Linux 则将线程定义为“执行上下文”,实际只是进程的另外一个执行上下文而已。这样,Linux 内核只需区分进程,只需要一个进程/线程数组
21、,而调度程序仍然是进程的调度程序。Linux 的克隆(clone)系统调用可用来建立新的线程。,5.1.8 会话和进程组,在Unix系统中,父进程创建子进程,子进程可以再创建新进程,形成一定的层次,称为“进程组”。一个或多个进程可以合起来构成一个进程组(process group),一个或多个进程组可以合起来构成一个会话(session)。这样,就有了对进程进行批量操作的能力,例如通过向某个进程组发送信号以实现向该组中的每个进程发送信号。Linux 内核通过维护会话和进程组而管理多用户进程。如图5.3 所示,每个进程是一个进程组的成员,而每个进程组又是某个会话的成员。一般而言,当用户在某个终端
22、上登录时,一个新的会话就开始了。进程组由组中的领头进程标识,领头进程的进程标识符就是进程组的组标识符。类似地,每个会话也对应有一个领头进程。,同一会话中的进程通过该会话的领头进程和一个终端相连,该终端作为这个会话的控制终端。一个会话只能有一个控制终端,而一个控制终端只能控制一个会话。用户通过控制终端,可以向该控制终端所控制的会话中的进程发送键盘信号。同一会话中只能有一个前台进程组,属于前台进程组的进程可从控制终端获得输入,而其他进程均是后台进程,可能分属于不同的后台进程组。,图5.3 会话和进程、进程组,5.2 进程的创建和进程调度5.2.1 进程的创建,第一个进程在系统启动时创建,当系统启动
23、的时候它运行在核心态,这时,只有一个进程:初始化进程。象所有其他进程一样,初始进程有一组用堆栈、寄存器等等表示的机器状态。当系统中的其他进程创建和运行的时候这些信息存在初始进程的task_struct数据结构中。在系统初始化结束的时候,系统初始化结束时,初始进程启动一个内核线程init,而自己则处于空循环状态。当系统中没有可运行的进程时,调度程序会运行这个空闲的进程。这个空闲进程的task_struct是唯一的不是动态分配而是在核心连接的时候静态定义的,为了不至于混淆,叫做init_task。,init 内核线程/进程的标识号为 1,它是系统的第一个真正进程。它负责初始的系统设置工作,例如打开
24、控制台,挂装文件系统等。然后,init 进程执行系统的初始化程序,这一程序可能是/etc/init、/bin/init 或/sbin/init。init 程序将/etc/inittab 当作脚本文件建立系统中新的进程,这些新的进程又可以建立新进程。例如,getty 进程可建立 login 进程来接受用户的登录请求。,图5.4 父进程和子进程共享打开的文件,新的进程通过克隆旧的程序(当前程序)而建立。fork 和 clone 系统调用可用来建立新的进程。这两个系统调用结束时,内核在系统的物理内存中为新的进程分配新的 task_struct 结构,同时为新进程要使用的堆栈分配物理页。Linux 还
25、会为新的进程分配新的进程标识符。然后,新 task_struct 结构的地址保存在 task 数组中,而旧进程的 task_struct 结构内容被复制到新进程的 task_struct 结构中。在克隆进程时,Linux 允许两个进程共享相同的资源。可共享的资源包括文件、信号处理程序和虚拟内存等。当某个资源被共享时,该资源的引用计数值会增加 1,从而只有两个进程均终止时,内核才会释放这些资源。图5.4 说明了父进程和子进程共享打开的文件。,系统对进程虚拟内存的克隆过程则更加巧妙。新的 vm_area_struct 结构、新进程自己的 mm_struct 结构以及新进程的页表必须在一开始就准备好
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 进程 管理 通讯
链接地址:https://www.31ppt.com/p-6351075.html