信号量和同步互斥

本文介绍了进程的互斥与P、V操作,包括临界资源和临界区的概念,信号量的结构、操作及实现,同步互斥模型的多种情况,如生产者 - 消费者问题不同场景的信号量实现,还提及删除信号量的处理及资源释放。此外,因测试基于ITRON的OS信号量资源而整理此内容。
进程的互斥与P、V操作

一、临界资源

什么是临界资源:
任何时候只允许一个进程使用的资源为临界资源。

什么是临界区:
访问临界资源的代码段为临界区。

例如:
代码段1
a = count;
a--;
count = a;
代码段2
b = count;
b++;
count = b;
为临界区,count为临界资源。

对临界资源的访问必须满足以下条件:
一次只能有一个进程进入,其他进程等待。
进入者必须在有限时间内退出。
等待者应该有机会进入。

二、信号量

信号量的结构模型:(S,Q)
在2.4.x内核中,信号量数据结构定义为(include/asm/semaphore.h):
struct semaphore {
atomic_t count;
int sleepers;
wait_queue_head_t wait;
#if WAITQUEUE_DEBUG
long __magic;
#endif
};

信号量操作:
1) 初始化
2) P操作
a) S--
b) if(S < 0) 在Q队列中睡眠
else 进入
3) V操作
a) S++
b) if(S <= 0) 唤醒一睡眠进程(等待队列中有进程在睡眠)
else 继续
P操作可以理解为申请资源,V操作可以理解为释放资源,在2.4.x内核semaphore结构中,count是资源计数,为正数或0时表示可用资源数,-1表示没有空闲资源且有等待进程,至于等待进程的数量并不关心,实现中依赖了sleepers,比较复杂,但好处是是up操作非常简单,只需汇编原子地将count加1,如果小于等于0表示有进程等待,调用__up_wakeup() --> __up(),否则返回。linux中的实现可以到arch/i386/kernel/semaphore.c中看到(2.4.x kernel),__down()和__up()。

信号量实现:
用ITRON的一个简单实现<An ITRON Implementation>做为例子。我粗略地看了一下代码,关于信号量(semaphore)的P/V操作,有计数器控制和等待队列。
SEMCB(semaphore control block)结构中有id,队列,计数器等成员。

OS_AcquireSemaphoreTimeOut()
{ ...
lock()
semcb->semcnt-- // counter --
if(semcb->semcnt < 0)
{
make_wait() // make current task in wait state
queue_insert_tpri() // insert by priority to the semaphore's
//wait queue,which is a circular queue
}
unlock()
...
}

OS_FreeSemaphore()
{ ...
lock()
if(semcp->semcnt < 0)
{
wait_release_ok()
|
-> wait_release() // release a task from wait state
}
semcb->semcnt++ // counter ++
unlock()
...
}
注:V操作中S先加1再判断是否小于等于0和先判断是否小于0再加1是等价的。
注:在这个实现中,semcnt值不仅正负分别表示资源空闲或有进程等待,其绝对值也是有意义的,为负时值代表等待的进程数,为正或零时值代表空闲的资源数。

三、同步互斥模型

S=1
A
P(S)
临界区
V(S)

B
P(S)
临界区
V(S)

生产者消费者问题
(生产者计算生成数,消费者打印之。)

1个生产者1个消费者,缓冲buf为1
2个信号量实现模型:put = 1 get = 0
A
计算x
P(put)
buf = x
V(get)
B
P(get)
y = buf
V(put)
打印y

1个生产者2个消费者,缓冲buf为1(2个消费者一个打印奇数一个打印偶数)
3个信号量实现模型:put = 1 getj = 0 geto = 0
A
计算x
P(put)
buf = x
if(x为奇)
V(getj)
else
V(geto)
B
P(getj)
y = buf
V(put)
打印y
C
P(geto)
y = buf
V(put)
打印y
这个模型比较直观,但是用了3个信号量。在计算出x后判断其奇偶再"V"不同的信号量。
如果只使用2个信号量如何实现,生产者计算出x后直接"V"一个信号量,2个消费者取出后判断是否打印还是不于理会。
2个信号量实现模型:put = 1 get = 0
A
计算x
P(put)
buf = x
V(get)
B
P(get)
y = buf }
if(y为奇)
{
V(put)
打印y
}
else
V(get)
C
P(get)
y = buf
if(y为偶)
{
V(put)
打印y
}
else
V(get)

