第1篇 进程和线程
声明:本博客为学习操作系统时所做笔记,主要资料来源为《操作系统概念》(第七版)以及“C语言中文网”中的操作系统篇,如有不妥之处,再修改或者删除。“C语言中文网址”如下:
http://c.biancheng.net/cpp/u/xitong/
1 进程
1.1 进程的概念:
由程序段、相关数据段和PCB三部分构成了进程映像(进程实体)。所谓创建进程,实质上是创建进程映像中的PCB;而撤销进程,实质上是撤销进程的PCB。
1.2 进程的状态:
1.3 有关PCB的结构描述:
http://blog.****.net/jnu_simba/article/details/11724277
1.4 调度队列:
进程进入系统时,会被加入到作业队列中,该队列包括系统中的所有进程。内存中的就绪的、等待运行的进程保存在就绪队列中,等待特定I/O设备的进程列表称为设备队列。
1.5 进程控制:进程的创建、终止、阻塞、唤醒和切换
在操作系统中,一般把进程控制用的程序段称为原语,原语的特点是执行期间不允许中断,它是一个不可分割的基本单位。
进程的创建
①创建进程称为父进程,而新进程称为子进程。
②大多数操作系统通过一个进程标识符(pid)来识别进程。
③进程需要一定的资源(CPU时间、内存、文件、I/O设备)来完成其任务,在一个进程创建子进程时,子进程可以从操作系统那里获得资源,也可以只从父进程那里获得资源。
④UNIX系统,通过fork()系统调用可以创建新进程;Win32 API通过调用CreateProcess()函数创建进程。
进程的终止
①根据被终止进程的标识符,检索PCB,从中读出该进程的状态。
②若被终止进程处于执行状态,立即终止该进程的执行,将处理机资源分配给其他进程。
③若该进程还有子进程,则应将其所有子进程终止。
④将该进程所拥有的全部资源,或归还给其父进程或归还给操作系统。
⑤将该PCB从所在队列(链表)中删除。
进程的阻塞和唤醒
正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等,则由系统自动执行阻塞原语(Block),使自己由运行状态变为阻塞状态。可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得CPU),才可能将其转为阻塞状态。
阻塞原语的执行过程是:
①找到将要被阻塞进程的标识号对应的PCB。
②若该进程为运行状态,则保护其现场,将其状态转为阻塞状态,停止运行。
③把该PCB插入到相应事件的等待队列中去。
当被阻塞进程所期待的事件出现时,如它所启动的I/O操作已完成或其所期待的数据已到达,则由有关进程(比如,提供数据的进程)调用唤醒原语(Wakeup),将等待该事件的进程唤醒。
唤醒原语的执行过程是:
①在该事件的等待队列中找到相应进程的PCB。
②将其从等待队列中移出,并置其状态为就绪状态。
③把该PCB插入就绪队列中,等待调度程序调度。
需要注意的是,Block原语和Wakeup原语是一对作用刚好相反的原语,必须成对使用。 Block原语是由被阻塞进程自我调用实现的,而Wakeup原语则是由一个与被唤醒进程相合作或被其他相关的进程调用实现的。
进程的切换
进程切换的过程如下:
①保存处理机上下文,包括程序计数器和其他寄存器。
②更新PCB信息。
③把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列。
④选择另一个进程执行,并更新其PCB。
⑤更新内存管理的数据结构。
⑥恢复处理机上下文。
1.6 进程间的通信方式
共享内存:
在通信的进程之间存在一块可直接访问的共享空间,通过对这片共享空间进行写/读操作实现进程之间的信息交换。
消息传递:
消息传递工具提供至少两种操作:发送(消息)和接收(消息)。
① 命名:
直接通信:需要通信的每个进程必须明确地命名通信的接收者或发送者。
间接通信:通过邮箱或端口来发送和接收消息。
② 同步:
进程间的通信可以通过原语send()和receive()来进行。消息传递可以是阻塞或者非阻塞(也称同步或异步)。因此可以分为:阻塞send、非阻塞send、阻塞receive、非阻塞receive。
③ 缓冲:
不管通信是直接的或是间接的,通信进程所交换的消息都驻留在临时队列中。队列的实现有三种方法:零容量、有限容量、无限容量。
管道通信
管道通信是消息传递的一种特殊方式。所谓“管道”,是指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件,又名pipe文件。向管道(共享文件)提供输入的发送进程(即写进程),以字符流形式将大量的数据送入(写)管道;而接收管道输出的接收进程(即读进程),则从管道中接收(读)数据。为了协调双方的通信,管道机制必须提供以下三方面的协调能力:互斥、同步和确定对方的存在。
有关进程间通信机制(管道、信号、共享内存/信号量/消息队列)、线程间通信机制(互斥锁、条件变量、posix匿名信号量)可以参考下面的博客:
http://blog.****.net/jnu_simba/article/details/11746217
有关Linux进程间通信–进程,信号,管道,消息队列,信号量,共享内存 可以参看下面的博客:
http://blog.****.net/21aspnet/article/details/7479469
http://blog.****.net/21aspnet/article/details/7420091
2 线程
2.1 线程的概念及多线程模型
线程概念
引入进程目的:为了使多道程序并发执行,以提高资源利用率和系统吞吐量;
引入线程目的:为了减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。
线程的组成:线程ID、程序计数器、寄存器集合和堆栈。它与属于同一进程的其他线程共享代码段、数据段和其他操作系统资源。
进程和线程的比较:
线程的实现方式:
用户级线程(User-LevelThread, ULT)和内核级线程(Kemel-LevelThread, KLT)。内核级线程又称为内核支持的线程。
多线程模型:
2.2 线程库
线程库为程序员提供创建和管理线程的API。主要有两种方法来说实现线程库:第一种为用户空间提供一个没有内核支持的库,此库的所有代码和数据结构都在用户空间中;第二种方法是执行一个由操作系统支持的内核级库,此库的代码和数据结构都存在于内核空间中。
目前,主要使用的三种库是 POSIX Pthread 、 Win32 、 Java
2.3 线程池
线程池的思想:进程开始时创建一定数量的线程,并放入到池中以等待工作。当服务器收到请求时,它会唤醒池中的一个线程(如果池中有可用的线程),并将要处理的请求传递给它。一旦线程完成了服务,它会返回池中再等待工作。如果池中没有可用的线程,那么服务器会一直等待直到有空线程为止。
线程池的优点:①通常用现有线程处理请求比等待创建新的线程速度要快。②线程池限制了在任何时候可用线程的数量。比较高级的线程池能动态的调整线程的数量。