进程与线程学习笔记

1. 进程

1.1 进程概述

  一个进程(process)是一个正在执行程序的实例,包括程序计数器、寄存器和当前值。每个进程有一个自己的虚拟CPU,几个虚拟的CPU并行运算,而事实上真正的CPU是在个进程之间切换的。假设有4个进程,各自控制自己的运行流程(逻辑计数器),在每个进程运行时,把逻辑计数器装入实际计数器中,结束运行时,则将实际计数器的内容装入逻辑计数器中。也就是说,一个CPU一次只能运行一个进程。

  守护进程(daemon):运行在后台的进程,通常处于睡眠状态,但是可能被唤醒。如电子邮件进程,当一个邮件到达时被唤醒接收邮件。

1.2 进程与程序的类比

  程序——菜谱

  进程——做菜

  进程切换——有其他紧急事务处理时,记录菜做到哪一步,先处理紧急事务,然后从离开时的那一步做起。

  进程不可以在计算机之间迁移而程序可以

1.3 进程创建与终止

  导致进程创建的事件:1)系统初始化;2)正在运行的进程进行系统调用;3)用户请求创建;4)一个批处理作业的初始化。

  导致进程终止的时间:1)正常退出(自愿);2)出错退出(自愿);3)严重错误(非自愿);4)被其它进程杀死(非自愿)

1.4 进程的层次结构

  父进程与子进程:Unix中,进程和其所有子女及后裔共同组成一个进程组,树结构;windows没有进程层次的概念所有进程地位相同。而Windows中一个类似的操作是在创建进程时,父进程得到一个句柄用来控制子进程,并且他可以把这个句柄传送给别的进程,而UNIX中的进程不能剥夺其子女的继承权

1.5 进程状态

  三状态:1)运行态(Running):该时刻进程实际占用CPU;2)就绪态(Ready):可运行,但因其它进程正在运行而暂时停止;3)阻塞态(Blocked):由于某种原因即使CPU空闲也不能运行,直到这个原因消除。

  进程挂起(Suspend):优先级的引入会使有些进程等待时间过长,从而会被换至外存,成为进程挂起。进程挂起分为阻塞挂起和就绪挂起。就绪挂起一旦进入内存即可执行。

1.6 进程实现

  动态性;独立性(个进程地址空间相互独立);并发性;异步性;结构化(代码段、数据段、栈段)

2. 线程

2.1 使用多线程的原因

  1)并行实体共享地址空间和可用数据的能力;2)线程比进程更轻量级,容易创建和撤销;3)性能高,当存在大量计算和大量IO处理时,多线程允许这些活动彼此重叠进行;4)多CPU系统中,真正的并行有了实现的可能。

  例子:编辑一个很长的文档

  在第1页删除一句话,然后想跳转到第600页修改。若不是多线程的话,用户需要等很长时间,等文档格式化完之后才能显示出来;而如果一个线程a处理格式化,一个线程b处理与用户交互的话,等用户删除一句话之后,a即刻进行格式化处理,当用户请求查看第600页时,可能已经格式化完成,线程b立即显示第600页。还可以加一个线程c用来隔段时间保存文档,若程序是单线程的话,在保存文档的时候,鼠标键盘都不会响应,性能很差。

  在这里,由于在同一个文件上操作,三个不同的进程是不能同时工作的,而线程共享数据和内存,可以同时访问同一个文件。

  另外一个例子是一个多线程的Web服务器(具体模型见《现代操作系统》P55页)

2.2 线程

  线程(thread)拥有一个程序计数器(记录执行到哪一个命令)、寄存器(保存当前的工作变量)和一个堆栈(记录执行历史)。线程状态与与进程状态类似。

3. 进程间通信

  竞争条件(race condition):两个或多个进程读写某些共享数据,而最后的结果取决于进程运行的精确时序。

  临界区(critical region/critical section):进行访问共享内存的程序片段。
  互斥(mutual exclusion),即以某种手段确保当一个进程在使用一个共享变量或文件时,其它进程不能做同样的操作。

  忙等待的互斥方案(不成熟的):

  ①屏蔽中断:在每个进程刚刚进入临界区后屏蔽所有中断。若没有打开中断,则产生问题,危险做法!

  ②锁变量:没有解决根本问题

  ③严格轮换法:自旋锁(spin lock)用于忙等待的锁。进程0、1轮流使用临界区,顺序必须严格按照010101...如果顺序为01000...则会有问题。

  ④Peterson解法

  ⑤TSL指令

  这些方法有一个共同点:忙等待。其基本思想都是当一个进程想进入临界区时,先检查是否允许进入,若不允许,则该进程原地等待直到被允许。这些方法都有优先级反转问题(priority inversion problem)—— 两个进程H(优先级高)L(优先级低),在某一时刻,L处于临界区中,此时H转为就绪态,开始忙等待,而当H就绪时L不会被调度,也就无法离开临界区,所以H将永远忙等待下去。

  睡眠与唤醒原语的互斥方案(也是不成熟的):

  以生产者消费者(生产者将信息放入缓冲区,消费者从缓冲区中取走信息)问题为例(一个producer和一个consumer)

  

 1 #define N 100  //缓冲区槽数
 2 int count=0;     //缓冲区中数据项数目
 3 
 4 void producer(void)
 5 {
 6     int item;
 7     while(true)
 8     {
 9           item = produce_item();//产生数据项
10           if(count == N) sleep(); //缓冲区满,去睡觉
11           insert_item(item);        //想缓冲区中放入新的数据项
12           count++;
13           if(count == 1) wakeup(consumer);//缓冲区空时,在生产新数据后唤醒消费者
14     }
15 }
16 
17 void consumer(void)
18 {
19     int item;
20     while(true)
21     {
22           if(count == 0) sleep(); //缓冲区空,去睡觉
23           item = remove_item;        //缓冲区中取出数据项
24           count--;
25           if(count == N - 1) wakeup(producer);//缓冲区不满了唤醒生产者
26           consume_item(item);//打印数据项
27     }
28 }
29           

  在这里,由于没有对count加以限制,可能会出现竞争。consumer和producer并发执行,假如count为0了,consumer判定其为0但是还没有执行sleep();此时,producer刚刚生产一个数据放入缓冲区然后“唤醒”consumer;然后consumer执行了sleep(),此时producer实际上并没有唤醒consumer,结果就会只生产不消费,最后producer也sleep了。

  

 (以下部分暂时没写完,明天继续~~)

  竞争避免的方法(解决共享资源竞争):

  ①信号量(semaphore):PV原语

  ②互斥量(mutex)

  ③消息传递(message passing)

 

  进程通信手段:管程(monitor)

posted @ 2012-05-16 16:58  起苏桃子  Views(193)  Comments(0Edit  收藏  举报