信号量(semaphore)是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语。
Posix信号量分为有名信号量和基于内存的信号量(无名信号量)。
二值信号量可用于互斥目的,就像互斥锁一样
Pthread_mutex_lock(&mutex) sem_wait(&sem);
Pthread_mutex_unlock(&mutex) sem_post(&sem);
不同的是互斥锁必须总是由锁住它的线程解锁,而信号量的挂出不必由执行过它的等待操作的同一线程执行。
信号量和条件变量的不同,信号量的挂出操作总是被记住。但条件变量发送信号时,如果没有线程等待在该条件变量上,那么该信号将丢失。
函数sem_open用于创建一个新的有名信号量或打开一个已经存在的有名信号量。该函数在linux下name只能有一个斜杠。
sem_t* sem_open(const char* name, int oflag, /* mode_t mode, unsigned int value */);
关闭信号量
int sem_close(sem_t* sem);
当进程终止的时候,内核将自动关闭该进程开启的信号量。
信号量关闭后,内核并未将其从系统中删除,信号量是随内核持续的,使用sem_unlink从系统中删除。每个信号量会有一个引用计数器记录当前打开的次数,当打开次数为0时,内核就会将该信号量从系统删除了。
int sem_unlink(const char* name);
当信号量的值大于0的时候,就将信号量值减1,并返回,否则将线程投入睡眠。直到调用了sem_post使信号量值加1。
int sem_wait(sem_t* sem);
该函数在信号量值为0是也不会将调用线程投入睡眠,只是返回一个EAGAIN。
int sem_trywait(sem_t* sem);
将信号量的值加1
int sem_post(sem_t*sem);
获取信号量的值
int sem_getvalue(sem_t* sem);
注:在centos下我发现一个奇怪的现象就是我在sem_open的时候value值为1,但是并未将信号量声明为二值信号量,每次调用sem_post的时候,信号量值都会加1。是否是centos不支持二值信号量。不仅如此,当value设为大于1的数后,再次调用sem_post,信号量的值还是可以增加的。那这个value值还有啥意义?望明白人给个答复。
基于内存的信号量
信号量初始化
当shared为0时,表示信号量实在同一进程的各个线程间共享,当为1时就是在进程间共享,此时的sem应该在共享内存中声明。value值同sem_open。
int sem_init(sem_t* sem, int shared, unsigned int value);
信号量销毁
int sem_destroy(sem_t* sem);
只要含有某个基于内存的信号量的内存区保持有效,信号量就一直存在。
注:对于进程间的信号量,可以使用sem_open,只要保证指定的name相同就行。对于子进程,Posix.1中有关fork函数的描述这么说:“在父进程中打开的任何信号量仍应在子进程中打开。”例:
sem_t* mutex = sem_open(“/test”,O_CREAT,S_IRUSR|S_IWUSR,1);
if((childpid = fork() == 0)
{
sem_wait(mutex);
}
sem_post(mutex);
以下是关于理解信号量的代码,用于参考:
semcreate.cpp
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <semaphore.h> #include <fcntl.h> int main(int argc, char **argv) { int c, flags; sem_t *sem; unsigned int value; flags = O_RDWR | O_CREAT; value = 1; while ((c = getopt(argc, argv, "ei:")) != -1) { switch (c) { case 'e': flags |= O_EXCL; break; case 'i': value = atoi(optarg); break; } } if (optind != argc - 1) printf("usage: semcreate [ -e ] [ -i initalvalue ] <name>"); sem = sem_open(argv[optind], flags, S_IRUSR|S_IWUSR, value); printf("%d\n",sem); sem_close(sem); exit(0); }
semwait.cpp
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <semaphore.h> #include <fcntl.h> int main(int argc, char **argv) { sem_t *sem; int val; if (argc != 2) printf("usage: semwait <name>"); sem = sem_open(argv[1], 0); sem_wait(sem); sem_getvalue(sem, &val); printf("pid %ld has semaphore, value = %d\n", (long) getpid(), val); exit(0); }
sempost.cpp
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <semaphore.h> #include <fcntl.h> int main(int argc, char **argv) { sem_t *sem; int val; if (argc != 2) printf("usage: sempost <name>"); sem = sem_open(argv[1], 0); sem_post(sem); sem_getvalue(sem, &val); printf("value = %d\n", val); exit(0); }
semgetvalue.cpp
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <semaphore.h> #include <fcntl.h> int main(int argc, char **argv) { sem_t *sem; int val; if (argc != 2) printf("usage: semgetvalue <name>"); sem = sem_open(argv[1], 0); sem_getvalue(sem, &val); printf("value = %d\n", val); exit(0); }
semunlink.cpp
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <semaphore.h> #include <fcntl.h> int main(int argc, char **argv) { if (argc != 2) printf("usage: semunlink <name>"); sem_unlink(argv[1]); exit(0); }
以上代码参照unix网络编程 卷2 posix信号量