一、进程
1.进程是对正在运行程序的一个抽象。一个进程就是一个正在执行程序的实例,包括程序计数器、寄存器、和变量的当前值。从概念上说,每个进程拥有它自己的虚拟cpu。当然实际的cpu各进程之间来回切换。
2.创建进程,有四种主要事件导致进程的创建:1.系统初始化。2.执行了正在运行进程所调用的进程创建系统调用。3.用户请求创建一个新进程。4.一个批处理作业的初始化。
启动操作系统时,通常会创建若干进程。其中有些是前台进程,也就是同用户交互并且替他们完成工作的那些进程。其他的是后台进程,这些进程与特定的用户没有关系,相反,却具有某些专门的功能,例如,设计一个后台进程来接收发来的电子邮件,这个进程在一天的大部分时间都在睡眠,但是当电子邮件到达时就突然被唤醒了。停留在后台处理诸如电子邮件、Web页面、新闻、打印之类活动的进程称为守护进程.
3.进程的终止,进程终止通常由下列条件引起:1.正常退出(自愿的) 。2.出错退出(自愿的)。3.严重错误(非自愿的)。4.被其它进程杀死(非自愿的)。
多数进程是由于完成了它们的工作而终止。第二个原因是进程发现了严重错误,例如用户执行cc foo.c,编译该程序,但是foo.c不存在,编译进程就会退出。第三个原因是由进程引起的错误 ,例如执行非法指令,引用不存在的内存,或除数是0。第四种终止进程的原因是,当某个进程执行一个系统调用通知操作系统杀死某个其他进程。
4.进程的层次结构,某些系统中,当进程创建了另一个进程后,父进程和子进程就以某种形式继续保持关联。在unix中,进程和它的所有子女及后裔共同组成一个进程组。当用户从键盘发出一个信号时,该信号被送给当前与键盘相关的进程组中的所有成员。
5.进程的状态,进程一般有三种状态,这三种状态是:1.运行态(该时刻进程实际占用cpu)。2.就绪态(可运行,但因为其他进程正在运行而暂时停止)。3.阻塞态(除非某种外部事件发生,否则进程不能运行)。
在操作系统发现进程不能继续运行下去时,发生由运行态到阻塞态的转换;运行态和就绪态的互相转换一般由进程调度程序引起的;当进程等待一个外部事件发生时(如一些输入到达),则发生由阻塞态到就绪态的转换,如果此时没有其他进程运行,则立即由就绪态到运行态。
使用进程模型使得我们易于想象系统内部的操作情况。一些进程正在运行执行用户键入命令所对应的程序。另一些进程是系统的一部分,它们的任务是完成下列一些工作:比如,执行文件服务请求、管理磁盘驱动器和磁带机的运行细节等。当发生一个磁盘中断时,系统会做出决定,停止运行当前的进程,转而运行磁盘进程,该进程在此前因等待中断而处于阻塞态。这样,我们就可以不再考虑中断,而只是考虑用户进程、磁盘进程、终端进程等。这些进程在等待时总是处于阻塞态。在已经读入磁盘或键入字符后,等待它们的进程就被解除阻塞,并成为可调度运行的进程。
操作系统的最底层是调度程序,在它上面有许多进程。所有关于中断处理、启动进程和停止进程的具体细节都隐藏在调度程序中。
6.进程的实现,为了实现进程模型,操作系统维护着一张表格(一个结构数组),即进程表。每个进程占用一个进程表项。也可以说是进程控制块。该表项包含了进程状态的重要信息,包括程序计数器、堆栈指针、内存分配状况、所打开文件的状态、账号和调度信息,以及其他在进程由运行态转换到就绪态或阻塞态时必须保存的信息,从而保证该进程随后能再次启动,就像从未被中断过一样。
与每一个I/O类关联的是一个称作中断向量的位置(靠近内存底部的固定区域,比如ARM系列cpu中断向量在0x0000000开始的位置)。它包含中断服务程序的入口地址。假设当一个磁盘中断发生时,用户进程3正在运行,则中断硬件将程序计数器、程序状态字,有时还有一个或多个寄存器压入堆栈,计算机随即跳转到中断向量所指示的地址。这些是硬件完成的所有操作,然后软件,特别是中断服务例程就接管一切剩余的工作。
所有的中断都从保存寄存器开始,对于当前进程而言,通常是在进程表项中。随后,会从堆栈中删除由中断硬件机制存入堆栈的那部分信息,并将堆栈指针指向一个由进程处理程序所使用的临时堆栈。一些诸如保存寄存器值和设置堆栈指针等操作,无法用c语言这一类高级语言描述,所以这些操作通过一个短小的汇编语言例程来完成,通过该例程可以供所有的中断使用。这也就是实现上下文切换的基本方法。
二、线程
为什么人们需要在一个进程中再有一类进程?有若干理由说明产生这些迷你进程(称为线程)的必要性。人们需要多线程的主要原因是,在许多应用中同时发生着多种活动。其中某些活动随着时间的推移会被阻塞。通过将这些应用程序分解成可以准并行运行的多个顺序线程,程序设计模型会变得简单。我们有了关于进程模型的抽象,我们才不必考虑中断、定时器和上下文切换,而只需考察并行进程。类似的,只是在有了多线程概念之后,我们才加入了一种新的元素:并行实体共享同一个地址空间和所有可用数据的能力。对于某些应用而言,这种能力是必须的,而这正是多进程模型(它们具有不同的地址空间)所无法表达的。
第二个需要多线程的理由是,由于线程比进程更轻量级,所以它们比进程更容易(即更快)创建,也更容易撤销。在许多系统中,创建一个线程比创建一个进程要快10-100倍。在有大量线程需要动态和快速修改时,具有这一特性是很有用的。
需要多线程的第三个原因涉及性能方面的讨论。若多个线程都是cpu密集型的,那么并不能获得性能上的增强。但是如果存在着大量的I/O处理,拥有多个线程允许这些活动彼此重叠进行,从而加快应用程序执行的速度。
最后在多cpu系统中,多线程是有益的,这样的系统中,真正的并行有了实现的可能。
线程分为用户线程和内核线程,用户线程:是指线程在用户空间实现,内核对线程一无所知,还是按照正常的方式管理,即单线程进程,用户线程的有点时上下文切换比较快,用户线程切换至少比陷入内核要快一个数量级,这是使用用户线程包的极大优点。另外一个优点是每个进程有自己定制的调度算法,用户线程还具有较好的可扩展性。用户线程主要考虑的是如何在进行阻塞系统调用时,不阻塞整个进程。另外一个是,一旦开始运行一个线程其它线程就不能运行,除非主动放弃cpu。
内核线程:是指线程在内核空间实现的,内核线程不需要任何新的、非阻塞系统调用。