信号量机制

  1. 首先 信号量机制是一种进程通信、同步的工具 可以用来解决互斥进程以及同步进程问题
    1. 互斥进程的概念(不能让多个进程访问同一块变量,这些进程为互斥进程):
      1. 可能造成的问题:多个进程,同时访问同一块变量,可能会造成内存泄漏,例如 a进程删除这块内存中的某个数据,b进程修改这块内存中的这个数据。
    2. 进程同步的概念(a进程需要以b进程的结果为前提才能继续运算)
      1. 同时运行多个进程时 若进程没有输入 则可能会造成cpu死锁

为解决上述同步互斥问题,引入了信号量机制

一个进程在收到指定信号前,会被迫在一个确定的或者需要的地方阻塞,从而保持进程的同步或者互斥

当信号量<=0时将进程阻塞

常见的信号量有两种数据结构

  1. 整形信号量:一个表示资源数目的整形变量s
  2. 记录型信号量:在整形信号量的基础上增加了一个数组 存放了不能访问同一资源而阻塞的各个进程

P操作:对信号量进行减一

V操作:对信号量进行加一

信号量的本质是一个静态变量


如何利用信号量机制解决线程互斥问题

互斥问题:避免同时操作内存时造成泄露

那么就需要在资源被占用时 让其他进程阻塞,从而保护被占用的资源

因此这里规定,信号量 mutex 用0表示临界资源被占用,信号量用1表示未被占用;互斥中,信号量默认为1,当信号量<=0时将进程阻塞

因此当进程互斥时,首先申请一块内存,然后使用,最后释放这块内存,当a进程申请了这块内存之后,b进程即处于阻塞状态,当a进程释放了这块内存,b进程申请完成进入使用;

具体流程:

  1. p(mutex)
  2. 进入临界区
  3. v(mutex)

其中 申请与释放的本质 就是对信号量这一静态变量进行修改,然后通过判断这个静态变量的值,来判断进程是否执行=

线程互斥问题解决的本质

首先信号量默认为1 所以当a程序申请变量时,首先会对信号量进行p操作即1-1 得到0 然后返回mutex<=0的值,为真时即成功申请到了内存,若为假则进入阻塞,等待io

当内存使用完毕后,使用v操作进行内存释放,实际上是将信号量初始化为1,因为1表示内存未被占用,即0+1=1

因此在解决互斥问题时 可以笼统的认为 p操作是申请一块内存,v操作是释放内存,而使用完内存后必须手动释放!

此时,无论进程谁先谁后,只要拿到了内存即可进行资源的读写,其他没拿到内存的进程只能等待内存释放,这就是线程锁的概念


信号量解决同步问题

同步问题:进程并发执行

在互斥问题中,我们将信号量的默认值设置为1,但在进程同步问题中,我们将信号量的默认值设置为0

此时若执行p操作 将会直接通过,于是我们将阻塞的判断条件修改为 s<0,也就是说,当信号量<0时,将进程阻塞

解决同步问题的本质

a,b进程并发执行时

b进程需要a的输入值。那么b操作会申请一块变量(信号量)由a进程控制的内存,若信号量执行p操作后小于0 那么意味着该进程需要等待,即进入阻塞状态

a进程拿到b进程需要的输入后,a进程对这块内存进行释放即修改变量(信号量+1)将信号量的值从0推进至1,处于阻塞状态的b进程的p操作(信号量-1)执行后检测到信号量值为0 已不满足信号量<0 的条件 即将进程唤醒,继续执行


因此 可以简单的认为在解决同步问题时

从内存占用的角度上来讲:

p操作是申请内存,信号量的初值是1 将其减1   若信号量<0则阻塞 >0则唤醒

v操作是释放内存,将信号量+1 恢复初值

从通信的角度来讲:

p操作是以信号量为标志发送一个等待输入的信号通知另一个进程,从而阻塞当前进程

v操作是以信号量为标志发送一个已拿到需要输入的值的信号回复那个进程,从而唤醒那个进程

我个人更倾向于内存角度,首先信号量的本质就是静态变量。不管是同步还是互斥。从物理层面上来说都是在改变信号量的基础上完成的,抽象的认为是对内存的锁定与解锁会更贴近编程思想,所以从内存占用上来说会更好一些。