83
第第第 UNIX 第第第第 第第第第第

第五讲 UNIX 进程调用 和进程存储

  • Upload
    sauda

  • View
    153

  • Download
    4

Embed Size (px)

DESCRIPTION

第五讲 UNIX 进程调用 和进程存储. 概述. UNIX 系统是分时多任务、多道程序环境系统。它采用时间片轮转转方式为系统中的多个用户的多道程序提供服务。为了跟踪计算机并行活动的状态及并发程序对资源的共享使用,提出了进程的概念。. 提纲. 1 进程基本概念 2 UNIX 系统中的进程 3 UNIX 系统进程调度和管理 4 存储管理基本 5 UNIX 进程的存储管理 6 UNIX 进程管理的系统调用. 1 进程的基本概念. 1 .进程与程序. ( a )程序:程序代码,静态 - PowerPoint PPT Presentation

Citation preview

Page 1: 第五讲  UNIX 进程调用 和进程存储

第五讲 UNIX 进程调用和进程存储

Page 2: 第五讲  UNIX 进程调用 和进程存储

概述 UNIX 系统是分时多任务、多道程序环境系统。它采用时间片轮转转方式为系统中的多个用户的多道程序提供服务。为了跟踪计算机并行活动的状态及并发程序对资源的共享使用,提出了进程的概念。

Page 3: 第五讲  UNIX 进程调用 和进程存储

提纲1 进程基本概念2 UNIX 系统中的进程3 UNIX 系统进程调度和管理4 存储管理基本5 UNIX 进程的存储管理6 UNIX 进程管理的系统调用

Page 4: 第五讲  UNIX 进程调用 和进程存储

1 进程的基本概念1 .进程与程序( a )程序:程序代码,静态 进程:可以看作某种特定任务的程序在一个数据集合上的一次具体的活动,动态。( b )进程与程序相比具有动态性、独立性、并发性等特点。

Page 5: 第五讲  UNIX 进程调用 和进程存储

1 进程的基本概念2 .进程的描述 进程的结构=进程描述信息 + 进程实体

PCB 程序段+数据结构集PCB: 包括进程的描述信息、控制信息及进程使用资源信息、处理器现场保护结构等,其中内容是进程动态特征的集中反映。 OS 通过 PCB 感知一个进程的存在。程序段:进程需要完成功能的程序代码。数据结构集:进程的程序执行时要完成功能的程序代码。

Page 6: 第五讲  UNIX 进程调用 和进程存储

1 进程的基本概念3 .进程的状态

非运行 运行

调度 进入 退出

暂停

两状态进程模式:

两状态进程模式中包含的进程状态转换过程:进程开始;调度运行;暂停运行;进程结束

Page 7: 第五讲  UNIX 进程调用 和进程存储

1 进程的基本概念五状态进程模式:

创建

阻塞

就绪 运行 退出

事件等待

超时调度

事件发生

提交

新建

释放

五状态进程模式中包含的进程状态转换过程: 创建新进程;进程提交;调度运行;释放;超时;事件等待;事件发生

Page 8: 第五讲  UNIX 进程调用 和进程存储

1 进程的基本概念4 .进程控制 主要实现进程状态的转换和并发进程的管理1 )原语 不可分解,不间断,不可并发执行的程序段2 )临界区 指共享某个资源时,不允许多个并发的程序交叉执行的一段代码。并发产生的原因:随机发生, 程序需要互不干涉。 3 )进程互斥 4 )信号量 描述共享资源使用情况的数据结构 5 )进程同步 有两种方式:互斥: 我在,你不能在 间接制约关系 同步: 你不来,我不能走 直接制约关系

Page 9: 第五讲  UNIX 进程调用 和进程存储

2 UNIX 中的进程 进程的引入使操作系统可以描述和掌握程序的动态执行过程 ,

传统的 UNIX 系统是基于进程管理的系统 , 虽然在现代 UNIX

系统中又增加了线程的概念 , 但进程仍然是操作系统管理的主要单元。与其他操作系统相比, UNIX 系统对进程的描述和管理具有自己的特色。

Page 10: 第五讲  UNIX 进程调用 和进程存储

2 UNIX 中的进程1 . UNIX 系统进程创建机制 Linux 系统的启动过程: 现假定系统已经完成了正常安装。打开计算机电源,计算机首先从固化在主板 ROM 中的 BIOS 开始启动, BOIS 对计算机的硬件进行一系列的检测,然后从指定设备的指定位置,把 boot loader读入系统内存并把控制权转交给 boot loader ,接着,在 boot loader 的控制下,系统启动代码被读入内存并进行初始化工作,控制权转交给系统初始化代码后,引导整个操作系统进入内存并控制整个系统,设置各种表格和数据结构,初始化可运行队列的时候建立系统的 0 号进程,然后创建系统最初的进程—— init 进程,该进程的进程号为 1 。

Page 11: 第五讲  UNIX 进程调用 和进程存储

2 UNIX 中的进程1 . UNIX 系统进程创建机制 init进程启动内核交换线程等系统内核线程,然后根据系统提供的参数,启动相应的终端管理进程,在每一个终端屏幕上显示 login 字样,等待用户的登录,整个启动过程到此结束,参看图 5.1 。 用户登录过程中, init进程启动 login进程对用户的账号和密码进行验证,通过之后,由 login进程启动 shell 命令解释进程,为用户提供操作系统的接口,接受用户的输入,解释执行用户命令,执行过程中又会创建新的进程。

