信号量(semaphore),也和互斥锁一样提供了线程间或者进程间的同步功能。
信号量有三种:
- Posix有名字的信号量
- Posix基于内存的信号量
- System V信号量
信号量比互斥锁高级,互斥锁只允许一个线程访问临界区,信号量可以多个,可以把信号量看作成互斥锁的升级版,但是如果能用互斥锁解决,就用互斥锁,互斥锁比信号量节省资源。
这篇文章只介绍Posix有名字的信号量
1,创建有名字的信号量,创建成功后,会在ubuntu的/dev/shm目录下,生成一个文件,名字为【sem.name】。【sem】是固定的,【name】是函数sem_open的第一个参数。
名字的信号量的生命周期和内核一样,只要系统不重启,它就一直存在。
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,
mode_t mode, unsigned int value);
- name:任意名字,当不能包含【/】
- oflag:和open函数一样,O_RDWR,O_CREAT,O_EXCL等
- mode:和open函数一样,比如0664
- value:可以同时访问临界区的线程或者进程的数量。如果设置为1,功能就和互斥锁一样了。
- 返回值:成功0;失败:SEM_FAILED(这个宏的实际值是-1)。
2,删除有名字的信号量,并删除文件。
#include <semaphore.h>
int sem_unlink(const char *name);
返回值:成功0
失败:-1,设置errno
EACCES:没有权限访问这个信号量对应的文件 ENAMETOOLONG:信号量的名字长了 ENOENT:信号量不存在
3,取得信号量的value值
#include <semaphore.h>
int sem_getvalue(sem_t *sem, int *sval);
- sem:信号量指针
- sval:返回的信号量的value值
- 返回值:成功0;失败:-1,设置errno。(EINVAL :不是一个有效的信号量)。
4,如果信号量的value值大于0,把信号量的value值-1;如果信号量的value值小于1,阻塞等待,直到信号量的value值大于0。注意:在ubuntu下,如果sem_wait执行前,value值为0,sem_wait执行后,value也不会变成-1,再次执行sem_wait,value还是0。但是有的unix系统value会变成负数。即使value不变成负数,内核也会准确记录它的值。
#include <semaphore.h>
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
sem:信号量指针
返回值:成功0;失败:
- EINTR :阻塞的过程种被信号终止了。
- EINVAL:不是一个有效的信号量
sem_trywait():不阻塞等待。
- EAGAIN: 信号量的value值为0。
sem_timedwait():
- EINVAL: Tabs_timeout.tv_nsecs 小于0,或者大于等于1000毫秒。
- ETIMEDOUT: 超时了。
5,把信号量的value值+1。
#include <semaphore.h>
int sem_post(sem_t *sem);
- 返回值:成功0;失败:
- EINVAL:不是一个有效的信号量
- EOVERFLOW:超过了信号量的value。
用下面5个程序观察有名字信号量的特性。
semcreate.c
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char** argv){
int c, flags;
unsigned int val = 1;
sem_t* sem;
flags = O_RDWR | O_CREAT;
while((c = getopt(argc, argv, "ei:")) != -1){
switch(c){
case 'e':
flags |= O_EXCL;
break;
case 'i':
val = atoi(optarg);
break;
}
}
if(optind != argc - 1){
printf("usage error\n");
return -1;
}
sem = sem_open(argv[optind], flags, 0664, val);
if(sem == SEM_FAILED){
perror("sem");
return -1;
}
sem_close(sem);
exit(0);
}
semunlink.c
#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char** argv){
if(argc != 2){
printf("usage err\n");
exit(1);
}
if(sem_unlink(argv[1]) == -1){
perror("sem_unlink");
}
}
semgetvalue.c
#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char** argv){
sem_t* sem;
int val;
if(argc != 2){
printf("usage error\n");
exit(1);
}
sem = sem_open(argv[1], 0);
sem_getvalue(sem, &val);
printf("value = %d\n", val);
exit(0);
}
semwait.c
#include <semaphore.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(int argc, char** argv){