1.进程和程序的区别
- 动态性:
- 是进程最基本的特性,表现为由创建而产生,由调度而执行,因得不到资源而暂停执行,由撤销而消亡。有一定的生命期。
- 程序只是一组有序的指令集合,是静态实体。
- 并发性:
- 是进程的重要特征,同时也是OS的重要特征。引入进程的目的是为了使其程序能和其他进程的程序并发执行。
- 程序是不能并发执行的
- 独立性:
- 是指进程实体是一个能独立运行的基本单位,也是系统中独立获得资源和独立调度的基本单位。
- 对于未建立任何进程的程序,不能作为独立单位参加运行
2.PCB(进程控制块,Process Control Block)
操作系统管理控制进程运行的信息集合,操作系统用 PCB 来描述进程的基本情况以及运行变化的过程,PCB 是进程存在的唯一标志。
- 进程的创建:为进程创建 PCB。
- 进程的终止:回收其 PCB。
- 进程的组织管理:通过对 PCB 的组织管理实现。
PCB提供给进程管理的信息:通用寄存器、指令计数器、程序状态字、用户栈指针
PCB提供给进程调度的信息:进程状态、进程优先级、事件、其他信息
PCB的组织方法:线性方式、链接方式、索引方式
3.进程的三种基本状态
1.就绪状态:进程已获得除CPU之外的所有必要资源,只等待CPU时的状态。一个系统会将多个处于就 绪状态的进程排成一个就绪队列。
2.执行状态:进程已获得CPU,正在执行。单处理机系统中,处于执行状态的进程只有一个;多处理机 系统中,有多个处于执行状态的进程。
3.阻塞状态:正在执行的进程由于某种原因而暂时无法继续执行,便放弃处理机而处于暂停状态,即进 程执行受阻。(又称等待状态或封锁状态)
就绪状态----》执行状态:进程被调度,分配到cpu资源
执行状态----》就绪状态:时间片用完
执行状态----》阻塞状态:I/O请求
阻塞状态----》就绪状态:I/O完成
4.信号量机制
整型信号量
用一个整数型的变量作为信号量,用来表示系统中某种资源的数量
int s = 1; //初始化整型信号量s,表示当前系统中可用的打印机资源数量
void wait(int s){
while(s <= 0);//如果资源数不够,就一直循环等待
s=s-1; //如果资源数够,就占用一个资源
}
void signal(int s){ //signal原语,相当于“退出区”
s=s+1; //使用完资源后,在退出区释放资源
}
进程P0:
wait(S); //进入区,申请资源
使用打印机资源 //临界区,访问资源
signal(S); //退出区,释放资源
进程P1:
wait(S);
使用打印机资源
signal(S);
进程P2:
wait(S);
使用打印机资源
signal(S);
记录型信号量
整型信号量的缺陷是存在“忙等”问题,因此又提出了“记录型信号量”,即用记录型数据结构表示的信号量,可以用记录型信号量实现系统资源的“申请”和“释放”,进程互斥、进程同步
//记录型信号量的定义
typedef struct{
int value; //剩余资源数
struct process *L; //等待队列
} semaphore;
//某进程需要使用资源时,通过wait原语申请
void wait(semaphore S) {
S.value--;
if(S.value<0){
block(S.L);
/*如果剩余资源数不够,使用block原语使进程从运行态进入阻塞态,并挂到信号量S的等待队列(阻塞队列)中*/
}
}
//进程使用完资源后,通过signal原语释放
void signal(semaphore S) {
S.value++;
if(S.value<=0){
wakeup(S.L);
/*释放资源后,若还有别的进程在等待这种资源,则使用wakeup原语唤醒等待队列中的一个进程,该进程从阻塞态变为就绪态*/
}
}
信号量机制实现进程互斥
互斥信号量的初值实际上是标志某种资源的数量,而我们这里临界区同一时间段内值允许一个进程来访问,所以可以把临界区理解成一种特殊的资源,该资源只有一个,只能被分配给一个进程使用,只有这个进程释放了,才能被其他进程使用。如果一个进程需要使用临界区这种特殊的资源的时候,那么使用之前就应该对所对应的信号量进行P(通过)操作,之后进行V(释放)操作。
步骤:
- 分析并发进程的关键活动,划定临界区(如:对临界资源打印机的访问就应放在临界区)
- 设置互斥信号量mutex,初值为1
- 在临界区之前执行P(mutex)
- 在临界区之后执行V(mutex)
注意:对不同的临界资源需要设置不同的互斥信号量。
P、V操作必须成对出现。缺少P(mutex)就不能保证临界资源的互斥访问。缺少V(mutex)会导致资源永不被释放,等待进程永不被唤醒。
//信号量机制实现互斥
semaphore mutex = 1; //初始化信号量
P1(){
···
P(mutex); //使用临界资源前需要加锁
临界区代码段···
V(mutex); //使用临界资源后需要解锁
···
}
P2(){
···
P(mutex);
临界区代码段···
V(mutex);
···
}
//注意:对不同的临界资源需要设置不同的互斥信号量
信号量机制实现进程同步
进程同步:要让各并发进程按要求有序地推进。
用信号量实现进程同步步骤:
1.分析什么地方需要实现“同步关系”,即必须保证“一前一后”执行的两个操作(或两句代码)
2.设置同步信号量S,初始为0;
3.在“前操作”之后执行V(S);
4.在“后操作”之前执行P(S);
//信号量机制实现同步
semaphore s = 0; //初始化同步信号量,值为0
p1(){
代码1;
代码2;
V(s);
代码3;
}
p2(){
P(s);
代码4;
代码5;
代码6;
}
//保证了代码4一定实在代码2之后执行
/*若先执行到V(S);操作,则S++后S=1。之后党执行到P(S)操作时,由于S=1,表示有可用资源,会执行S--,S的值变回0,P2进程不会执行block原语,而是继续往下执行代码4。
若先执行到P(S)操作,由于S=0,S--后S=-1,表示此时没有可用资源,因此P操作中会执行block原语,主动请求阻塞。之后当执行完代码2,继而执行V(S)操作,S++,使S变为0,由于此时有进程在该信号量对应的阻塞队列中,因此会在V操作中执行wakeup原语,唤醒P2进程,这样P2就可以继续执行代码4*/