Page 12: 第五讲  UNIX 进程调用 和进程存储

2 UNIX 中的进程

:核心程序创建0号进程

图 5.1 Linux系统启动过程

Page 13: 第五讲  UNIX 进程调用 和进程存储

2 UNIX 中的进程1 . UNIX 系统进程创建机制

Linux系统的所有进程共同构成一个完整的进程树从 init进程开始, init进程是所有其他进程的祖先。 init产生终端管理进程mingetty,mingetty产生 login, login产生用户的 shell进程,然后 shell产生其他用户进程, 因此,其他所有进程都是由init 或者它的子孙创建而来。 同样,在进程结束之后,父进程也要负责该进程的最后回收工作,如果某一个进程创建了子进程之后,由于某种原因先于子进程终止,由它创建的子进程成为孤儿进程,孤儿进程的祖父进程就要负责回收工作,依此类推。最后,在系统要关机之前, init进程还要负责结束所有的进程,卸载所有文件系统并终止处理器的指令执行。

Page 14: 第五讲  UNIX 进程调用 和进程存储

2 UNIX 中的进程1 . UNIX 系统进程创建机制

Linux源代码树中 arch/i386/boot 下的这几个文件: bootsect.S、 setup.S、 video.S等。

其中 bootsect.S是生成引导扇区的汇编源码,它完成加载动作后直接跳转到 setup.S的程序入口。 setup.S的主要功能就是将系统参数(包括内存、磁盘等,由BIOS返回)拷贝到特别内存中,以便以后这些参数被保护模式下的代码来读取 此外, setup.S 还将 video.S中的代码包含进来,检测和设置显示器和显示模式。最后, setup.S 将系统转换到保护模式,并跳转到 0x100000。

Page 15: 第五讲  UNIX 进程调用 和进程存储

2 UNIX 中的进程1 . UNIX 系统进程创建机制

0x100000 这个内存地址存放的是解压后的内核。当CPU跳到 0x100000时,将执行 "arch/i386/kernel/head.S"中的 startup_32 ,它也是 vmlinux的入口,然后就跳转到 start_kernel()中去了。 start_kernel()是 "init/main.c"中的定义的函数, start_kernel()中调用了一系列初始化函数,以完成 kernel本身的设置。 start_kernel() 函数中,做了大量的工作来建立基本的Linux 核心环境。如果顺利执行完 start_kernel(),则基本的 Linux 核心环境已经建立起来了。

Page 16: 第五讲  UNIX 进程调用 和进程存储

2 UNIX 中的进程1 . UNIX 系统进程创建机制

在 start_kernel()的最后,通过调用 init() 函数,系统创建第一个核心线程,启动了 init过程。核心线程 init()主要是来进行一些外设初始化的工作的,包括调用 do_basic_setup()完成外设及其驱动程序的加载和初始化。并完成文件系统初始化和 root 文件系统的安装。此外,当do_basic_setup() 函数返回 init(), init()又打开了 /dev/console设备,重定向三个标准的输入输出文件 stdin、 stdout和 stderr 到控制台,最后,搜索文件系统中的 init程序(或者由 init= 命令行参数指定的程序),并使用 execve()系统调用加载执行 init程序。到此 init() 函数结束,内核的引导部分也到此结束了。

Page 17: 第五讲  UNIX 进程调用 和进程存储

2 UNIX 中的进程1 . UNIX 系统进程创建机制

init程序需要读取配置文件 /etc/inittab。 inittab是一个不可执行的文本文件,它有若干行指令所组成。在 init的配置文件中有这么一行: si::sysinit:/etc/rc.d/rc.sysinit它调用执行了 /etc/rc.d/rc.sysinit。 rc.sysinit是一个 bash shell的脚本,它主要是完成一些系统初始化的工作, rc.sysinit是每一个运行级别都要首先运行的重要脚本。

Page 18: 第五讲  UNIX 进程调用 和进程存储

2 UNIX 中的进程1 . UNIX 系统进程创建机制

在 rc.sysinit执行后,将返回 init 继续其它的动作,通常接下来会执行到 /etc/rc.d/rc程序。以运行级别5为例l5:5:wait:/etc/rc.d/rc 5  这一行表示以 5 为参数运行 /etc/rc.d/rc, /etc/rc.d/rc是一个 Shell 脚本,它接受 5 作为参数,去执行 /etc/rc.d/rc5.d/ 目录下的所有的rc启动脚本, /etc/rc.d/rc5.d/ 目录中的这些启动脚本实际上都是一些链接文件,而不是真正的 rc启动脚本,真正的 rc启动脚本实际上都是放在 /etc/rc.d/init.d/ 目录下。而这些 rc启动脚本有着类似的用法,它们一般能接受 start、 stop、 restart、 status等参数。 init在等待 /etc/rc.d/rc执行完毕之后,将在指定的各个虚拟终端上运行 /sbin/mingetty,等待用户的登录。

Page 19: 第五讲  UNIX 进程调用 和进程存储

2 UNIX 中的进程1 . UNIX 系统进程创建机制

