操作系统(六)--------进程控制操作(原语)

下面我们介绍一下进程控制 进程控制操作呢,主要是完成了进程 之间,进程的各状态之间的什么呀,转换 那么进程控制操作实际上就是具有特定功能的程序 那么这个程序执行的时候呢,由于不允许被中断,所以呢,我们把它称之为原语那么进程相关的控制原语,有这样一些那么什么是原语呢? 所谓原语,有的时候又称之为原子操作 那么它是完成某种特定功能的一段程序 比如说,完成创建,或者是完成阻塞,它是一段程序,完成了某种特定功能但是这个程序在执行过程中呢,是具有不可分割性,或者是不可中断的 它必须持续地执行,不允许被打断,这就是原语 当然实现原语,需要操作系统通过屏蔽中断一些措施来达到这样一个结果 那么下面我们来看一些典型的进程控制操作 最重要的一个呢,是进程的创建进程创建呢,主要完成以下几个工作 首先,给每一个新的进程分配一个标识,ID再给它找一个空的、没有用过的进程控制块 然后要给这个进程分配它所需要的地址空间 当然这个地址空间,如果在虚拟存储机制之下那么这个空间呢,就假设给了它,但是不真正,啊,给它内存,只是给了一个虚拟地址空间 下一步,是初始化这个进程控制块,填写相应的内容通常呢,都是设定一些默认值,比如说状态,进程的状态设定为 New 等等 那么创建好了这个进程控制块之后呢,要把它插入到相应的队列当中 所以呢,要设置相应的队列指针,比如说,它进入的是就绪队列就把它,这个指针链,链好 在不同的操作系统当中,啊提供了不同的进程创建操作,比如说 UNIX 里头呢,进程创建的主要操作是 fork exec 的一个配合使用,在 WINDOWS当中呢,是 CreateProcess进程的撤销,进程的撤销实际上就是结束进程结束进程其实主要做两件事情,第一件事情呢,是把这个进程所占有的资源回收关闭它打开的文件 如果有网络连接就断开 分配了一些内存,就把它回收在做完这些资源回收之后 最重要的,是要把分配给它的 PCB 收回,这就是进程的撤销,通常呢,我们调用了 exit 或者在 WINDOWS 里调用了 TerminateProcess,那么这个进程呢就撤消了进程的阻塞,这个操作处于运行状态的进程,在其运行过程中,会期待、 等待某个事件的发生 比如说,键盘,等待键盘的输入或者是等待磁盘的数据,输,传输或者是等待其他进程给它发来一些消息 在这些事件没有发生的情况下,那么进程需要自己执行一个阻塞原语使自己的状态由运行态变为阻塞态 那么我们可以通过 UNIX wait 或者是 WINDOWS里头的 WaitForSingleObject等,啊,操作函数 完成这项工作。 那么在 UNIX 里头,有几个非常重要的 进程控制操作,一个就是 fork fork 呢,实际上是创建新的进程,它的创建过程呢是通过复制调用进程本身 来创建的,那么调用进程我们称之为父进程,也就是通过复制父进程,来创建子进程 这是一个非常基本的进程建立过程 那么exec,这是一个系列的系统调用 它的主要目的 是通过用一段新的程序 来覆盖原来的地址空间,也就是 父进程,原来是把自己的所有内容 复制给子进程,那么子进程呢,用一些新的代码 程序代码,把父进程拷贝过来的内容,给它覆盖掉 通过这样一个覆盖,实现了进程的执行代码的一个转换wait,实际上是一个初级的一个同步操作 它能使得一个进程等待另外一个进程的结束 那么exit 刚才已经说过,它是用来终止一个进程的运行 那么这是UNIX 里头,最重要的几个进程控制操作 它们都是以系统调用的形式 作为一个接口,呈现给用户程序,由用户程序来调用 下面我们来看看,UNIX里头,fork这个系统调用的实现,fork在实现 过程中,首先会为子进程分配一个空闲的进程描述符 也就是 PCB,然后这个PCB 呢,在UNIX 里头我们一般叫proc 结构 那么它给子进程也找,分配了一个唯一的标识pid 下面就是给子进程分配地址空间了 那么,在UNIX 里头,fork怎么做的呢 它是以一次一页的方式,把父进程的 地址空间内容完全地拷贝给子进程 之后,再从父进程那里 继承各种共享资源,像打开的文件啊,还有工作,当前工作目录等等 那么都是从父进程那里继承下来,子进程的状态 设置为就绪态,并且把它插入到了就绪队列 做完这项工作之后,fork就为子进程返回一个值 0而为父进程返回一个值,是子进程的 pid那么也就是说,fork执行完后,原来一个进程,父进程就 一分为二,变成了两个进程,一个父进程,一个子进程,在父进程 在父进程的里头,得到的返回值是pid,在子进程里头,得到的返回值是0 那这里头呢,我们来看一下这个操作 这个步骤,以一次一页的方式来复制父进程的地址空间 那么这么做,有什么弊端呢? 我们知道,父进程把它所有的内容都拷贝给子进程 但是子进程是否需要呢?父进程创建子进程 让它做什么事儿呢?通常情况下,父进程 创建子进程是让子进程做与父进程所不同的工作 如果是这样的话,那么你把你的所有内容拷贝给子进程,实际上,子进程也不需要 因此,子进程会接着执行exec 这样一个函数 来把,用新的,啊,一段代码,来把父进程拷贝过来的这些地址空间给覆盖掉 那么因此,之前的这种复制工作,实际上就是无用功了 而且还花了很多的时间和空间,因此 那么,在UNIX 里头实现fork 花费的时间比较长 到了LinuxLinux要想改善这样一个调用的这个 实现,那么 Linux怎么改善呢? Linux是这么做的 Linux使用了一个技术,叫做写时复制技术 Copy-On-Write,那么这个技术,是在存储管理 这个模块当中提供的一个支持 那么Linux 就用了这样一个技术 用在了Linux fork 的实现过程中 那么,采用了这个技术之后 在Linux 里的fork 就加快了速度,那么为什么呢?我们来看一下 原来是要复制父进程的地址空间,而现在呢 只需要父进程把地址空间的指针传递给子进程 再把地址空间设置为只读 那么当子进程要往地址空间里写 东西的时候。 写时复制,当要写东西的时候 啊,譬如写代码啊,写一个值在里头的时候。 这个时候 被操作系统接受,然后呢 操作系统会为子进程单独再开辟一块空间,把相应的内容放进去 那么这样的话呢,节省了 之前复制父进程地址空间的时间,加快了 fork 的实现速度。 那么当 Linux引入这个技术之后,实际上是 fork的速度是非常快了 那么这就是 UNIXFORK的实现,以及 Linux对这段实现的一个改进 下面我们来看一下怎么样来使用 fork那么我们看一下,这一段代码当中呢 在这里,这个进程呢,创建了一个子进程 那么把这个 fork 创建的子进程 把这个fork 的返回值呢,返回给一个整数pid 里头 下面就来判断这个pid 的值 如果pid 小于0 ,说明这个fork 失败,因此报错 退出就好了。 如果 不小于0 ,那么就要判断pid 的值是0 还是非0 如果是0 ,表示的是子进程的 代码空间,因此呢执行的是子进程要做的事情 如果不为0 ,那么返回的就是子进程的pid ,说明是在 父进程的地址空间。 因此呢,就执行父进程所需要的代码 也就是说fork 执行完之后,原来的一个进程分支,变成了两个进程 子进程要做的事情,就是用一段代码来覆盖 父进程的地址空间内容。 这个因为是UNIX ,所以它是覆盖过去 然后呢,去执行相应的代码,最后,执行完了以后退出。 而父进程呢 创建完子进程之后呢,就调用了一个wait 这样一个函数,等待子进程的结束 那么子进程结束之后呢,父进程得到信号,然后父进程再做其它的工作 那么这就是这段代码的基本含义。 我们来看一下 从地址空间的角度,在创建完 进程之后,也就是执行完了这个fork 父进程的地址空间和子进程的地址空间,这是两个空间 那么在父进程地址空间当中的这个pid 变量拿到的是 子进程的pid xxx ,啊 那么子进程的空间里头,这个pid 这个单元得到的是一个0 ,表示的这是子进程的空间 那么下一个指令从哪开始执行呢? 两个空间里头,下一条指令都是从判断pid 是否等于0 开始执行,所以那边也是,这边也是 那么对于父进程pid 因为不等于0 ,所以呢父进程就进入到了 它所需要执行的代码段 而子进程由于判断出pid 等于0 ,它就执行这个代码段 我们来看一下,就是父进程就开始执行print "parent",打印一个"parent",而子进程呢执行的是打印"child" 的工作 那么这就是我们说的fork 执行完之后 变成了一分为二,那么有两个地址空间 每个地址空间当中,这个pid 的值是不一样的,因此 父进程执行父进程所需要的代码 子进程执行子进程所需要的代码 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱coding的同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值