一 Posix信号量:
(1)信号量(semaphore):是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语。
*二值信号量:其值或为0或为1的信号量。可用于互斥,像互斥锁一样,但互斥锁必须总是由锁住它的线程解锁,信号量的释放(post)却不必由执行过它的等待操作的同一个线程执行。
*计数信号量:通常初始化为某个值N,指示可用的资源数(比如缓冲区)。
(2)Posix信号量种类:
*Posix有名信号量:使用Posix IPC名字标示,可用于进程或线程间的同步。例如下图:
*Posix基于内存的信号量(无名信号量):存放在共享内存中,可用于进程或线程间的同步。如下图:进程内线程间、进程间:
*注意:尽管Posix有名信号量由可能与文件系统中的路径对应的名字来标示,但是并不要求它们真正存放在文件系统内的某个文件中。然而,如果信号量的实现用到了映射文件,那么信号量的真正值确实出现在某个文件中,而该文件是映射到所有让该信号量打开着的进程的地址空间的。
*两类信号量使用的函数如下:
(3)信号量与互斥锁和条件变量的差异:
*互斥锁必须总是由锁住它的线程解锁,信号量的释放(post)却不必由执行过它的等待操作的同一个线程执行。
*互斥锁要么被锁住,要么被解开(二值状态,类似于二值信号量)。
*信号量有一个与之关联的状态(它的计数值),那么信号量的释放(post)总是被记住;然而,当一个条件变量发送信号时,如果没有线程等待在该条件变量上,那么该信号将丢失。
*互斥锁是为上锁而优化的,条件变量为等待而优化的,信号量既可用于上锁也可用于等待,因而导致更多的开销和更高的复杂性。
二 Posix有名信号量:
(一)有名信号量既可用于进程内线程间的同步,也可用于进程间的同步。
(二)相关函数:
(1)创建一个新的有名信号量或打开一个已存在的有名信号量:
sem_t *sem_open(const char *name,int oflag,.../*mode_t mode,unsigned int value*/);
返回值:成功则返回执行信号量的指针,出错则为SEM_FAILED(ubuntu下#define SEM_FAILED ((sem_t*)0)).
sem_t的结构:
typedef union
{
char __size[__SIZEOF_SEM_T];
long int __align;
}sem_t;
name:可以是路径名。形式如下:/somename。具体详见man sem_overview(7)。
oflag:可以是O_CREAT或O_CREAT|O_EXCL等。具体详见man sem_open。
mode:指定权限位。
value:指定信号量的初始值,这个初始值不能超过SEM_VALUE_MAX。二值信号量初始值通常为1,计数信号量通常大于1.
(2)关闭和删除有名信号量:
*int sem_close(sem_t *sem):关闭有名信号量。
一个进程终止(无论是自愿终止还是非自愿的终止)时,内核对其上仍然打开着的所有有名信号量自动执行关闭操作;关闭一个信号量并没有将它从系统中删除。即Posix有名信号量至少是随内核持续的。
*int sem_unlink(const char*pathname)
每个信号量都有一个引用计数器记录当前的打开次数:当引用计数大于0时,name就能从文件系统中删除,然后其信号量的析构却要等待最后一个sem_close发生时为止。
(3)sem_wait和sem_trywait函数和sem_timedwait函数:
*int sem_wait(sem_t *sem):
sem_wait函数测试所指定信号量的值:如果该值大于0,那就将它减1并立即返回;如果该值等于0,调用线程就阻塞,直到该值变为大于0,这时再将它减1,函数随后返回。“测试并减1”操作必须是原子的。
sem_trywait:当指定信号量的值是0的时候,不阻塞线程,而是返回EAGIN错误。
*sem_timedwait(sem_t *sem,const struct timespec *abs_timeout):与sem_wait一样,但当sem_timedwait调用不能立即返回时,abs_timeout指定一个时间限制。
struct timespec
{
time_t tv_sec;/*seconds*/
long tv_nsec;/**/
}
(4)sem_post和sem_getvalue函数:
*int sem_post(sem_t *sem):本函数把它指定信号量的值加1,然后唤醒正在等待该信号量变为正数的任意线程。当一个线程使用完某个信号量时,应该调用sem_post。
*int sem_getvalue(sem_t *sem,int *valp):在valp指向的整数中返回指定信号量的当前值。
如果一个或多个进程或线程在等待锁住该信号量,Posix允许返回两个可能结果:0或负数(负数的绝对值表示阻塞在sem_wait的进程或线程的数量),但是linux只允许返回0。
三 Posix基于内存的信号量:
(1)基于内存的信号量由应用程序分配信号量的内存空间,然后由系统初始化它们的值(sem_init)。
(2)相关函数:
*int sem_init(sem_t *sem,int shared,unsigned int value)
sem_init初始化基于内存的posix信号量。
sem参数:指向应用程序必须分配的sem_t变量。
shared:为0:待初始化的信号量是在同一进程的各个线程间共享的;为非0:该信号量必须存放在某种类型的共享内存区中,使使用它的进程都能访问该内存区。
value:该信号量的初始值。
*int sem_destroy(sem_t *sem);
使用完基于内存的信号量后,调用sem_destroy函数摧毁它。
(3)基于内存信号量的持续性:
*基于内存的信号量通常至少具有随进程持续性,这取决于存放信号量的内存区的类型:
~是由单个进程内的多个线程共享时,该信号量具有随进程持续性。
~由不同进程间共享的,那么该信号量必须在共享内存区中,因而只要该内存区存在,该信号量继续存在,即随内核持续性。
四 信号量的限制:
Posix定义了两个信号量限制:
*SEM_NSEMS_MAX:一个进程可打开着的最大信号量数(Posix要求至少256)。
*SEM_VALUE_MAX:一个信号量的最大值(Posix要求至少32767)。
五 使用情况:
*当不需要使用与有名信号量关联的名字时,可以使用基于内存的信号量。
*彼此无亲缘关系的不同进程需使用信号量时,通常使用有名信号量。