Linux的账号验证程序是 login, login 会接收mingetty传来的用户名作为用户名参数。 login 将搜索 /etc/passwd以及 /etc/shadow来验证密码以及设置账户的其它信息。login程序成功后,会向对应的终端再输出最近一次登录的信息 (在 /var/log/lastlog中有记录 ),并检查用户是否有新邮件 (在 /usr/spool/mail/的对应用户名目录下 )。然后开始设置各种环境变量:对于 bash来说,系统首先寻找 /etc/profile 脚本文件,并执行它;然后如果用户的主目录中存在 .bash_profile 文件,就执行它,在这些文件中又可能调用了其它配置文件,所有的配置文件执行后,会出现命令行提示符,到此整个启动过程就结束了。

Page 20: 第五讲  UNIX 进程调用 和进程存储

2 UNIX 中的进程2 . UNIX 中进程的描述 运行中的每个进程都有一个独立的运行环境,这个环境是进程生存的基础,即进程上下文 。 UNIX 系统进程上下文包含的内容有: proc 结构和 user 结构用户栈和系统栈正文段和数据段其中 proc 结构是关联进程上下文中的核心模块,它是 UNIX 系统进程管理的关键数据结构,它与其他模块间构成的关联关系如图 5.2所示。

Page 21: 第五讲  UNIX 进程调用 和进程存储

2 UNIX 中的进程 Proc

User

系 统 栈

用 户 栈

数 据 段

正 文 段

图 5.2 UNIX 进程上下文结构图

Page 22: 第五讲  UNIX 进程调用 和进程存储

2 UNIX 中的进程2 . UNIX 中进程的描述 不同版本的 UNIX 系统其进程上下文数据结构描述可能略有不同,但它们的基本内容和数据结构在系统中的作用是一致的。其中 proc 和 user 结构形成了操作系统管理中的进程控制块 PCB 。 进程的正文段中包含的是进程的程序代码和使用常量,它是可以被多哥进程访问的共享区域。 数据段中包含的是进程的私有信息,是用户态进程访问的区域。 系统栈是进程在系统态执行时完成子程序嵌套和中断处理时使用的信息保留,它只能被系统态进程所使用。 用户栈的作用与系统栈类似,只是它中间包含的内容是进程在用户态执行时子程序嵌套和中断处理时使用的信息。

Page 23: 第五讲  UNIX 进程调用 和进程存储

2 UNIX 中的进程2 . UNIX 中进程的描述 proc 结构: 为了对系统中的进程进行管理,在 UNIX 系统中设立了进程管理表以记载进程的基本情况。 proc 结构中保存的被容就是每个进程在系统进程管理表中的登记信息。因为进程表是常驻内存的,所以 proc 结构也经驻内存。proc 包括的内容,比如: 进程的状态;进程的用户状态信息;进程的标识符;进程在存储区中的位置和大小;进程的调度参数;软中断信号项;进程执行时间和系统资源使用情况; user 结构存放的起始地址;进程页表指针;进程被挂起时事件描述符的集合

Page 24: 第五讲  UNIX 进程调用 和进程存储

2 UNIX 中的进程2 . UNIX 中进程的描述 user 结构: 进程的 user 结构是对进程内部特征作进一步描述的信息。 user 结构中包含的是进程私有信息及数据, unix 系统规定这部分信息不常驻内存,只有当进程被执行时才调入内存。其中主要的内容包括: 指向 proc 结构的指针;当前系统调用;与用户标识有关的数据项;一些相关的输入、输出项;进程打开文件的描述符;中断、软中断参数;出错信息;进程各段的长度;文件权限屏蔽项;进程交互的有关数据;

Page 25: 第五讲  UNIX 进程调用 和进程存储

2 UNIX 中的进程2 . UNIX 中进程的描述 寄存器的存储机构: 进程运行时需要保存的常用寄存器的内容有: 程序计数器 PC 的内容;处理机状态寄存器 PSW;栈指针 SP 内容;通用寄存器中的内容; 进程包含的段: 共享正文段;数据段;栈段

Page 26: 第五讲  UNIX 进程调用 和进程存储

3 UNIX 进程调度和管理 进程调度和管理的主要任务与需要解决的关键问题:任务:在一个多个进程并行运行的并行环境中,如何合理、有效的管理这些进程,使它们能够顺利、并行的运行;需要解决的关键问题:在进程生命期内,进程的调度必会及到进程状态的改变,进程状态应当有哪些?调度的条件?调度的策略?调度的时机?调度的具体完成?如何给这些进程合理地分配资源?包括存储资源、处理力资源

Page 27: 第五讲  UNIX 进程调用 和进程存储

3 UNIX 进程调度和管理 3.1UNIX 进程状态及其转换: 在 UNIX 中,一个进程从被激活到执行完以后被注销,它 的生命周期中会出现多种状态,这些状态的描述信息都记录在 proc结构中,随着进程状态的改变 proc 结构中的这些数据项也会改变,UNIX 系统可以随时掌握该进程运行的状况。 进程的状态转换过程是通过系统中一系列操作原语或系统的核心函数完成的。

Page 28: 第五讲  UNIX 进程调用 和进程存储

3 UNIX 进程调度和管理 LINUX 进程状态转换:

Page 29: 第五讲  UNIX 进程调用 和进程存储

3 UNIX 进程调度和管理 LINUX 进程状态转换:运行状态( TASK_RUNNING) :当进程正在被CPU 执行,或已经准备就绪随时可由调度程序执行,则称该进程为处于运行状态( running)。进程可以在内核态运行,也可以在用户态运行。就绪状态:当系统资源已经可用时,进程就被唤醒而进入准备运行状态,该状态称为就绪态。这些状态(图中中间一列)在内核中表示方法相同,都被称为处于 TASK_RUNNING状态。 可中断睡眠状态( TASK_INTERRUPTIBLE)当进程处于可中断等待状态时,系统不会调度该进行执行。当系统产生一个中断或者释放了进程正在等待的资源,或者进程收到一个信号,都可以唤醒进程转换到就绪状态(运行状态)。不可中断睡眠状态( TASK_UNINTERRUPTIBLE)与可中断睡眠状态类似。但处于该状态的进程只有被使用wake_up()函数明确唤醒时才能转换到可运行的就绪状态。

Page 30: 第五讲  UNIX 进程调用 和进程存储

3 UNIX 进程调度和管理 LINUX 进程状态转换:暂停状态( TASK_STOPPED)当进程收到信号 SIGSTOP、 SIGTSTP、 SIGTTIN或SIGTTOU 时就会进入暂停状态。可向其发送 SIGCONT信号让进程转换到可运行状态。僵死状态( TASK_ZOMBIE)当进程已停止运行,但其父进程还没有询问其状态时,则称该进程处于僵死状态。

Page 31: 第五讲  UNIX 进程调用 和进程存储

3 UNIX 进程调度和管理 3.2 UNIX 进程调度算方法与策略 操作系统中管理进程状态转换的模块被称为“进程调度程序”。在 UNIX 系统中完成进程状态转换的程序是 0 号进程,主要负责进程的调度及进程在内外存交换中的有关操作。例如:对参与竞争处理器并且已具备执行条件的进程进行分析和裁决对选中的进程做处理器控制权转交管理进程运行中各种状态的转换完成进程在系统内外存之间的交换

Page 32: 第五讲  UNIX 进程调用 和进程存储

3 UNIX 进程调度和管理 3.2 LINUX 进程调度算方法与策略 调度时机 调度函数是 Linux 系统中执行最为频繁的一个,它的主要目标就是选择合适的进程占有 CPU ,实现程序的多道执行,充分提高 CPU 的利用率。调度函数通过对每个进程 PCB 中相关信息查询,按照一定的算法,在可运行队列选择进程。如果选中的进程并不是当前占有处理机的进程,调度函数还负责保存当前进程的执行现场(进程上下文),然后恢复选中进程的进程上下文使之顺利执行。 Linux 系统中没有设置专门的调度进程,在需要调度的时候,调用一个特定的调度函数来完成调度功能。一般来讲, Linux 系统中的进程调度发生的时机有下面几种。

Page 33: 第五讲  UNIX 进程调用 和进程存储

3 UNIX 进程调度和管理 可以归结为两类时机, 一是进程本身自动放弃处理机发生调度,这包括进程转换到等待和僵死状态,这类调度是用户进程可以预测的; 比如:当正在执行的进程申请某种暂时无法获得的资源进程转入等待状态; 当正在执行的进程完成了任务或者得到特定的信号而退出,转入僵死状态;这两种进程状态转换完成后,进程主动放弃 CPU 资源,调用调度函数,选择新的进程来使用 CPU 。 二是由核心态转入用户态时发生调度,这类调度发生最为频繁。系统调用完成和内核处理完中断之后系统是由核心态转入用户态,

Page 34: 第五讲  UNIX 进程调用 和进程存储

3 UNIX 进程调度和管理 3.2 LINUX 进程调度算方法与策略 调度算法 Linux系统采用相当简单但效率很高的调度算法,具有很好的响应特性,它提供了三种调度算法: POSIX 操作系统标准规定的用于实时进程的先进先出算法( FIFO, First In First Out)和轮转算法( RR, Round Robin),用于普通进程的可抢占式动态优先级算法( Preemptive Scheduling)。

Page 35: 第五讲  UNIX 进程调用 和进程存储

3 UNIX 进程调度和管理 3.2 LINUX 进程调度算方法与策略 Linux系统中,非实时进程的调度采用抢占式动态优先级算法。每个进程拥有不变的静态优先级和可变的动态优先级,调度函数根据各进程的优先级来确定权值,拥有最大权值的进程被选中执行,如果多个进程具有相同权值,则选取排在可运行队列最前面的那一个。每一个进程都分配一定的时间片,时间片使用完之后,系统转入调度函数,等所有非实时进程的时间片都使用完之后,再按照各自的优先级给每个进程重新分配时间片。

Page 36: 第五讲  UNIX 进程调用 和进程存储

3 UNIX 进程调度和管理 UNIX SYSTEM V

动态优先级多级反馈循环调度法公式: 2 .计算公式P-pri = P-cpu + PUSER ( 25 )+ P-nice + NZERO ( 20 ) 优点: 1 )创建新进程, cpu = 0 , P-pri值较小,优先级较大 2 )使用越多, P-cpu越大, P-pri值较大,优先级较小 3 )使用越少, P-cpu越小, P-pri值较小,优先级较大

Page 37: 第五讲  UNIX 进程调用 和进程存储

3 UNIX 进程调度和管理 调度过程:Linux系统的调度过程简洁而高效。整个调度过程大概可以分为五个部分。首先检测中断,如果有中断运行时,调度过程到此为止,直接退出,如果没有中断运行,关中断,在调度的过程中将不再允许中断。其次处理系统的内核例程。然后对当前进程做相关处理:如果当前进程是时间片用完的进程按照轮转法调度,系统重新赋予时间片并把它移到队列的尾部;如果进程因为等待某个事件而转入等待状态引起调度,调度过程中发现事件已经发生,进程仍然转入就绪状态;如果进程处于其他非可运行态的话,就要从可运行队列中删除。这些都是为开始调度而进行的准备工作。接着调度函数遍历整个可运行队列,从中找到最值得运行的进程,该进程的权值( goodness)最大。如果该进程不是当前进程,系统需要进行进程上下文切换,如果正好就是当前进程,这一部分就可以跳过去。最后打开中断,选中的进程开始执行。

