信号量与互斥锁都是用于多线程编程中,以实现资源共享和线程同步的机制,但它们在应用场景、实现方式和性能特点上有所不同。以下是详细介绍:
- 应用场景。信号量主要用于线程同步,其核心思想是控制对共享资源的访问许可,当资源可用时,允许线程继续操作;当资源被占用时,线程则阻塞直到资源变得可用。而互斥锁主要用于线程互斥,确保同一时刻只有一个线程能访问特定的资源,防止资源被多个线程同时访问。
- 实现方式。信号量的值可以是非负整数,这表示了可用资源的数量。当信号量的值大于0时,表示有可用资源,线程可以继续操作;当信号量的值为0时,表示没有可用资源,线程需要阻塞直到资源变得可用。而互斥锁的值通常只能为0或1,表示资源是否被锁定。
- 性能特点。信号量不仅用于资源同步,还可以用于进程间通信,而互斥锁仅用于线程间通信。互斥锁在锁定资源时,所有试图访问该资源的线程都会被阻塞,直到资源被解锁。而信号量在资源被锁定时,允许其他线程继续执行某些任务,直到资源被释放。
总结来说,信号量更侧重于资源共享和线程间的协作,而互斥锁更侧重于资源的安全访问和线程间的互斥。
互斥锁:
互斥锁是一种保护机制。上锁后其他线程不能进入保护区域的代码,直到锁被释放。
mutex函数在内核文件include\linux\mutex.h中声明,如下表:
mutex的结构体定义如下:
它里面有一项成员“struct task_struct *owner”,指向某个进程。一个mutex只能在进程上下文中使用:谁给mutex加锁,就只能由谁来解锁。
而semaphore并没有这些限制,它可以用来解决“读者-写者”问题:程序A在等待数据──想获得锁,程序B产生数据后释放锁,这会唤醒A来读取数据。semaphore的锁定与释放,并不限定为同一个进程。
信号量:
信号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该 信号量的下一个进程。信号量的值与相应资源的使用情况有关。当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。注意,信号量的值仅能由PV操作来改变。
p操作和v操作
p操作和v操作是不可中断的程序段,称为原语。P,V原语中P是荷兰语的Passeren,相当于英文的pass, V是荷兰语的Verhoog,相当于英文中的incremnet。且在P,V原语执行期间不允许有中断的发生。
首先应弄清PV操作的含义:PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下:
P(S):①将信号量S的值减1,即S=S-1;②如果S>=0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。
V(S):①将信号量S的值加1,即S=S+1;②如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。
PV操作的意义:
我们用信号量及PV操作来实现进程的同步和互斥。PV操作属于进程的低级通信。
对信号量有4种操作:
1. 初始化(initialize),int set_init(sem_t *sem, int pshared, unsigned int value);//第二参数为0表示进程间不共享
2. 等信号(wait),int sem_wait(sem_t *sem);//信号量大于1时,减一并返回;小于1时线程阻塞。
3. 给信号(signal)int sem_post(sem_t *sem);//信号量加一
4. 清理(destory) int sem_destory(sem_t *sem);
初始化semaphore之后,就可以使用down函数或其他衍生版本来获取信号量,使用up函数释放信号量。我们只分析down、up函数的实现。
down函数的实现:
如果semaphore中的count大于0,那么down函数就可以获得信号量;否则就休眠。在读取、修改count时,要使用spinlock来实现互斥。
休眠时,要把当前进程放在semaphore的wait_list链表中,别的进程释放信号量时去wait_list中把进程取出、唤醒。
代码如下:
up函数的实现:
如果有其他进程在等待信号量,则count值无需调整,直接取出第1个等待信号量的进程,把信号量给它,共把它唤醒。
如果没有其他进程在等待信号量,则调整count。整个过程需要使用spinlock来保护,代码如下:
semaphore和mutex的区别
信号量是一种同步机制。semaphore中可以指定count为任意值,当值大于0代表有可用资源,则允许继续操作,否则线程阻塞,等待可用资源。比如有10个厕所,所以10个人都可以使用厕所。
而mutex的值只能设置为1或0,只有一个厕所。如果资源大于1时使用互斥锁,则就算资源数大于1时,也只能有一个线程进入操作,其余线程必须阻塞。
是不是把semaphore的值设置为1后,它就跟mutex一样了呢?不是的。
两者主要区别如下表所示:
信号量可用于进程通信和线程通信,而互斥锁只能用于线程通信。