进程就是程序的一次执行过程,程序是静态的,它作为系统中的一种资源是永远存在的。而进程是动态的,它是动态的产生,变化和消亡的,拥有其自己的生命周期。
进程控制块PCB,
(资料图)
1)进程描述信息:用来让操作系统区分各个进程
2)进程控制和管理信息:记录进程的运行情况。比如 CPU 的使用时间、磁盘使用情况、网络流量使用情况等。
3)资源分配清单:记录给进程分配了哪些资源。比如分配了多少内存、正在使用哪些 I/O 设备、正在使用哪些文件等。
4)CPU 相关信息:进程在让出 CPU 时,必须保存该进程在 CPU 中的各种信息,比如各种寄存器的值。用于实现进程切换,确保这个进程再次运行的时候恢复 CPU 现场,从断点处继续执行。这就是所谓的保存现场信息。
数据段。即进程运行过程中各种数据(比如程序中定义的变量)
程序段。就是程序的代码(指令序列)
经典的进程三态模型如下:
运行态(running):进程占有 CPU 正在运行。
就绪态(ready):进程具备运行条件,等待系统分配 CPU 以便运行。
阻塞态/ 等待态(wait):进程不具备运行条件,正在等待某个事件的完成。
需要注意的是:阻塞态是由于缺少需要的资源从而由运行态转换而来,但是该资源不包括 CPU 时间片,缺少 CPU 时间片会从运行态转换为就绪态。
很多系统中都增加了新建态(new)和终止态(exit),形成五态模型:
从上图可以发现,只有就绪态和运行态可以相互转换,其它的都是单向转换,阻塞状态结束进入就绪态而不可能直接进入运行态。
进程的阻塞和唤醒显然是由进程切换来完成的。
进程的阻塞步骤,也就是阻塞原语的内容为:
进程的唤醒步骤,也就是唤醒原语的内容为:
阻塞原语和唤醒原语的作用正好相反,阻塞原语使得进程从运行态转为阻塞态,而唤醒原语使得进程从阻塞态转为就绪态。如果某个进程使用阻塞原语来阻塞自己,那么他就必须使用唤醒原语来唤醒自己,因何事阻塞,就由何事唤醒,否则被阻塞的进程将永远处于阻塞态。因此,阻塞原语和唤醒原语是成对出现的。
1).数据传输:一个进程需要将它的数据发送给另一个进程;
2).资源共享:多个进程之间共享同样的资源;(可能会从死锁那里引申过来)
3).通知事件:一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件;
4).进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),该控制进程希望能够拦截另一个进程的所有操作,并能够及时知道它的状态改变。
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信机制。
主要的过程如下图所示:
(本质都是在内核开辟一块空间共享)
管道(内存中的特殊文件)、命名管道(硬盘上的文件)、消息队列(内核)、共享存储、信号量、套接字、信号详细可参考:https://www.jianshu.com/p/c1015f5ffa74
管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。进程间通信的方式是匿名的,所以只能用于具有亲缘关系的进程间通信
最基本的IPC机制,由pipe函数创建,
管道在用户程序看起来就像一个打开的文件,通过read(pipefd[0])或者write(pipefd[1])向这个文件读写数据,其实是在读写内核缓冲区。
1.父进程调用pipe开辟管道,得到两个文件描述符指向管道的两端。
2.父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管道。
3.父进程关闭管道读端,子进程关闭管道写端。父进程可以往管道里写,子进程可以从管道里读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。
管道出现的四种特殊情况:
1.写端关闭,读端不关闭;
那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。
2.写端不关闭,但是也不写数据,读端不关闭;
此时管道中剩余的数据都被读取之后再次read会被阻塞,直到管道中有数据可读了才重新读取数据并返回;
3.读端关闭,写端不关闭;
此时该进程会收到信号SIGPIPE,通常会导致进程异常终止。
4.读端不关闭,但是也不读取数据,写端不关闭;
此时当写端被写满之后再次write会阻塞,直到管道中有空位置了才会写入数据并重新返回。
使用管道的缺点:
1.两个进程通过一个管道只能实现单向通信,如果想双向通信必须再重新创建一个管道或者使用sockpair
才可以解决这类问题;
socketpair()
函数用于创建一对无名的、相互连接的套接子。如果函数成功,则返回0,创建好的套接字分别是sv[0]
和sv[1]
;否则返回-1
,错误码保存于errno
中。基本用法:
- 这对套接字可以用于全双工通信,每一个套接字既可以读也可以写。例如,可以往
sv[0]
中写,从sv[1]
中读;或者从sv[1]
中写,从sv[0]
中读;- 如果往一个套接字(如
sv[0]
)中写入后,再从该套接字读时会阻塞,只能在另一个套接字中(sv[1]
)上读成功;- 读、写操作可以位于同一个进程,也可以分别位于不同的进程,如父子进程。如果是父子进程时,一般会功能分离,一个进程用来读,一个用来写。因为文件描述副
sv[0]
和sv[1]
是进程共享的,所以读的进程要关闭写描述符, 反之,写的进程关闭读描述符。
2.只能用于具有亲缘关系的进程间通信,例如父子,兄弟进程。
3.只能承载无格式字节流以及缓冲区大小受限
命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。命名管道的名字对应于一个磁盘索引节点,有了这个文件名,任何进程有相应的权限都可以对它进行访问。
FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存储文件系统中。命名管道是一个设备文件,因此即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。
命名管道的特点:
1.命名管道是一个存在于硬盘上的文件,而管道是存在于内存中的特殊文件。所以当使用命名管道的时候必须先open将其打开。
2.命名管道可以用于任何两个进程之间的通信,不管这两个进程是不是父子进程,也不管这两个进程之间有没有关系。
消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
消息队列,FIFO,管道的消息传递方式一般为 :
1).服务器获取输入的信息;
2).通过管道,消息队列等写入数据至内存中,通常需要将该数据拷贝到内核中;
3).客户从内核中将数据拷贝到自己的客户端进程中;
4).然后再从进程中拷贝到输出文件;
上述过程通常要经过4次拷贝,才能完成文件的传递。而共享内存只需要:
1).输入内容到共享内存区
2).从共享内存输出到文件
上述过程不涉及到内核的拷贝,这些进程间数据的传递就不再通过执行任何进入内核的系统调用来传递彼此的数据,节省了时间,所以共享内存是这五种进程间通信方式中效率最高的。
信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
信号量的本质是一种数据操作锁,用来负责数据操作过程中的互斥,同步等功能。
信号量用来管理临界资源的。它本身只是一种外部资源的标识,不具有数据交换功能,而是通过控制其他的通信资源实现进程间通信。 可以这样理解,信号量就相当于是一个计数器。当有进程对它所管理的资源进行请求时,进程先要读取信号量的值:大于0,资源可以请求;等于0,资源不可以用,这时进程会进入睡眠状态直至资源可用。
当一个进程不再使用资源时,信号量+1(对应的操作称为V操作),反之当有进程使用资源时,信号量-1(对应的操作为P操作)。对信号量的值操作均为原子操作。
套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。
信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
信号生命周期和处理流程(1)信号被某个进程产生,并设置此信号传递的对象(一般为对应进程的pid),然后传递给操作系统;(2)操作系统根据接收进程的设置(是否阻塞)而选择性的发送给接收者,如果接收者阻塞该信号(且该信号是可以阻塞的),操作系统将暂时保留该信号,而不传递,直到该进程解除了对此信号的阻塞(如果对应进程已经退出,则丢弃此信号),如果对应进程没有阻塞,操作系统将传递此信号。(3)目的进程接收到此信号后,将根据当前进程对此信号设置的预处理方式,暂时终止当前代码的执行,保护上下文(主要包括临时寄存器数据,当前程序位置以及当前CPU的状态)、转而执行中断服务程序,执行完成后在回复到中断的位置。当然,对于抢占式内核,在中断返回时还将引发新的调度。
进程(process)是指在系统中正在运行的一个应用程序,是系统资源分配的基本单位,在内存中有其完备的数据空间和代码空间,拥有完整的虚拟空间地址。一个进程所拥有的数据和变量只属于它自己。
线程(thread)是进程内相对独立的可执行单元,所以也被称为轻量进程(lightweight processes );是操作系统进行任务调度的基本单元。它与父进程的其它线程共享该进程所拥有的全部代码空间和全局变量,但拥有独立的堆栈(即局部变量对于线程来说是私有的)
优点:
缺点尽管线程在现代计算机中极具重要性,它们却有很多缺点:
优点
缺点
X 关闭
Copyright © 2015-2022 欧洲医疗网版权所有 备案号:沪ICP备2022005074号-23 联系邮箱: 58 55 97 3@qq.com