Page 38: 第五讲  UNIX 进程调用 和进程存储

4 进程存储管理

存储管理的主要功能:( 1 )存储空间的分配,回收;( 2 )地址变换;( 3 )存储共享和保护;( 4 )存储器扩充( 5 )提高主存的利用率

计算机的存储结构图: 寄存器

高速缓存

主存

辅存

CPU

CPU内

系统内存

硬盘,光盘,磁带

访问速度

存储空间

单位价格

Page 39: 第五讲  UNIX 进程调用 和进程存储

4.1 进程存储技术 程序以进程的方式存放于内存中, CPU 在内存中实现进程之间的切换调度。采用一些策略和算法来分配存储空间,使进程在内存和磁盘之间来回切换。此过程为进程的存储管理过程。

1 .连续分配存储技术 操作系统为运行的进程分配一个连续的内存空间 (a) 单一连续分配存储方式:

系统区

用户区 为一个进程分配,适用了单用户单任务的系统管理,出现在较早时期

Page 40: 第五讲  UNIX 进程调用 和进程存储

4.1 进程存储技术

系统区

用户区 1 可以连续放多个进程,实现进程的并发,但是有碎片产生,可以利用内存紧缩技术

用户区 2

…..

1 .连续分配存储技术 操作系统为运行的进程分配一个连续的内存空间 (b) 分区式连续存储方式:

Page 41: 第五讲  UNIX 进程调用 和进程存储

4.1 进程存储技术 ( 1 )覆盖 目的: 在较小的可用内存中运行较大的程序 原理: 一个程序的几个代码段或数据段按照使用的先后顺序以覆盖方式占用共享内存区域。 原则: 将程序中必要的数据放在常驻内存中。将不经常用的放在交换区。将不存在调用关系的模块可以采用覆盖方式共享内存分区。( 2 )交换:在多个程序并发执行的时候,将暂时不执行的代码放入交换区,从而可以用更大的内存空间装入新的进程。

2 .覆盖和交换技术

Page 42: 第五讲  UNIX 进程调用 和进程存储

4.1 进程存储技术 3 .页式存储管理 解决内存碎片,提高内存的利用率 1 )基本思想:进程逻辑空间划分为若干页,内存划分为若干物理页。进程按照页的大小分配内存空间,可以连续或不连续分配。

页号 页面号0 5

1 76

2 12

静态页式管理:进程在执行前将程序段和数据段一次性装入内存的各个页面中。数据结构: 进程页表:每个进程都有进程页表,逻辑页号对应物理页面号

Page 43: 第五讲  UNIX 进程调用 和进程存储

4.1 进程存储技术 请求表:系统有一个请求表,可以放到 PCB 描述中

进程号 请求数 页表地址 页表长度 状态1 20 1022 20 已分配  用于地址转换  

存储页面表: 系统有一张,用于描述内存各个页面的分配情况动态页式管理:只需要将执行部分调入(缺页中断则调入),动态管理程序各部分在内存中的调入调出。

Page 44: 第五讲  UNIX 进程调用 和进程存储

4.1 进程存储技术 2 )页式管理的地址变换

页表长度 页表地址

控制寄存器

8

有效地址

逻辑页号 物理页号

0 2

1 3

2 8 1C4

2 1C4(页内地址)

物理地址

Page 45: 第五讲  UNIX 进程调用 和进程存储

4.1 进程存储技术 4 .段式存储管理 1 )将内存分为不同的分区,视为二维空间,进程按照逻辑内容分段。 2 )特点:以段为单位分配内存分区,段可以不连续。每个段可以占用一个内存分区。内存管理采用动态分区方法,采用缺段中断处理。 3 )数据结构:进程段表

Page 46: 第五讲  UNIX 进程调用 和进程存储

4.2 虚拟存储技术 1 .局部性原理 程序执行过程中,在较短的时期内,所执行的指令地址以及操作数地址分别局限在一个区域内。 时间局限性: 当前指令执行与下条指令执行,数据的当前访问和下次访问集中在一个时间段中。 空间局部性: 当前访问指令和临近访问的指令,当前访问的数据和临近访问的数据集中在一个较小区域内。 原因:大部分程序在空间上顺序执行,在时间上循环执行

Page 47: 第五讲  UNIX 进程调用 和进程存储

4.2 虚拟存储技术 2 .虚存的原理 只需将当前需要执行的部分页和部分段读入到内存中就可让程序开始执行。利用缺页和缺段请求,动态实现存储管理。 3 .特点:存储空间变大 内存+外存, 支持程序的并发执行 , 采用部分交换。 4 .虚拟存储器的种类 1 )页式虚存 2 )段式虚存 3 )段页式虚存

Page 48: 第五讲  UNIX 进程调用 和进程存储

5 UNIX 进程存储管理 1 .交换策略 早期 内存和交换区之间传送整个进程 费时,影响效率 现代 部分交换 关键信息, PCB 不参与交换,常驻内存。 0 号进程负责调度和交换工作,无限循环的执行。 数据结构 映射图来管理交换设备的空间资源,包含可分配的资源地址及该地址可用的资源单位。