1个生产者1个消费者,缓冲buf为m
2个信号量实现模型:put = m get = 0
A
计算x
P(put)
buf[t] = x
t = (++t) % m
V(get)
B
P(get)
y = buf[k]
k = (++k) % m
V(put)
打印y

N个生产者M个消费者,缓冲buf为m
4个信号量实现模型:put = m get = 0 T = 1 K = 1
A
计算x
P(put)
P(T)
buf[t] = x
t = (++t) % m
V(T)
V(get)
B
P(get)
P(K)
y = buf[k]
k = (++k) % m
V(K)
V(put)
打印y

四、删除一个信号量

删除一个信号量,系统应该释放一些资源。如果无进程在等待此信号量,处理比较简单。
如果有进程在等待此信号量,如何处理这些进程,有人说kill,但我看到的一个实现是唤醒。
除进程之外,这个等待队列也需要释放,否则会造成内存泄露。
不仅是删除一个信号量,在系统中当删除某个资源时,会释放等待队列的所有task。

OS_DelFlag --|
OS_DelMessageBuffer --|--> call wait_delete(QUEUE *) --> OS_DelSemaphore --|

while()
{wait_release();...} // release all tasks blocked on specified wait queue

五、由来

为什么敲打这么个东西。因为有个自主实现的基于ITRON的OS需要测试。在测试其信号量资源时我补充了一个测试用例,即当有进程在等待一个信号量时删除此信号量系统会有如何反应。基于此,先给测试人员做了presentation介绍信号量及同步互斥模型。然后粗略地看了看linux 2.4.x,ITRON和ucLinux三个系统的P/V操作和删除信号量的实现。因工作繁忙没有深入研究,简单整理一下用了两个小时边解bug边偷闲着输入完。然后steedhorse帮忙做了检查,经过了一个小时的讨论,改了一些错误,在此感谢。
有的问题我不是很清楚,希望看了后能提出来大家讨论。
<think>我们之前已经详细讨论了信号量的基本概念操作,以及它在同步互斥中的应用。现在用户的问题聚焦在“信号量同步互斥之间的区别及用法”。根据引用内容,我们可以总结如下:1.互斥信号量用于互斥时,通常是一个二进制信号量(初值为1),确保一次只有一个进程/线程访问临界区资源。这解决的是资源互斥访问问题。2.同步信号量用于同步时,通常是计数信号量,用于控制进程/线程之间的执行顺序。例如,在生产者-消费者问题中,通过两个信号量(emptyfull)来协调生产者消费者的执行,保证在缓冲区有空间时生产者才能生产,缓冲区有产品时消费者才能消费。从引用的内容中:-引用[1]提到了使用信号量描述前驱关系(同步)以及生产者-消费者问题(同步),同时也提到了互斥信号量的初值为1(互斥)。-引用[2]中详细讨论了在C语言中信号量用于进程同步互斥以及线程同步互斥的不同方式,并给出了示例代码。-引用[3]补充了POSIX信号量的两种实现机制(无名信号量命名信号量),并说明无名信号量通常用于线程间(共享内存),命名信号量用于进程间(不共享内存)。因此,我们这样回答用户:##信号量同步互斥中的区别及用法###1.目的不同-**互斥**:防止多个进程/线程同时访问某个共享资源(临界区)。互斥要求一次仅一个进程/线程进入临界区。-**同步**:协调多个进程/线程之间的执行顺序。同步要求某些事件发生(比如某个条件满足)后,进程/线程才能继续执行。###2.信号量类型-**互斥**:通常使用**二进制信号量**(取值0或1),信号量初始值设为1。例如:```csem_tmutex;sem_init(&mutex,0,1);//初始值为1的互斥信号量```-**同步**:使用**计数信号量**(取非负整数),其初始值根据资源数量或条件设定。例如,生产者-消费者问题中:```csem_tempty;//表示缓冲区空闲槽位数量sem_init(&empty,0,BUFFER_SIZE);//初始值为缓冲区大小sem_tfull;//表示缓冲区已使用槽位数量sem_init(&full,0,0);//初始值为0```###3.操作逻辑-**互斥**:在临界区前后分别执行`P(S)``V(S)`操作。伪代码示例:```P(mutex);//加锁//访问临界区V(mutex);//解锁```任意时刻只有一个进程/线程进入临界区。-**同步**:信号量操作通常成对出现在不同的进程/线程中,以实现事件等待事件通知。例如在生产者-消费者问题中:-**生产者**:```cP(empty);//等待缓冲区有空位//生产数据并放入缓冲区V(full);//增加已用槽位,通知消费者```-**消费者**:```cP(full);//等待缓冲区有数据//取出数据消费V(empty);//增加空槽位,通知生产者```通过`empty``full`两个信号量的配合,实现生产消费的协调。###4.总结-互斥信号量本质是解决资源独占访问问题,通过二进制信号量的`P/V`操作实现锁机制[^1]。-同步信号量则是通过计数信号量实现进程/线程间的等待-通知机制,用于条件协调[^2]。---根据引用内容,我们可以生成相关问题:§§相关问题§§1.在生产者-消费者问题中,如何保证生产者消费者不会相互阻塞?2.除了信号量,还有哪些机制可以实现进程同步?3.为什么互斥信号量的初始值通常为1?4.计数信号量的初始值应该如何确定?5.信号量互斥锁在实现互斥时有何异同?注意:在回答中引用了引用[1]引用[2],所以我们在适当位置添加引用标识。</think> ### 信号量同步互斥中的区别及用法 #### 一、核心区别 1. **目的不同** - **互斥(Mutual Exclusion)**:确保共享资源同时只能被一个进程/线程访问[^1] 数学表达:$P_{\text{互斥}}(S) \rightarrow CS \rightarrow V_{\text{互斥}}(S)$,其中$S_{\text{初始}} = 1$ - **同步(Synchronization)**:协调进程/线程的执行顺序,确保特定事件发生后才能执行后续操作[^2] 2. **信号量类型** | 类型 | 互斥应用 | 同步应用 | |------------|--------------------|-------------------------| | 二进制信号量 | ✅(锁机制) | ❌ | | 计数信号量 | ❌ | ✅(协调多资源访问)[^1] | 3. **初始化值** - 互斥信号量初始值:$S_{\text{初始}} = 1$ - 同步信号量初始值:$S_{\text{初始}} = N$(N为资源数量)[^3] #### 二、互斥信号量使用范式 ```c sem_t mutex = 1; // 初始化 void process() { P(mutex); // 进入临界区前加锁 // 访问共享资源 V(mutex); // 退出临界区后解锁 } ``` 数学保证:$ \forall t, \sum P_t(mutex) - \sum V_t(mutex) \leq 1 $,确保互斥访问[^1] #### 三、同步信号量使用范式 ```c sem_t resource = N; // N个可用资源 void producer() { // 生产资源 V(resource); // 增加可用资源计数 } void consumer() { P(resource); // 等待资源可用 // 消费资源 } ``` 事件关系:$ \text{生产事件} \rightarrow V(S) \rightarrow \text{消费事件可触发} $[^2] #### 四、典型应用对比 | 特性 | 互斥应用 | 同步应用(生产者-消费者) | |--------------|-----------------------------|----------------------------------| | 信号量数量 | 1个 | 2个(empty/full)[^1] | | 关键操作 | P/V操作包裹临界区 | 跨进程协调:P(empty)/V(full)等 | | 阻塞原因 | 资源被占用 | 条件未满足(如缓冲区空/满) | | 死锁风险 | 高(需避免嵌套锁) | 中(需正确设计P/V顺序) | #### 五、本质区别图解 ``` 互斥模型: ┌────────┐ P(mutex) ┌──────────────┐ │ 进程A ├─────────────►│ 共享资源访问 │ └────────┘ └──────┬───────┘ ┌────────┐ ▲ │ │ 进程B ├──────────────┘ V(mutex) └────────┘ 同步模型(生产者-消费者): V(full) ┌───────────┐ ┌───────────┐ │ 生产者 ├────►│ 缓冲区 ├─────┐ └───────────┘ └───────────┘ │ P(full) ▲ │ V(empty) │ ▼ ┌───────────┐ │ 消费者 │ └───────────┘ ``` 前驱关系:$ \text{生产} \xrightarrow{\text{V(full)}} \text{缓冲区非空} \xrightarrow{\text{P(full)}} \text{消费} $[^1] #### 六、编程实践要点 1. **互斥信号量** - 优先使用互斥锁(mutex)而非二进制信号量 - 避免在临界区内调用阻塞操作[^2] 2. **同步信号量** - 优先使用条件变量+互斥锁的组合 - 设计明确的先验关系:$ \text{事件A} \rightarrow \text{V(S)} \rightarrow \text{事件B可触发} $ - 初始值设置需精确匹配资源数:$ S_{\text{initial}} = \text{max\_resources} $[^3] ---
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值