序言
1.OS以进程、线程的方式在CPU中执行静态保存在外存(内存)中的程序,进程的构成与状态转化,特别是进程的切换;
2.当有多个进程处于就绪态,有哪些常见的挑选以执行方式;
3.并发执行(乱序发射)的进程,共享内存等资源,很可能修改其他程序的变量,需作同步处理来控制;
此外有些资源如打印机不能共享,需作互斥处理;
Question
- 程序分割成线程执行的代码怎么写?用进程管理程序实现,python如何可以实现多线程并行吗,如何实现?
- 为什么用户级线程中,1个线程因系统调用被阻塞会导致其他线程也被阻塞?
因为在大多数操作系统中,内核是单线程运行的,所以当一个线程被阻塞时,它会释放CPU资源。这将导致其他线程在等待CPU资源时也被阻塞,其他线程只能要么等待,无法保存现场(工作在用户层)再切换,produce block。
在内核级线程中,中断的保存/恢复现场为内核的功能,可以实现线程间的调度,且支持多处理机。
在组合方式中,
a. 用户进程的多个线程对应内核级的多个线程,用户1个线程系统调用的内核级线程A被阻塞,CPU可以切换到其他线程的不同内核级线程B;
b. 多核处理器可对应多个内核级线程;
- 系统动态DLL库是个玩意?
进程
- 进程是一段指令流,还是?process=PCB+data section+code section
PCB=处理器(时间片轮转比其他设备周期短,单成1列)+其他资源+进程控制与管理信息+PIDUUID - 不同的进程是如何利用系统资源(IO,MM,CPU)的?
- 进程之间如何通信?进程如何运转?
线程
- 线程是处理器调度与分配的基本单元
- 进程是除了处理器之外其他资源的分配单位------IO,MM的分配单元
进程调度
- 线程的调度类似,不过同一个进程的线程切换不用替换进程上下文,运行时间的算法需要更新(更复杂了)
同步与互斥
同步与互斥都围绕共享资源的使用:
1.对于那些只能被单独访问的共享资源,要做互斥处理;
2.对于那些可以被共享访问的资源,虽然RAR没问题,但RAW,WAR,WAW 均可能导致最终的共享资源结果不同,要作同步处理。
互斥可通过用户程序中使用API实现lock和release
- 数据库中,为了防止数据的脏读,需要给数据表或表项加上二阶段锁或S锁,
- 为什么要引入同步与互斥?------进程之间共享资源,不同进程并发执行,封闭性丧失,为了得到逻辑正确的结果。
- 临界资源:一次只能被1个进程访问的资源
- 临界区:访问临界资源的代码;
- 进入区:访问临界资源前,要把临界区的标志设置为正在访问;
- 退出区:将正在访问的临界区的signal清除;
- 剩余区:代码中的其余部分?
- 同步:如A和B通过缓冲区实现数据传送,缓冲区满了,A必须等待B读取数据,才能接着传送;缓冲区空,B必须等待A传入数据,才能接着传送;缓冲区相比于📫机制,再与缓冲区大小固定。
- 互斥:对临界资源的访问;而同步,是为了正确完成任务而协调工作次序;
双标志互斥alg的问题是进程的线程乱序执行导致的,只有Peterson alg能在所有乱序执行保证同步的3大准则:
空则让进,忙则等待,有限等待,(让权等待)。
硬件实现有中断屏蔽和硬件指令2种方法:
- 中断屏蔽:关中断,临界区,开中断。
- 硬件指令方法:TestandSet或Swap原子操作
c++
boolean TestandSet(boolean *lock){
boolean old;
old=*lock;
*lock=true;
return old;;
}
while TestandSet(&lock);
临界区
lock=false;
剩余区
comment:
- 为每个临界资源设置一个共享布尔变量
- 和下面的Swap都是互斥锁
c++
Swap(boolean *a,boolean *b){
boolean temp;
temp=*a;
*a=*b;
*b=temp;
}
key=true;
while(key!=false)
swap(&key,&lock);
临界区
lock=false
剩余代码
comment:
- 当lock=false(代表资源空闲),设置key=false,lock=ture,标识该进程独占临界资源;
- swap和testandset其实是硬件原子操作,不可分,不会出现多个进程进入lock=false的swap函数,不会出现共用临界资源;
- 当lock=true,未得到临界资源的进程会一直while查询,占用处理机资源,不能实现让权等待;
对比
实现临界区互斥的硬件方法和软件方法各有什么优势?
硬件方法的优势:
原子性操作:硬件方法通常使用特定的指令或硬件机制来实现原子操作,如原子交换(Atomic Exchange)或测试并设置(Test-and-Set)。这些操作在硬件级别上保证了操作的原子性(不会出现共用临界资源),无需额外的软件开销。
性能:硬件方法通常比软件方法更高效,因为它们直接利用了硬件的特性,如高速缓存和并行处理能力。这使得硬件方法更适合于对性能要求较高的应用场景。
软件方法的优势:
可移植性:软件方法不依赖于特定的硬件指令或机制,因此更易于在不同的平台上实现和移植。这使得软件方法更具通用性,可以应用于不同类型的系统和架构。灵活性:软件方法通常可以更灵活地实现复杂的同步机制,如信号量、条件变量等。这些机制提供了更多的控制和功能,可以满足不同线程同步需求。
容错性:软件方法可以在一些特殊情况下提供更好的容错性。例如,当硬件出现故障或不支持某些原子操作时,软件方法可以通过算法和复杂的同步机制来弥补。
需要注意的是,硬件方法和软件方法并不是互斥的选择,它们可以结合使用来提供更可靠和高效的同步机制。例如,硬件指令可以用于实现基本的原子操作,而软件方法可以用于构建更复杂的同步机制。选择何种方法取决于具体的应用需求、平台特性和性能要求。
解决临界区------互斥锁+信号量
互斥锁更easy
cpp
acquire(){
//锁被占用则等待
while(available!=true)
;
//锁空闲,占用后,将锁设为false
available=false;
}
release(){ //释放锁
available=true;
}
信号量(系统中的资源数目)mechanism
采用两个不可中断(软件中由屏蔽中断方法实现)的原语------wait和signal来管理
整形信号量
cpp
wait(S){ //请求资源
while(S<=0) ; //若没有资源,则等待
S--; //占用资源,S--
}
signal(S){
S++; //释放资源
}
Defect:wait在无资源可用,会一直occupy cpu------while query,违背 让权等待
记录型信号量
- S.value>0,表示可用的资源数目;
- S.value<=0,标识阻塞的进程数目;
- 每种资源对应1个S
cpp
typedef struct{
int value;
struct process *L;
} semaphore(信号量);
请求资源
cpp
void wait(semaphore S){
S.value--; //S>0则资源数--;S<0则阻塞进程数++;
if(S.value<0){
add this process to S.L;
block(S.L);
}
}
释放资源
cpp
voic signal(semaphore S){
S.value++; //资源数++或阻塞进程数--
if(S.value<0){ //若为后者,唤醒该进程------资源来临
remove process P from S.L;
wakeup(P);
}
}
信号量的饮用
只有互斥用到了临界资源;
同步和前驱只关于特定变量的逻辑序;
同步
------进程按照(资源的)特定顺序执行
semaphore S=0;
P1(){
x;
V(S);
...
}
P2(){
...
P(S);
y;
...
}
互斥------只有1个进程能进入临界区
-
P(S)请求资源;V(S)释放资源
semaphore S=1;
P1(){
...
P(S);
critical area;
V(S);
}
P2(){
...
P(S);
critical area;
V(S);
}
用同步实现前驱关系------每个偏序关系用1个资源
S1(){
V(a1);
}
S2(){
P(a1);
V(b1);V(b2);
}
分析同步、互斥、前驱关系,设置信号量,确定P,V
管程
背景:针对每个共享变量,所有使用变量都要设置P(),V()操作;
改进:每个共享资源,只要1个共享数据结构demo来管理;
管程=类,成员变量为共享资源的数目,成员函数为对共享资源的操作,如对互斥变量的take_away和release,
多个共享资源可以用记录型信号量表示,
不知道为什么要把条件变量单独拎出来,用来表示进程被阻塞的原因,
- 如果两个进程有先后关系,它们之间可以一个条件变量的lock和release,
- 互斥资源可以且应该用条件变量表示,就是整数型信号量
互斥问题的求解
- 互斥资源的访问用锁表示,
- 像生产者、消费者问题,明明可以用num表示缓冲区的数目,为了符合P(锁),V(释放)的形式(值得唾弃),
用full和empty两个信号量,并用mutex实现对full和empty的互斥修改(simultaneously 只有一个生产者/消费这个能访问缓冲区)------然鹅并不是~~ - 像放苹果橘子问题,可以把事件的同步与互斥用箭头表示,每个同步关系用一个针对互斥资源的P(),V()表示;
- 读者-写者问题,终于用count表示资源读者的数量,为了count错误修改,自然加个mutex的PV锁;
写者与其他人的互斥用rw锁表示,读者与写者的互斥,读者的同步(on count)非常秀------but only分条件,进行rw的判断,所以应该放开编程,appleorange也可以进行while(apple=0 and orange=0)的判断,不用和书上一致; - 吸烟者问题,吸完烟要向供应商发信号,也要用个锁~~
死锁
- 可剥夺资源:优先级高的可以抢占优先级低的进程的资源;
- 有循环等待未必构成死锁,
- 若每类资源只有1个资源,资源分配图含圈 ↔ \leftrightarrow ↔ 系统出现死锁
PS:CSDN图片id用的是128位的散列值
死锁处理
安全状态:按照某种推进顺序,能满足每个进程对资源的最大需求,使每个进程可顺序完成;
死锁避免在分配资源给进程的过程中拒绝进入dangerous state
banker's alg一般都是DFS+回溯,可以用BFS?
破圈法是为了寻找有环图的最小生成树,
死锁检测定理------待证
1)找出非阻塞(请求的资源数量均不大于系统的空闲资源数量)的非孤立进程Pi,释放Pi所有资源;
2)重复1),若消去所有边,图可完全简化 ↔ \leftrightarrow ↔ 不死锁;
预防中的消除循环等待和请求并保持,均不可行,