UNIX 系统采用交换和请求调页两种策略完成存储管理

Page 49: 第五讲  UNIX 进程调用 和进程存储

5 UNIX 进程存储管理 2 .请求调页策略 内存分页 外存分页 进程运行时系统只在内存中保留当前活动进程的某些页面,而不放入整个进程页面 缺页中断 虚存技术不受空间限制 为了提高运行效率,核心进程提供了一个工作区,存放内存中最近被访问过的页面集合。 3 . UNIX 系统中进程存储管理结构  Proc + User = PCB

放内存中 放外存中

Page 50: 第五讲  UNIX 进程调用 和进程存储

8 UNIX 进程管理的系统调用parent

child child child

parent

fork

exec

exit

wait

Page 51: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 6.1 进程创建

include <unistd.h>int fork(void);

fork 函数创建一新进程。UNIX 系统在实现 fork()调用完成的主要工作 :1)为子进程在进程表中分配一个空闲的 proc结构2)赋给子进程一个唯一的进程标识符pid3)复制一个父进程上下文的逻辑副本4 )增加与父进程相关联的文件表和索引节点表的引用次数5 )创建成功对父进程返回子进程的 pid.对子进程返回 0.创建不成功返回 -1

Page 52: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 include <unistd.h> int getpid(void); int getppid(void);

getpid 函数返回调用进程的进程 IDgetppid 函数则返回调用进程的父进程 ID。

Page 53: 第五讲  UNIX 进程调用 和进程存储

8 UNIX 进程管理的系统调用 #include <stdio.h>#include <unistd.h>main(){ int pid; pid=fork( ); if (pid <0) { printf ("fork failed!\n"); exit(1);} if( pid == 0) {printf("child process: pid is %d\n",pid); printf ("It is child process, pid is %d\n", getpid ());} else {printf("parent process:pid is %d\n",pid); printf("It is parent process pid is %d\n", getpid());} exit( 0 );}

Page 54: 第五讲  UNIX 进程调用 和进程存储

8 UNIX 进程管理的系统调用

Page 55: 第五讲  UNIX 进程调用 和进程存储

8 UNIX 进程管理的系统调用 # include <iostream.h># include <unistd.h>int main(){ int pid; pid=fork( ); if (pid <0) { cout <<"fork failed!\n"; exit(1);} if( pid == 0) {cout << "child process: pid is" << pid << "\n"; cout << "It is child process, pid is" << getpid () << "\n";} else {cout << "parent process: pid is" << pid <<"\n"; cout << "It is parent process, pid is" << getpid () << "\n";} exit( 0 );}

Page 56: 第五讲  UNIX 进程调用 和进程存储

8 UNIX 进程管理的系统调用

Page 57: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 6.1 进程退出

include <stdlib.h>void exit(int code);

用 exit() 函数可以在退出程序并将控制权返回给操作系统 . exit(int code)中的 code是子进程交给父进程的退出码 ,父进程由此可以判断子进程的工作状态 . 0 表示正常退出,非 0 表示非正常退出。

Page 58: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 6.1 进程退出用 exit() 函数可以在退出程序并将控制权返回给操作系统,而用 return语句可以从一个函数中返回并将控制权返回给调用该函数的函数。

Page 59: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 6.1 进程退出

include <unistd.h>void _exit(int status);

_exit() 函数的作用最为简单:直接使进程停止运行,清除其使用的内存空间,并销毁其在内核中的各种数据结构; exit() 函数则在这些基础上作了一些包装,在执行退出之前加了若干道工序,也是因为这个原因,有些人认为 exit已经不能算是纯粹的系统调用。

exit() 函数与 _exit() 函数最大的区别就在于 exit() 函数在调用 exit系统调用之前要检查文件的打开情况,把文件缓冲区中的内容写回文件,就是图中的 "清理 I/O缓冲 "一项。

Page 60: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 6.1 进程退出

在 Linux的标准函数库中,有一套称作 "高级 I/O"的函数,我们熟知的 printf()、 fopen()、 fread()、 fwrite() 都在此列,它们也被称作 "缓冲 I/O( buffered I/O) ",其特征是对应每一个打开的文件,在内存中都有一片缓冲区,每次读文件时,会多读出若干条记录,这样下次读文件时就可以直接从内存的缓冲区中读取,每次写文件的时候,也仅仅是写入内存中的缓冲区,等满足了一定的条件(达到一定数量,或遇到特定字符,如换行符和文件结束符 EOF),再将缓冲区中的内容一次性写入文件,这样就大大增加了文件读写的速度,但也为我们编程带来了一点点麻烦。如果有一些数据,我们认为已经写入了文件,实际上因为没有满足特定的条件,它们还只是保存在缓冲区内,这时我们用 _exit() 函数直接将进程关闭,缓冲区中的数据就会丢失,反之,如果想保证数据的完整性,就一定要使用 exit() 函数。

Page 61: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 6.2 执行一个新程序include <unistd.h>external char **environ;int execl(const char *path,const char *arg0,…,);int execlp(const char *file,const char *arg0,…);int execle(const char *path,const char *arg0,…), const char *envp[]);int execv(const char *path,const char *argv[]);int execvp(const char *file,const char *argv[]);int execve(const char *path,const char *argv[], const char *envp[]);

