先讲讲进程是什么。
首先,我们需要知道程序是什么。
程序就是一个指令序列。早期的计算机只支持单道程序。如下图
一个程序在内存中有数据段和程序段。程序的代码放在程序段内,程序运行过程的数据放在数据段内(如变量)。然后高地址存放数据段,地地址存放程序段。
后面发展到支持多道程序。即内存中同时放入多道程序,各个程序的代码,运行数据存放的位置不同。如下图:
所以就是系统系统为每个运行的程序排至一个数据结构,称为进程控制块(PCB,progress control block),就是一个程序对应一个PCB。里面有进程的状态,进程的寄存器,进程编号,内存界限,程序计数器等信息。
为了方便操作系统的管理,完成各程序并发执行,引入了进程,进程实体的概念。
PCB,程序段,数据段三部分构成了进程实体(又叫进程映像)。
程序段:描述进程本身要完成的功能;
数据段:程序加工的对象和场所;
进程控制块:内存中存储,记录进程生存周期内状态变化的存储区域。不同操作系统有不同的进程控制块格式和信息,但基本包括进程标识符,当前状态,现场保护区,存储指针,占用资源表以及进程优先级等信息。它是进程存在的唯一标志。
一般而言,我们称进程实体为进程。
例如,我们说的创建进程就是创建进程实体里的PCB,而撤销进程也是撤销进程实体里面的PCB。
比较官方地说是:进程是进程实体的运行过程,是系统进行资源分配和资源调度的一个独立单位。
严格来说,进程并不等同于进程实体,进程实体是静态的,进程是动态的。
进程和程序的区别:
一般说进程是程序的一次运行过程,是动态地产生,变化和消亡。
进程的三种基本状态:
运行态:占有cpu,并且就在cpu上执行。
就绪态:已经具备运行条件,但由于没有空闲cpu,而暂时不能运行。(也就是cpu没有调度到它)
阻塞态:因等待某一事件而不能运行。
其实这个很好理解,如果看过我之前关于线程的讨论的话应该很容易理解。注意:单核处理机同一时间只有一个进程处于运行态,双核则是两个。
还有两个状态:
创建态:进程正在被创建,系统为其初始化PCB,分配资源。
终止态:进程正在从系统中撤销,回收进程的资源,撤销其PCB。
下面一张图可看出这五个状态的关系:
感觉和java的线程那里也差不多,理解起来很快。
这里简单说一下:就绪态通过进程调度可以变为运行态,运行态通过中断变为就绪态,运行态通过io请求等待事件进入阻塞态,阻塞态被唤醒,即等待事件结束,就变为了就绪态。
接下来我们讲进程的通信:
上面这张图大概目录了进程通信的全部东西,下面我们进行详细讲解。
下面的图片有助于理解,下面我们就跟着图片来讲解:
进程通信:就是指进程之间的信息交换。
各个进程拥有的内存地址空间相互独立,为了保证线程安全,操作系统中规定一个进程不能直接访问另一个进程的地址空间。比如上图中的进程一就不能直接访问进程二的地址空间。
但是进程之间又必须可以通信,比如微信发送图片时,就是微信这个进程和手机管理图片的进程间的通信,取出图片并且进行发送。
进程通信的方式主要分为三种:共享存储,消息传递,管道通信。
先来看第一种:共享存储。
共享存储就是操作系统另外分配一个内存区域用于进程间的通信,这就是共享空间,两个进程都可以访问这个共享空间,但是两个进程对共享空间的访问必须是互斥的,即同一时间段只能有一个进程访问这个空间,类似于线程那样。操作系统只负责提供共享空间和同步互斥工具(如P V操作,我看了一下,锁、PV操作、进程互斥与同步的实现 - qicheng777的博客这个链接里讲了啥事pv操作,我总结了一下就是类似于一个AQS(不懂得同学自己建立模型理解,注意它的等待队列),然后为啥PV操作能实现进程互斥呢?可以看这位大哥的模型就很不错,一看就懂了,主要就是一个信号量,PV操作的加一减一,等待队列)。
然后共享存储又分为基于数据结构的共享和基于存储区的共享。
1.基于数据结构的共享:比如它的共享空间只能放一个长度为10的数组,数据量比较小,存储的数据形式也有限制。所以这种方式速度慢,限制多, 是一种低级通信‘
2.基于存储区的共享:此时操作系统只负责提供内存,然后由进程来控制数据的形式和存放位置。所以这种方式速度更快,是一种高级的共享方式。
然后来看第二种通信方式:管道通信。
管道:是指用于连接读写进程的一个共享文件,又名pipe文件。其实就是在内存中开辟一个大小固定的缓冲区。它的大小一般是和系统的内存页面大小一样,比如linux默认的内存页面大小是4kb,所以linux的管道大小也是4kb.
1.管道是才有半双工通信,有单片机基础或者计算机网络基础的同学都很容易理解这个概念,简单说就是同一时间只能实现单向传输,要实现双向同时通信,则需要两个管道。
2.各进程要互斥地访问管道。
3.数据以字符流的形式写入管道,当管道写满时,写进程的write()系统调用将被阻塞,等待进程将数据读走。当都进程将数据全部取走后,管道变空,此时读进程的read()系统调用将被阻塞。
4.如果没写满,就不会允许读。如果没读空,就不允许写。
5.数据一旦被读出,就会从管道中抛弃,这就意味着读进程最多只能有一个,否则可能会有读错数据的情况。
然后我们来看第三种进程通信方式:消息传递。
进程间的数据交换以格式化的消息为单位,进程通过操作系统提供的“发送消息/接收消息”两个原语进行数据交换。也就是说:一个进程调用“发送原语"这个api就可以把消息发送出去,通过”接收原语“就能取出消息。
一个消息包括消息头和消息体,消息头包括:发送进程的id,接收进程id,消息类型,消息长度等格式化的消息(计算机网络中发送的”报文“其实就是一种格式化的消息)。如果大家写过聊天室之类的小项目就很容易理解了,这就相当于自己自定义消息头,像tcp,udp的报头也是。而消息体就是消息内容了。
消息传递又包括两种通信方式:直接通信方式和间接通信方式。
1. 直接通信方式:消息直接挂到接收进程的消息缓冲队列上。也就是说,每个进程都有一个消息缓存队列。当进程A要给进程B通信,就先调用发送原语,把消息发送到进程B的消息缓存队列当中,然后进程B会调用接收原语,从自己的消息缓存队列当中取出消息。
2.间接通信方式:消息要先发送到中间实体(信箱)中,因此也称信箱通信方式,比如计算机网络中的电子邮箱系统。这就类似于,有一个中间部门专门管理这些信息,每个进程发送消息都是发送到这里,每个进程接收消息都是从这里取出。因为有消息头的目的进程id等信息,所以这一切都可以实现。
上面就是进程通信方式的讲解了。下面有个总结的图: