信号量机制本质是对于资源的预订操作,线程或者进程预订了之后,确保未来有一段时间,资源是属于我的。
对于预订资源,会有一个最小单位,资源都是以这个最小单位为整体被使用的。
信号量需要做到:
这里,由于是信号量的前导,我们简单的把信号量理解为一个计数器(是由OS维护的)。
我们这里对于这个信号量的计数器的设计,提出几个问题?
1.计数器能不能简单的设计成一个整型变量?
不行,因为整型变量在经过进程创建之后,任意一个进程对他进行改变的时候,会发生写时拷贝,导致两个进程看到的不是同一个计数器,这样信号量的第一个目的,限制进入的进程数也就失效了。
2.count++和count--不是原子的。
3.申请sem和释放sem来保护临界资源,是规则。这个规则的由来?
这个规则就是,程序员之间规定的规则,再使用多进程访问临界资源的时候,需要代码这样来保护临界资源。
4.所有的进程要访问临界资源,都需要先申请信号量,那么所有进程都需要看到同一个信号量,说明了信号量本身就是一个临界资源。那么我们需要利用临界资源去保护另一个临界资源,为了防止临界资源保护的嵌套,我们就需要保证信号量这个临界资源是安全的。
所以,信号量的申请(++)和信号量的释放(--)这两个操作都是原子的。
5.如果,信号量的初始值是1?
那么,这个信号量不就是一个二元信号量(不就是一把锁吗)
6.我们前面提到了信号量也需要合理的分配资源,那么由谁来做呢?
这里,也是由程序员,在代码部分来完成这项目标。
7.pv操作
我们把原子性的申请信号量称为p操作,原子性的释放信号量称为v操作。
sem_init是Posix信号量操作中的一个函数,用于初始化一个定位在sem的匿名信号量。以下是对sem_init函数的详细解析:
一、函数原型
二、参数说明
三、返回值
四、注意事项
sem_wait
是 POSIX 信号量(semaphore)API 中的一个函数,用于对信号量进行 (wait/down)操作。这个函数会阻塞调用它的线程,直到信号量的值大于零,然后它会将信号量的值减一并继续执行。如果信号量的值已经是零,则调用 sem_wait
的线程会被阻塞,直到信号量的值被其他线程通过 sem_post
增加。
一、函数原型
二、参数
sem
:指向 sem_t
类型的信号量对象的指针。这个信号量对象应该是之前通过 sem_init
或 sem_open
初始化的。三、返回值
sem_wait
返回 0。sem_wait
返回 -1,并设置 errno
来指示错误的原因。可能的错误包括 EINVAL
(无效的参数,即 sem
不是有效的信号量),EINTR
(操作被信号中断),EDEADLK
(死锁条件,如果信号量是通过 sem_init
初始化为线程间共享且调用线程已经拥有该信号量),以及 ENOSYS
(如果系统不支持这个操作,尽管在现代 POSIX 系统中这不太可能)。四、使用场景
sem_wait
通常用于保护临界区(critical section),确保同一时间只有一个线程(或进程,取决于信号量的共享属性)可以进入和执行临界区内的代码。这有助于避免多线程程序中的竞态条件(race conditions)和数据不一致问题。
五、注意事项
sem_wait
之前,应确保信号量已经被正确初始化。sem_init
的 pshared
参数非零),则所有操作该信号量的线程都应该在同一个进程内,或者信号量应该位于共享内存区域中,以便在不同进程间共享。sem_destroy
来销毁它(对于通过 sem_init
初始化的信号量)或 sem_close
和 sem_unlink
(对于通过 sem_open
创建的命名信号量)。sem_post
是 POSIX 信号量(semaphore)API 中的一个函数,用于对信号量进行 V(signal/up)操作。这个函数会增加信号量的值,并可能唤醒一个或多个因为调用 sem_wait
而被阻塞的线程。
一、函数原型
二、参数
sem
:指向 sem_t
类型的信号量对象的指针。这个信号量对象应该是之前通过 sem_init
(对于匿名信号量)或 sem_open
(对于命名信号量)初始化的。三、返回值
sem_post
返回 0。sem_post
返回 -1,并设置 errno
来指示错误的原因。可能的错误包括 EINVAL
(无效的参数,即 sem
不是有效的信号量)。四、使用场景
sem_post
通常与 sem_wait
一起使用,以实现线程间的同步。当一个线程完成了对临界区的访问后,它会调用 sem_post
来增加信号量的值,从而可能允许其他被阻塞的线程进入临界区。
五、注意事项
sem_post
之前,应确保信号量已经被正确初始化。sem_init
的 pshared
参数非零),则所有操作该信号量的线程都应该在同一个进程内,或者信号量应该位于共享内存区域中,以便在不同进程间共享。然而,需要注意的是,POSIX 信号量 API 并不直接支持跨进程的匿名信号量;跨进程共享通常是通过命名信号量(使用 sem_open
)来实现的。sem_post
的操作是原子的,即它不会被其他线程的 sem_wait
或 sem_trywait
调用中断。sem_destroy
是 POSIX 信号量(semaphore)API 中的一个函数,用于销毁一个由 sem_init
初始化的匿名信号量。这个函数会释放与信号量相关联的任何资源,并且只有在没有线程等待该信号量时才能成功调用。
一、函数原型
二、参数
sem
:指向 sem_t
类型的信号量对象的指针。这个信号量对象应该是之前通过 sem_init
初始化的。三、返回值
sem_destroy
返回 0。sem_destroy
返回 -1,并设置 errno
来指示错误的原因。可能的错误包括 EBUSY
(信号量当前正在被使用,即有线程正在等待它),以及 EINVAL
(无效的参数,即 sem
不是有效的信号量)。四、使用场景
sem_destroy
应该在信号量不再需要时被调用,以释放与其相关联的资源。这通常发生在程序结束或信号量完成其同步任务之后。
五、注意事项
sem_destroy
之前,应确保没有线程在等待该信号量。如果有线程在等待,sem_destroy
将返回 -1 并设置 errno
为 EBUSY
。sem_destroy
只能用于由 sem_init
初始化的匿名信号量。对于通过 sem_open
创建的命名信号量,应使用 sem_close
和 sem_unlink
来关闭和删除它们。sem_wait
、sem_post
等),否则会导致未定义的行为。