这 6 个函数具有相同的功能,它们都能用新程序的程序映像覆盖进程原来的程序映像。新程序文件名由参数 path 或file 给出,它的程序代码将替代原来的程序代码被执行。

Page 62: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 #include <unistd.h>#include <stdlib.h>#include “err_exit.h”int main(void){ pid_t pid; if((pid = fork( ))<0) err_exit(“fork error”); else if (pid == 0) { if( execlpexeclp(“/demo.exe”,“myarg1”, “MYARG2”,(char*)0)<0) err_exit(“execle error”); } exit(EXIT_SUCCESS);}

Page 63: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 char *arg[]={“file1”,“file2”,0};

Char *env[]={“PATH=/home/usr”,”TERM=console”,0}

int main(void)

{

execl(“/bin/mycat”,”file1”,”file2”,(char*)argv[0]);

execlp(“mycat”, ”file1”,”file2”,(char*)argv[0]);

execle(“/bin/mycat”,”file1”,”file2”,(char*)argv[0], env); execv(“/bin/mycat”,arg); execvp(“mycat”, arg); execvle(“/bin/mycat”,arg, env);

}

Page 64: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 6.3 等待进程完成

include <sys/wait.h>int wait(int *stat_loc);int waitpid(int pid, int *stat_loc, int options);

通常,父进程用 fork派生了一个子进程后,常常会需要等待子进程执行完后才能继续执行。 UNIX 提供了两个函数来等待子进程的结束

Page 65: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 6.3 等待进程完成#include<sys/types.h>#include<sys/wait.h>函数定义: int wait (int * status);函数说明: wait() 会暂时停止目前进程的执行 ,直到有信号来到或子进程结束。如果在调用 wait()时子进程已经结束 ,则 wait() 会立即返回子进程结束状态值。子进程的结束状态值会由参数 status 返回 , 而子进程的进程识别码也会一起返回。如果不在意结束状态值 ,则参数 status 可以设成 NULL。 如果执行成功则返回子进程识别码 (PID),如果有错误发生则返回返回值 -1 。失败原因存于 errno 中。

Page 66: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 #include<sys/types.h>#include<sys/wait.h>#include<stdio.h>#include<stdlib.h>int main(void){ int pid,tmp; int status; if( (pid = fork()) < 0 ) printf("call fork() failed!\n"); if (pid ==0) {printf("child process %d finished!\n",getpid( )); exit(1);} else {tmp=wait(&status); printf ("the tmppid is %d\n",tmp); if ( tmp!= pid ) printf("call wait() failed!\n"); else printf( "parent process %d finished!\n",getpid());}}

Page 67: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制

Page 68: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 #include<sys/types.h>#include<sys/wait.h>#include<stdio.h>#include<stdlib.h>int main(void){ int pid,tmp; int status; if( (pid = fork()) < 0 ) printf("call fork() failed!\n"); if (pid ==0) {printf("child process %d finished!\n",getpid( )); exit(1);} else {/*tmp=wait(&status); printf ("the tmppid is %d\n",tmp); if ( tmp!= pid ) printf("call wait() failed!\n"); else*/ printf( "parent process %d finished!\n",getpid());}}

Page 69: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制

Page 70: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 6.3 等待进程完成#include<sys/types.h>#include<sys/wait.h>函数定义: int waitpid(pid_t pid,int * status,int options);函数说明: waitpid() 会暂时停止目前进程的执行 ,直到有信号来到或子进程 结束。如果在调用 waitpid()时子进程已经结束 ,则 waitpid() 会立即返回子进程结束状态值。 子进程的结束状态值会由参数 status 返回 , 而子进程的进程识别码也会一快返回。如果不在意结束状态值 ,则参数 status 可以设成 NULL。参数 pid 为欲等待的子进程识别码 , 其他数值意义如下 : pid<-1 等待进程组识别码为 pid 绝对值的任何子进程。 pid=-1 等待任何子进程 ,相当于 wait()。 pid=0 等待进程组识别码与目前进程相同的任何子进程。 pid>0 等待任何子进程识别码为 pid 的子进程。如果执行成功则返回子进程识别码 (PID),如果有错误发生则返回返回值 -1 。失败原因存于 errno 中。

Page 71: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 6.3 等待进程完成

参数 option 可以为 0 或下面的 OR 组合 :WNOHANG 如果没有任何已经结束的子进程则马上返回 , 不予以等待。WUNTRACED 如果子进程进入暂停执行情况则马上返回 ,但结束状态不予以理会。子进程的结束状态返回后存于 status, 底下有几个宏可判别结束情况 :WIFEXITED(status) 如果子进程正常结束则为非 0 值。WEXITSTATUS(status) 取得子进程 exit() 返回的结束代码 ,一般会先用 WIFEXITED 来判断是否正常结束才能使用此宏。WIFSIGNALED(status) 如果子进程是因为信号而结束则此宏值为真WTERMSIG(status) 取得子进程因信号而中止的信号代码 ,一般会先用 WIFSIGNALED 来判断后才使用此宏。WIFSTOPPED(status) 如果子进程处于暂停执行情况则此宏值为真。一般只有使用 WUNTRACED 时才会有此情况。WSTOPSIG(status) 取得引发子进程暂停的信号代码 ,一般会先用 WIFSTOPPED 来判断后才使用此宏。

Page 72: 第五讲  UNIX 进程调用 和进程存储

