10.1 概述
三种信号灯
- posix有名信号灯:使用posix IPC标识,可用于进程或线程之间的同步
- posix基于内存的信号灯:放在共享内存去中,可用于进程或线程之间的同步
- System V信号灯:在内核中维护,可用于进程或线程之间的同步
说明
- system信号灯由内核维护,Posix却不必
- Posix信号灯是由可能与文件系统中的路径对应的名字来标识,但有些不放在文件系统中的某个文件
P/V操作
- P操作:代表荷兰语单词proberen(尝试),也称递减或上锁,posix术语叫等待
- V操作:代表荷兰语单词verhogen(增加),也称递增或解锁或发信号,posix术语叫挂出
互斥锁、条件变量和信号量之间的差别
- 互斥锁必须总是由给他上锁的线程解锁,信号量的挂出却不必由执行过它的等待操作的同一线程执行
- 互斥锁呈二值状态,信号灯却还有计数信号灯
- 既然信号量有一个与之关联的状态,那么信号量的挂出操作总是被记住
- 能够从信号处理程序中安全调用的唯一函数是sem_post()
10.2 sem_open()、sem_close()、sem_unlink()函数
#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);
int sem_close(sem_t *sem);
int sem_unlink(const char *name);
Link with -pthread.
-
oflag:如果指定了O_CREAT,那参数三和四就是必须的,信号量尚未存在就初始化它
- O_CREAT|O_EXCL(独占):信号量已存在会出错
-
mode:指定权限位
-
value:指定信号量的初始值
-
使用sem_open()打开的信号量使用sem_close()关闭,进程结束时会自动执行
-
Posix信号量至少是随内核持续的,每个信号量有一个引用计数器记录当前的打开次数
,引用计数大于0时,name就能从文件系统中删除,拆除却要等到最后一个sem_close()
10.3 sem_wait()和sem_trywait()等函数
#include <semaphore.h>
int sem_wait(sem_t *sem);//被中断返回EINTR
int sem_trywait(sem_t *sem);//失败返回EAGAIN
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
int sem_post(sem_t *sem);
int sem_getvalue(sem_t *sem, int *sval);
- sval:当前信号量的值
- 0:信号量已经上锁
- 负数:绝对值表示等待信号量解锁的线程数
- create程序
#include "unpipc.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)
err_quit("usage: semcreate [ -e ] [ -i initalvalue ] <name>");
sem = Sem_open(argv[optind], flags, FILE_MODE, value);
Sem_close(sem);
exit(0);
}
semgetvalue程序
#include "unpipc.h"
int main(int argc, char **argv)
{
sem_t *sem;
int val;
if (argc != 2)
err_quit("usage: semgetvalue <name>");
sem = Sem_open(argv[1], 0);
Sem_getvalue(sem, &val);
printf("value = %d\n", val);
exit(0);
}
- sem_open()打开一个已经存在的信号灯时第二个参数为0
semwait程序
#include "unpipc.h"
int main(int argc, char **argv)
{
sem_t *sem;
int val;
if (argc != 2)
err_quit("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);
pause(); /* blocks until killed */
exit(0);
}
sempost程序
#include "unpipc.h"
int main(int argc, char **argv)
{
sem_t *sem;
int val;
if (argc != 2)
err_quit("usage: sempost <name>");
sem = Sem_open(argv[1], 0);
Sem_post(sem);
Sem_getvalue(sem, &val);
printf("value = %d\n", val);
exit(0);
}
10.8 基于内存的信号灯
1.接口
#include <semephore.h>
int sem_init(sem_t *sem,int shared,unsigned value);
int sem_destroy(sem_t *sem); //出错返回-1
- value
- 0:同一进程的各个线程间共享
- 1:进程间共享(信号灯必须放在g共享内存中)
- 注意:
- sem_init总是会初始化信号灯的值
- 对已经初始化的信号灯调用sem_init结果是未定义的
- 线程间共享信号灯的一个例子
#include "unpipc.h"
#define NBUFF 10
int nitems; /* read-only by producer and consumer */
struct { /* data shared by producer and consumer */
int buff[NBUFF];
sem_t mutex, nempty, nstored; /* semaphores, not pointers */
} shared;
void *produce(void *), *consume(void *);
int main(int argc, char **argv)
{
pthread_t tid_produce, tid_consume;
if (argc != 2)
err_quit("usage: prodcons2 <#items>");
nitems = atoi(argv[1]);
/* 4initialize three semaphores */
Sem_init(&shared.mutex, 0, 1);
Sem_init(&shared.nempty, 0, NBUFF);
Sem_init(&shared.nstored, 0, 0);
Set_concurrency(2);
Pthread_create(&tid_produce, NULL, produce, NULL);
Pthread_create(&tid_consume, NULL, consume, NULL);
Pthread_join(tid_produce, NULL);
Pthread_join(tid_consume, NULL);
Sem_destroy(&shared.mutex);
Sem_destroy(&shared.nempty);
Sem_destroy(&shared.nstored);
exit(0);
}
void *produce(void *arg)
{
int i;
for (i = 0; i < nitems; i++) {
Sem_wait(&shared.nempty); /* wait for at least 1 empty slot */
Sem_wait(&shared.mutex);
shared.buff[i % NBUFF] = i; /* store i into circular buffer */
Sem_post(&shared.mutex);
Sem_post(&shared.nstored); /* 1 more stored item */
}
return(NULL);
}
void *consume(void *arg)
{
int i;
for (i = 0; i < nitems; i++) {
Sem_wait(&shared.nstored); /* wait for at least 1 stored item */
Sem_wait(&shared.mutex);
if (shared.buff[i % NBUFF] != i)
printf("buff[%d] = %d\n", i, shared.buff[i % NBUFF]);
Sem_post(&shared.mutex);
Sem_post(&shared.nempty); /* 1 more empty slot */
}
return(NULL);
}
进程间共享信号灯
- 有名信号灯总是所有进程共享的
- 在父进程中打开的任何信号灯仍应在子进程中打开