第10章 Posix信号灯

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            /* For O_* constants */
#include         /* For mode constants */
#include 

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 

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 ] ");

	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 ");

	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 ");

	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 ");

	sem = Sem_open(argv[1], 0);
	Sem_post(sem);
	Sem_getvalue(sem, &val);
	printf("value = %d\n", val);

	exit(0);
}

10.8 基于内存的信号灯

1.接口

#include 
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);
}

进程间共享信号灯

  • 有名信号灯总是所有进程共享的
  • 在父进程中打开的任何信号灯仍应在子进程中打开

你可能感兴趣的:(网络编程卷2-进程间通信)