#include<sys/types.h>#include<sys/wait.h>#include<stdio.h>#include<stdlib.h>int main(void){ int pid1,pid2,tmp; int status; pid1 = fork(); if (pid1 ==0) {printf("I am the first child process!\n"); exit(1);} else {pid2=fork( ); if (pid2==0) {printf("I am the second child process!\n"); exit(2);} else {printf("I am the parent process!\n"); waitpid(-1,NULL,0 ); printf("the parent process finished!\n");} }}

Page 73: 第五讲  UNIX 进程调用 和进程存储
Page 74: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 6.4 进程终止与僵死进程

1 、正常终止( a)从main 函数内执行 return。这相当于调用 exit。( b)调用 exit 函数。( c)调用 _exit 函数,该函数由 exit调用并处理与 UNIX相关的特定细节。

Page 75: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 6.4 进程终止与僵死进程

2 、异常终止( a)调用 abort 函数。( c)当进程收到某种信号时。

Page 76: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 6.4 进程终止与僵死进程

3.僵死进程在一个进程调用了 exit 之后,该进程并非马上就消失掉,而是留下一个称为僵尸进程( Zombie)的数据结构。在 Linux进程的 5种状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。 当一个进程已退出,但其父进程还没有调用系统调用wait(稍后介绍)对其进行收集之前的这段时间里,它会一直保持僵尸状态,

Page 77: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 例 2int main(void)

{ pid_t pid;

int n;

printf(“fork program starting\n”);

pid = fork(); switch(pid) { case 0: exit(0); default: sleep(60000); // 在父进程 sleep期间,子进程为僵死状态 wait(&n); //僵死的子进程被释放 } exit(EXIT_SUCESS);}

Page 78: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 # ps –alS UID PID PPID TIME COMDR 181 2081 1872 0:00 ps S 181 2074 1872 0:00 testZ 181 2080 2074 0:00 <defunct>S 181 2041 1860 0:00 ftp 状态 Z表示该进程为僵死进程,该进程的父进程 PPID为 2074 ,其父进程 test仍然在运行D 不可中断睡眠 (通常是在 IO操作 )R 正在运行或可运行(在运行队列排队中)S 可中断睡眠 (在等待某事件完成 )T Stopped, either by a job control signal or because it is being traced.W 正在换页 (2.6.内核之前有效 )X 死进程 (should never be seen)Z 僵尸

Page 79: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 6.4 进程终止与僵死进程

系统调用 exit的作用是使进程退出,但也仅仅限于将一个正常的进程变成一个僵尸进程,并不能将其完全销毁。僵尸进程虽然对其他进程几乎没有什么影响,不占用 CPU 时间,消耗的内存也几乎可以忽略不计,但是 Linux系统中进程数目是有限制的,在一些特殊的情况下,如果存在太多的僵尸进程,也会影响到新进程的产生。那么,我们该如何来消灭这些僵尸进程呢? 僵尸进程中保存着很多对程序员和系统管理员非常重要的信息,首先,这个进程是怎么死亡的?是正常退出呢,还是出现了错误,还是被其它进程强迫退出的?其次,这个进程占用的总系统 CPU时间和总用户 CPU 时间分别是多少?发生页错误的数目和收到信号的数目。这些信息都被存储在僵尸进程中 .那么,我们如何收集这些信息,并终结这些僵尸进程呢?就要waitpid调用和 wait调用。这两者的作用都是收集僵尸进程留下的信息,同时使这个进程彻底消失 .

Page 80: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 6.5 system函数include <stdlib.h>int system(const char * command);

参数 command 为要执行的命令字符串,它将被直接传送给 UNIX 的命令解释程序 shell ,由 shell 来执行该命令。 例如 system(“ls”); system(“/home/user/test.exe”);

Page 81: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 #include<stdlib.h>

#include<stdio.h>

int main()

{

printf(“running ps with system\n”);

system(“ps-af”);

printf(“Done.\n”);

exit(EXIT_SUCCESS);

}

Page 82: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 6.6 进程组 进程组 ID类似于进程 ID,它是一个整数,并且也用 pid_t数据类型表示。# include <unistd.h> pid_t getpgrp(void);

每一个进程组有一个组长,称为进程组组长,它是 PID与 PGID相同的进程。通常,一个进程从它的父进程继承进程组 ID,并且,在此组内的所有其他进程都是该进程组长的子孙后代。 通过调用 setpgid 函数,进程可以改变它的进程组 ID从而加入到一个已经存在的进程组中;或者改变自己的进程组 ID等于自身的 PID而创建一个新的进程组,从而使自己成为新进程组的组长。# include <unistd.h> int setpgid(pid_t pid,pid_t pgid);

该函数改变参数 pid 所指定的进程组 ID为 pgid 所给定值。如果这两个参数相同,则由pid 所指定的进程成为一个进程组长。

Page 83: 第五讲  UNIX 进程调用 和进程存储

6 UNIX 进程控制 关于 g++和 gccgcc和 g++都是 GNU( 组织 )的一个编译器C和 C++程序都可以用 gcc 编译及连接。如果没有其它指定, gcc 根据文件的扩展名判断编译的是 C程序还是 C++程序。g++ 专门用来处理 C++程序,不论文件的扩展名是表示C还是 C++程序,一律当成 C++程序编译。在编译 C++程序的时候,用 gcc和 g++是等价的。在连接 C++程序的时候, g++相当于 gcc -lstdc++。因此,处理C++程序用 g++比 gcc方便。