1、程序并发执行的特征
间断性、失去封闭性、不可再现性
间断性:并发的某一个程序没有完成会导致另一个程序无法继续进行下去
失去封闭性:并发程序共享资源,共享的资源的状态由多个程序改变
不可再现性:由于并发程序共享资源,且不同程序以不可预知的速度运行,
会导致程序每次执行的结果不一致
2、进程的特征和状态
特征:动态性 ,并发性,独立性,异步性
状态:创建,就绪,执行,阻塞,终止。
创建:为进程创建PCB(进程控制块)
就绪:一个进程获得了除了cpu之外的所有资源,插入到就绪队列中,只要获得cpu即可运行
执行:进程获得cpu
阻塞:正在执行的进程由于发生某事件而暂时无法继续执行时,便放弃处理机而处于暂停状态,如io请求
终止:当一个进程到达了自然结束点,或是出现了无法克服的错误,或 是被操作系统所终结,或是被其
他有终止权的进程所终结,它将进入终止状态。
进程终止的步骤:清除进程控制块,收回空间。
3、进程控制块(PCB)
3.1 进程控制块是进程实体的一部分,操作系统根据pcb对进程进行管理和控制。
pcb是进程存在的唯一标志。pcb放于内存中。
3.2 进程控制块中的信息
1)进程标识符
用于唯一标识一个进程,包括内部标识符和外部标识符
2)处理机状态
处理机状态信息主要是由处理机的各种寄存器中的内容组成的。
3)进程调度信息
包括进程状态,进程优先级,事件等
4)进程控制信息
包括程序和数据的地址,进程同步和通信的机制等
3.3 进程控制块的组织方式
链接方式和索引方式
链接方式:把具有同一状态的 PCB,用其中 的链接字链接成一个队列
索引方式:系统根据所有进程的状态建立几张索引表。例如,就绪索引表、
阻塞索引表等,并把各索引表在内存的首地址记录在内存的一
些专用单元中。
3.4 进程控制
进程控制一般是由 OS 的内核中的原语(primitive)来实现的。原语由由若干条指令组成,
是一种原子操作。
原子操作:是指一个操作中的所 有动作要么全做,要么全不做
引发创建进程的事件:用户登录,作业调度,提供服务,应用请求。
进程创建的步骤:
1)申请空白控制块
2)分配内存空间
3)初始化 包括标识符信息和处理机的信息
4)插入就绪队列
进程的阻塞
引起进程阻塞的事件有以下几种
1)请求系统服务
2)启动某种操作
3)新数据尚未到达
4)无新工作可做
进程阻塞过程:系统把处理机的状态信息保留在pcb中,将Pcb插入阻塞队列,将处理机
分配给新的进程。
进程的唤醒过程:系统把阻塞进程从阻塞队列移出来,并修改阻塞状态为就绪状态,把
pcb插入就绪队列中
4、 进程同步
临界资源:许多硬件资源如打印机和磁带机
临界区:访问临界资源的那段代码称为临界区
进程同步应遵循的原则:
1)空闲让进
2)忙则等待:
3)有限等待:保证在有限时间内能进入自己的临界区, 以免陷入“死等”状态。
4)让权等待:当进程不能进入自己的临界区时,应立即释放处理机
信号量机制
1)整型信号量:定义一个表示资源数目的整型量S和两个标准的原子操作wait(S)和signal(S)。
操作描述为:
wait(S): while S<=0 do no-op;
S:=S-1;
signal(S): S:=S+1;
2)记录型信号量
procedure wait(S)
var S:semaphore;
begin S.value:=S.value-1; //请求分配资源
if S.value<0 then block(S.L); //资源分配完毕,进程应调用 block 原语,进行自我阻塞,放弃处理机,
end
procedure signal(S)
var S: semaphore;
begin S.value:=S.value+1; //释放资源
if S.value<=0 then wakeup(S.L);//s.value<=0,表示阻塞队列中还有进程被阻塞,调用wakeup唤醒进程。
end
消除了忙等,采取了让权等待策略。
3)AND型信号量
基本思想:对若干个临界资源的分配,采取原子操作方 式:要么把它所请求的资源全部分配到进程,要么一个也不分配
Swait(S1,S2,…,Sn)
if Si>=1 and … and Sn>=1 then
for i:=1 to n
do Si :=Si-1;
endfor
else
place the process in the waiting queue associated with the first Si found with Si<1,and set the program count of this process to the beginning of Swait operation endif
Ssignal(S1,S2,…,Sn)
for i:=1 to n
do Si :=Si+1;
Remove all the process waiting in the queue associated with Si into the ready queue.
endfor;
5、信号量的应用
1)利用信号量实现进程互斥
设置mutex的值为1,把临界区放在wait(mutex)和signal(mutex)之间即可。
注:wait和signal必须成对出现
Var mutex: semaphore:=1;
begin parbegin
process 1:
begin
repeat
wait(mutex);
critical section
signal(mutex);
remainder seetion
until false;
end
process 2:
begin
repeat
wait(mutex);
critical section
signal(mutex);
remainder section
until false;
end
parend
2)利用信号量实现前驱关系
6、经典的进程同步问题
6.1 生产者消费者问题
1)利用记录型信号量来解决生产者消费者问题
Var mutex,empty,full:
semaphore:=1,n,0; //信号量mutex,empty,full的初值,empty代表空缓冲区的数量,full代表满缓冲区数量
buffer:array[0,…,n-1] of item; //公共缓冲区,大小为n
in,out: integer:=0,0;
begin parbegin
proceducer: //生产者
begin
repeat
producer an item nextp;
wait(empty);
wait(mutex);
buffer(in):=nextp;
in:=(in+1) mod n;
signal(mutex);
signal(full);
until false;
end
consumer://消费者
begin
repeat
wait(full);
wait(mutex);
nextc:=buffer(out);
out:=(out+1) mod n;
signal(mutex);
signal(empty);
consumer the item in nextc;
until false;
end
parend
end
2)利用ANd信号量
将1)中的wait(empty)和wait(mutex)合并成swait(empty,mutex),signal(mutex)和signal(full)合并成ssignal(mutex,full),
wait(full)和wait(mutex)合并成swait(full,mutex),signal(mutex)和signal(empty)合并成ssignal(mutex,empty)。
6.2 哲学家进餐问题
有五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌 上有五个碗和五只筷子,他们的生活方式是交替地进行思考和进餐。平时,一个哲学家进 行思考,饥饿时便试图取用其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。 进餐完毕,放下筷子继续思考。
1)用记录型信号量来描述
var chopstick: array[0,…,4] of semaphore;//定义一个信号量数组,表示五根筷子,初始化值为1
repeat
wait(chopstick[i]);
wait(chopstick[i+1] mod 5);
eat;
signal(chopstick[i+1]mod 5);
signal(chopstick[i]);
think;
until false;
可能引起死锁问题:假如五位哲学家同时拿起左手边的筷子,就会使得chopstick信号量均为0,陷入循环无线等待。
解决方案:至多允许四位哲学家同时拿起筷子;或者只有当哲学家同时获取左右两边的筷子时才能就餐,否则就释放
拿到的筷子。
2) and型信号量
var chopsticks array of semphore:[1,1,1,1,1];
repeat
think;
sswait(chopsticks[i],chopsticks[i+1]mod 5);
eat;
ssignal(chopsticks[i],chopsticks[i+1]mod 5);
until false;
6.3 读写者问题
允许多个读进程同时读一个共享对象,但不允许一个两个写对象同时写共享对象,不允许一个写和多个读
对象同时访问共享对象。
用记录型信号量描述
读者优先
var rmutex,wmutex: semaphore=1,1;//定义读互斥量,写互斥量
readCount integer=0;//读的进程数量
begin
parbegin
读
reader:begin
repeat
wait(rmutex);
if(readCount==0) wait(wmutex); //第一个读进程进来,让wmutex变为0,阻塞写进程
readCount++;
signal(rmutex);
read;
wait(rmutex);
readCount--;
if(readcount==0) signal(wmutex);//所有读进程结束,释放写进程。
signal(rmutex);
until false
end
写
writer:begin
repeat;
wait(wmutex);
write;
signal(wmutex);
until false
end
parend
end
写者优先
semaphore fmutex=1, rdcntmutex=1, wtcntmutex=1, queue=1;
//fmutex --> access to file; rdcntmutex --> access to readcount
//wtcntmutex --> access to writecount
int readcount = 0, writecount = 0;
void reader(){
while(1){
wait(queue);
wait(rdcntmutex);
if(0 == readcount)wait(fmutex);
readcount = readcount + 1;
signal(rdcntmutex);
signal(queue);
//Do read operation ...
wait(rdcntmutex);
readcount = readcount - 1;
if(0 == readcount)signal(fmutex);
signal(rdcntmutex);
}
}
void writer(){
while(1){
wait(wtcntmutex);
if(0 == writecount)wait(queue);
writecount = writecount + 1;
signal(wtcntmutex);
wait(fmutex);
//Do write operation ...
signal(fmutex);
wait(wtcntmutex);
writecount = writecount - 1;
if(0 == writecount)signal(queue);
signal(wtcntmutex);
}
}
每个读进程最开始都要申请一下 queue 信号量,之后在真正做读操作前即让出(使得写进程可以随时申请到 queue)。而只有第一个写进程需要申请 queue,之后就一直占着不放了,直到所有写进程都完成后才让出。等于只要有写进程提出申请就禁止读进程排队,变相提高了写进程的优先级。
7、 进程通信
定义:进程之间的信息交换
三类高级通信机制包括共享存储器系统,消息传递系统,管道通信。
共享存储器系统:相互通信的进程共享某些数据结构或共享存储区,进程之间能够通过这些空间进行通信。
共享数据结构的例子:生产者——消费者共享共用缓冲区。
消息传递系统:进程间的数据交换是以格式化的消息(message)为单位的,在计算机网络中message称为报文。
管道通信:管道是指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享 文件,又名 pipe 文件。
写进程以字符流的形式向管道文件中写入数据,读进程以字符流形式从管道中接收数据。
消息传递通信的实现方法:直接通信和间接通信
直接通信:写进程直接发送原语给读进程
间接通信:读写进程通过一个中间的共享数据结构来传递消息
8、线程
8.1 、引入线程的理由:进程是资源的拥有者,在进程的创建,销毁和切换过程中,会花费不少的时间,因此引入
线程这个只拥有少量资源的调度和分派的基本单位。
线程和进程的比较:1)进程是拥有系统资源的基本单位,线程是独立调度 和分配的基本单位,只拥有少量资源。
一个进程可拥有多个线程,多个线程共享进程中的资源。
2)进程的切换需要很大的开销,而线程不需要
8.2 线程的属性
1)轻型实体:只拥有少量保证独立运行的资源,如线程控制块
2) 独立调度和分派的基本单位
3)并发执行
4)共享进程资源:同一个进程中的线程共享资源
8.3 线程的状态
和进程类似,可参考进程的状态
8.4 线程间的同步和通信
互斥锁,条件变量,信号量
8.5 线程的实现方式
两种方式:内核支持线程和用户级线程
内核支持线程:存在于内核空间,线程的创建,撤销和切换依赖于系统内核,调度以线程为单位,一个线程阻塞,可
调用同一个进程中的其他线程运行。
用户级线程:仅存在于用户空间,线程的创建,撤销和切换不必依赖于系统内核,调度以进程为单位,如果一个
线程阻塞,同一个进程中的所有其他线程会被阻塞。