1、信号量概述
信号量(semaphore)是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语。
i、Posix有名信号量:使用Posix IPC名字标识,可用于进程或线程间的同步;
ii、Posix基于内存的信号量:存放在共享内存区中,可用于进程或线程间的同步;
iii、System V信号量:在内核中维护,可用于进程或线程间的同步。
Posix信号量不必在内核中维护。其Posix有名信号量如下图所示:
其Posix基于内存的信号量如下图所示:
尽管Posix有名信号量是由可能与文件系统中的路径对应的名字标识的,但是并不要求它们真正存放在文件系统的某个文件中。
一个进程可以在某个信号量上执行的三种操作:
i、创建一个信号量。可要求调用者指定初始值,对于二值信号量来说,通常是1,也可心介0。
ii、等待一个信号量。该操作执行两个动作,等待信号量的值变为大于0,然后将它减1。
iii、挂起一个信号量。该操作将信号量的值加1,从而唤醒正在等待该信号量变为大于0的任意线程。
二值信号量可用于互斥目的,就像互斥锁一样,但两者还是有根本上的区别:互斥锁必须总是由锁住它的线程解锁,而信号量的挂出却不必由执行过它的等待操作的同一线程执行。
以下列出信号量、互斥锁、条件变量之间的三个差异:
i、互斥锁总是由给它上锁的线程解锁,信号量的挂出却不必由执行过它的等待操作的同一线程执行。
ii、互斥锁要么被锁住,要么被解开(二值状态,类似于二值信号量)。
iii、既然信号量有一个与之关联的状态(计数值 ),那么信号量挂出操作总是被记住。然而当向一个条件变量发送信号时,如果没有线程等待在该条件变量上,那么该信号交丢失。
应该知道的是:标准提供信号量的主要目的是提供一种进程间同步方式,而互斥锁和条件变量是作为线程间的同步机制说明的。此句不应该是绝对的,因为在之前的章节就也看见过用互斥锁和条件变量运用于进程间同步。
以下提供一张有关有名信号量和基于内存的信号量,及它们所用到的各自、共同函数。
2、有名信号量及其相关函数
sem_open、sem_close、sem_unlink、sem_wait、sem_trywait、sem_post和sem_getvalue函数
sem_open函数,创建一个新的有名信号量或打开一个已存在的有名信号量。
#include<semaphore.h>
sem_t* sem_open(const char* name, int oflag, ...
/* mode_t mode,unsigned int value */);
//返回:若成功则为指向信号量的指针,若出错则为SEM_FAILED
oflag参数可以是0、O_CREAT或O_CREAT | O_EXCL。如果指定了O_CREAT标志那么第三个或第四个参数是需要的:其中mode参数指定权限位,value参数指定信号量的初始值。
如果指定了O_CREAT(而没有指定O_EXCL),那么只有当所需的信号量尚未存在时才初始化它,不过所需信号量已存在条件下指定O_CREAT并不是一个错误。该标志的意思仅仅是“如果所需信号量尚未存在,那就创建并初始化它”。但是所需信号量已存在条件下指定O_CREAT | O_EXCL却是一个错误。
sem_close函数,使用sem_open打开的信号量,使用sem_close将其关闭。
#include<semaphore.h>
int sem_close(sem_t* sem);
//返回:若成功则为0,若出错则为-1
需要值得注意的是,由于Posix有名信号量至少是随内核持续的:即使当前没有进程打开着某个信号量,它的值仍然保持。
sem_unlink函数,将有名信号量从系统中删除。
#include<semaphore.h>
int sem_unlink(const char* name);
//返回:若成功则为0,若出错则为-1
sem_wait函数,测试所指定信号量的值,如果该值大于0,那就将它减1并立即返回。如果该值等于0,调用线程就被投入睡眠中,直到该值变为大于0,这时再将它减1,函数随后返回。
sem_trywait函数,是当指定信号量的值已经是0时,后者并不将调用线程投入睡眠。相反,它返回一个EAGAIN错误。
#include<semaphore.h>
int sem_wait(sem_t* sem);
int sem_trywait(sem_t* sem);
//均返回:若成功则为0,若出错则为-1
sem_post函数,把指定信号量的值加1,然后唤醒正在等待该信号量值变为正数的任意线程。
sem_getvalue函数,在由valp指向的整数中返回所指定信号量的当前值。
#include<semaphore.h>
int sem_post(sem_t* sem);
int sem_getvalue(sem_t* sem, int* valp);
//均返回:若成功则为0,若出错则为-1
3、生产者-消费者问题(一个生产者和一个消费者)
类比于7.3节讲述的生产者-消费者问题。现对生产者-消费者问题进行扩展,把共享缓冲区用作一个环绕缓冲区:生产者填写最后一项(buff[NBUFF-1])后,回过头来填写第一项(buff[0]),消费者也同样这么做。于是又增加一个同步问题:即生产者不能走到消费者的前面。
当共享缓冲区作为一个环绕缓冲区考虑时,必须由代码来维持以下三个条件:
i、当缓冲区为空时,消费者不能试图从其中去除一个条目;
ii、当缓冲区填满时,生产者不能试图往其中放置一个条目;
iii、共享变量可能描述缓冲区的当前状态(下标、计数和链表指针等),因此生产者和消费者的所有缓冲区操纵都必须保护起来,以避免竞争状态。
使用三个信号量方案展示三种不同类型的信号量:
i、互斥锁信号量:名为mutex的二值信号量保护两个临界区:一个是往共享缓冲区中插入一个数据条目(由生产者执行),另一个是从共享缓冲区中移走一个数据条目(由消费者执行)。
ii、空槽数信号量:名为nempty的计数信号量统计共享缓冲区中的空槽位数。该信号量初始化为缓冲区中的槽位数(NBUFF)。
iii、已填写槽数信号量:名为nstored的计数信号量统计共享缓冲区中已填写的槽位数。该信号量初始化为0,因为缓冲区一开始是空的。
/* include main */
#include "unpipc.h"
#define NBUFF 10
#define SEM_MUTEX "mutex" /* these are args to px_ipc_name() */
#define SEM_NEMPTY "nempty"
#define SEM_NSTORED "nstored"
int nitems; /* read-only by producer and consumer */
struct { /* data shared by producer and consumer */
int buff[NBUFF];
sem_t *mutex, *nempty, *nstored;
} shared;
void *produce(void *), *consume(void *);
int
main(int argc, char **argv)
{
pthread_t tid_produce, tid_consume;
if (argc != 2)
err_quit("usage: prodcons1 <#items>");
nitems = atoi(argv[1]);//条目数(生产者最多能生产的),nitems可以大于NBUFF
/* 4create three semaphores */
shared.mutex = Sem_open(Px_ipc_name(SEM_MUTEX), O_CREAT | O_EXCL,
FILE_MODE, 1);//很明显初始值主1 ,为二值信号量
shared.nempty = Sem_open(Px_ipc_name(SEM_NEMPTY), O_CREAT | O_EXCL,
FILE_MODE, NBUFF);//初始值为NBUFF信号量
shared.nstored = Sem_open(Px_ipc_name(SEM_NSTORED), O_CREAT | O_EXCL,
FILE_MODE, 0);//初始值为0的信号量
/* 4create one producer thread and one consumer thread */
Set_concurrency(2);
Pthread_create(&tid_produce, NULL, produce, NULL);
Pthread_create(&tid_consume, NULL, consume, NULL);
/* 4wait for the two threads */
Pthread_join(tid_produce, NULL);
Pthread_join(tid_consume, NULL);
/* 4remove the semaphores */
Sem_unlink(Px_ipc_name(SEM_MUTEX));
Sem_unlink(Px_ipc_name(SEM_NEMPTY));
Sem_unlink(Px_ipc_name(SEM_NSTORED));
exit(0);
}
/* end main */
/* include prodcons */
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);//等待信号量值大于0,然后将值-1,被唤醒,执行临界保护区代码
shared.buff[i % NBUFF] = i; /* store i into circular buffer */
Sem_post(shared.mutex);//将信号量值+1,唤醒等待该信号量的线程
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);
}
/* end prodcons */
注意死锁的避免
此处如果将上述代码中消费者的两个sem_wait调换位置,将产生死锁。因为生产者在等待mutex信号量,但是消费者却持有该信号量并在待nstored信号量。然而生产者只有获取了mutex信号量才能挂出nstored信号量。于是死锁情况产生。死锁可以避免,见之前博文。
4、基于内存的信号量及其相关函数
sem_init、sem_destroy函数
#include<semaphore.h>
int sem_init(sem_t* sem, int shared, unsigned int value);
//返回:若出错则为-1
int sem_destroy(sem_t* sem);
//返回:若成功则为0,若出错则为-1
针对上述函数的参数进行说明:
基于内存的信号量是由sem_init初始化的。sem参数指向应用程序必须分配的sem_t变量。
如果shared为0,那么待初始化的信号量是在同一进程的各个线程间共享的,否则该信号量是在进程间共享的。
当shared为非零时,该信号量必须放某种类型的共享内存区中,而即将使用它的所有进程都要能访问该共享内存区。
value参数值即是该信号量的初始值。
切记:对一个已初始化过的信号量调用sem_init,其结果是未定义的。
基于内存的信号量至少具有随进程的持续性,然而它们真正的持续性却取决于存放信号量的内存区的类型:
i、基于内存的信号量是由单个进程内的各个线程共享的,则该信号量具有随进程的持续性,当该进程终止时它也消失。(很好理解哈)
ii、基于内存的信号量是在不同进程间共享的,那么信号量必须存放在共享内存区中,因而只要共享内存区仍然存在,该信号量也就继续存在。
将上述代码(利用有名信号量处理单个生产者单个消费者问题)改为基于内存的信号量实现的代码:
#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);
}
5、多个生产者,单个消费者
/* include main */
#include "unpipc.h"
#define NBUFF 10 //缓冲区容量
#define MAXNTHREADS 100 //总线程最大数量
int nitems, nproducers; /* read-only by producer and consumer */
struct { /* data shared by producers and consumer */
int buff[NBUFF];
int nput;
int nputval;
sem_t mutex, nempty, nstored; /* semaphores, not pointers */
} shared;
void *produce(void *), *consume(void *);
int
main(int argc, char **argv)
{
int i, count[MAXNTHREADS];
pthread_t tid_produce[MAXNTHREADS], tid_consume;
if (argc != 3)
err_quit("usage: prodcons3 <#items> <#producers>");
nitems = atoi(argv[1]);
nproducers = min(atoi(argv[2]), MAXNTHREADS);
/* 4initialize three semaphores */
Sem_init(&shared.mutex, 0, 1); //以下三行,均是是基于内存的信号量初始化
Sem_init(&shared.nempty, 0, NBUFF);
Sem_init(&shared.nstored, 0, 0);
/* 4create all producers and one consumer */
Set_concurrency(nproducers + 1);
for (i = 0; i < nproducers; i++) { //产生多个生产者线程
count[i] = 0;
Pthread_create(&tid_produce[i], NULL, produce, &count[i]);
}
Pthread_create(&tid_consume, NULL, consume, NULL); //一个消费者线程
/* 4wait for all producers and the consumer */
for (i = 0; i < nproducers; i++) { //等待清除生产者终止线程
Pthread_join(tid_produce[i], NULL);
printf("count[%d] = %d\n", i, count[i]);
}
Pthread_join(tid_consume, NULL); //等待清除消费者线程
Sem_destroy(&shared.mutex);
Sem_destroy(&shared.nempty);
Sem_destroy(&shared.nstored);
exit(0);
}
/* end main */
/* include produce */
void *
produce(void *arg)
{
for ( ; ; ) {
Sem_wait(&shared.nempty); /* wait for at least 1 empty slot */
Sem_wait(&shared.mutex);
if (shared.nput >= nitems) { //条件为真时,表明生产的产品足够多了,可以跳出生产者线程
Sem_post(&shared.nempty);
Sem_post(&shared.mutex);
return(NULL); /* all done */
}
shared.buff[shared.nput % NBUFF] = shared.nputval;
shared.nput++;
shared.nputval++;
Sem_post(&shared.mutex);
Sem_post(&shared.nstored); /* 1 more stored item */
*((int *) arg) += 1;//统计每个生产者线程所生产的条目 (int*)是强制类型转换
}
}
/* end produce */
/* include consume */
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("error: buff[%d] = %d\n", i, shared.buff[i % NBUFF]);
Sem_post(&shared.mutex);
Sem_post(&shared.nempty); /* 1 more empty slot *///腾出一个存放生产者条目的缓冲区
}
return(NULL);
}
/* end consume */
6、多个生产者,多个消费者
书上列出两个需要使用多个消费者的例子,此处只举一个。
一个读出UDP数据报,对它们进行操作后把结果写入某个数据库的程序。每个数据报由一个消费者线程处理,为重叠可能很花时间的每个数据报的处理,需要有多个消费者线程。尽管由消费者线程们写入数据库中的数据报顺序通常不同于原来的数据报顺序,数据库中的记录排序功能却能处理顺序问题。
/* include globals */
#include "unpipc.h"
#define NBUFF 10
#define MAXNTHREADS 100
int nitems, nproducers, nconsumers; /* read-only */
struct { /* data shared by producers and consumers */
int buff[NBUFF];
int nput; /* item number: 0, 1, 2, ... */
int nputval; /* value to store in buff[] */
int nget; /* item number: 0, 1, 2, ... */
int ngetval; /* value fetched from buff[] */
sem_t mutex, nempty, nstored; /* semaphores, not pointers */
} shared;
void *produce(void *), *consume(void *);
/* end globals */
/* include main */
int
main(int argc, char **argv)
{
int i, prodcount[MAXNTHREADS], conscount[MAXNTHREADS];//分别对每个调用到的线程进行数数
pthread_t tid_produce[MAXNTHREADS], tid_consume[MAXNTHREADS];
if (argc != 4)
err_quit("usage: prodcons4 <#items> <#producers> <#consumers>");
nitems = atoi(argv[1]);
nproducers = min(atoi(argv[2]), MAXNTHREADS);
nconsumers = min(atoi(argv[3]), MAXNTHREADS);
/* 4initialize three semaphores */
Sem_init(&shared.mutex, 0, 1);
Sem_init(&shared.nempty, 0, NBUFF);
Sem_init(&shared.nstored, 0, 0);
/* 4create all producers and all consumers */
Set_concurrency(nproducers + nconsumers);
for (i = 0; i < nproducers; i++) {
prodcount[i] = 0;
Pthread_create(&tid_produce[i], NULL, produce, &prodcount[i]);
}
for (i = 0; i < nconsumers; i++) {
conscount[i] = 0;
Pthread_create(&tid_consume[i], NULL, consume, &conscount[i]);
}
/* 4wait for all producers and all consumers */
for (i = 0; i < nproducers; i++) { //等待生产者线程终止
Pthread_join(tid_produce[i], NULL);
printf("producer count[%d] = %d\n", i, prodcount[i]);
}
for (i = 0; i < nconsumers; i++) { //等待消费者线程终止
Pthread_join(tid_consume[i], NULL);
printf("consumer count[%d] = %d\n", i, conscount[i]);
}
Sem_destroy(&shared.mutex);
Sem_destroy(&shared.nempty);
Sem_destroy(&shared.nstored);
exit(0);
}
/* end main */
/* include produce */
void *
produce(void *arg)
{
for ( ; ; ) {
Sem_wait(&shared.nempty); /* wait for at least 1 empty slot */
Sem_wait(&shared.mutex);
if (shared.nput >= nitems) {
//注意理解这一行代码的目的所在,是为了当缓冲区所有条目都被消费掉后,让消费者线程解除阻塞调用
Sem_post(&shared.nstored); /* let consumers terminate */
Sem_post(&shared.nempty);
Sem_post(&shared.mutex);
return(NULL); /* all done */
}
shared.buff[shared.nput % NBUFF] = shared.nputval;
shared.nput++;
shared.nputval++;
Sem_post(&shared.mutex);
Sem_post(&shared.nstored); /* 1 more stored item */
*((int *) arg) += 1;
}
}
/* end produce */
/* include consume */
void *
consume(void *arg)
{
int i;
for ( ; ; ) {//此处for循环与单个消费者的for循环有所区别
Sem_wait(&shared.nstored); /* wait for at least 1 stored item */
Sem_wait(&shared.mutex);
if (shared.nget >= nitems) {
Sem_post(&shared.nstored);
Sem_post(&shared.mutex);
return(NULL); /* all done */
}
i = shared.nget % NBUFF;
if (shared.buff[i] != shared.ngetval)//如果不相等成立,说明生产者生产条目出现错误
printf("error: buff[%d] = %d\n", i, shared.buff[i]);
shared.nget++;
shared.ngetval++;
Sem_post(&shared.mutex);
Sem_post(&shared.nempty); /* 1 more empty slot */
*((int *) arg) += 1;
}
}
/* end consume */
7、利用FIFO实现信号量的举例
使用FIFO完成Posix有名信号量的实现。每个有名信号量作为一个使用同一名字的FIFO来实现,该FIFO中的非负字节数代表信号量的当前的值。sem_post函数往该FIFO中写入1个字节,sem_wait函数则从该FIFO中读出一个字节(如果该FIFO为空,则阻塞调用线程)。sem_open函数在指定了O_CREAT标志的前提下创建待打开的FIFO,然后(不论是否指定了O_CREAT)打开该FIFO两次(一次用于只读,一次用于只写),如果该FIFO是新创建的,那就往其中写入作为信号量初始值的那个数目的字节。
以下是#include<semaphore.h>的内容
//以下是#include<semaphore.h>的内容
/* include semaphoreh */
/* 4the fundamental datatype */
typedef struct {
int sem_fd[2]; /* two fds: [0] for reading, [1] for writing */
int sem_magic; /* magic number if open */
} mysem_t;
#define SEM_MAGIC 0x89674523
#ifdef SEM_FAILED
#undef SEM_FAILED
#define SEM_FAILED ((mysem_t *)(-1)) /* avoid compiler warnings */
#endif
/* end semaphoreh */
/* 4our functions */
int mysem_close(mysem_t *);
mysem_t *mysem_open(const char *, int, ...);
int mysem_post(mysem_t *);
int mysem_unlink(const char *);
int mysem_wait(mysem_t *);
以下是mysem_open的内容:
#include<semaphore.h>
#include<unpipc.h>
#include <stdarg.h> /* for variable arg lists */
mysem_t *
mysem_open(const char *pathname, int oflag, ...)
{
int i, flags, save_errno;
char c;
mode_t mode;
va_list ap; //va_list表示可变参数列表类型,实际就是char指针
mysem_t *sem;
unsigned int value;
if (oflag & O_CREAT) {//判断是否是存在O_CREAT
va_start(ap, oflag); /* init ap to final named argument */
mode = va_arg(ap, va_mode_t);//根据类型(类型长度),获取第三个参数
value = va_arg(ap, unsigned int);//获取第四个参数
va_end(ap);
if (mkfifo(pathname, mode) < 0) { //mkfifo与open是配套使用哈!
if (errno == EEXIST && (oflag & O_EXCL) == 0)
oflag &= ~O_CREAT; /* already exists, OK */
else
return(SEM_FAILED);
}
}
/* *INDENT-OFF* */
if ((sem = malloc(sizeof(mysem_t))) == NULL) //分配内存
return(SEM_FAILED);
sem->sem_fd[0] = sem->sem_fd[1] = -1;//先赋初值,是为了防止当close,可能关闭该进程其它正在使用的某个描述符
if ((sem->sem_fd[0] = open(pathname, O_RDONLY | O_NONBLOCK)) < 0)//只读权限,打开该FIFO
goto error;
if ((sem->sem_fd[1] = open(pathname, O_WRONLY | O_NONBLOCK)) < 0)//只写FIFO
goto error;
/* *INDENT-ON* */
/* 4turn off nonblocking for sem_fd[0] */
if ((flags = fcntl(sem->sem_fd[0], F_GETFL, 0)) < 0)//获取F_GETFL属性
goto error;
flags &= ~O_NONBLOCK;//关闭非阻塞
if (fcntl(sem->sem_fd[0], F_SETFL, flags) < 0)//设置F_SETFL属性
goto error;
if (oflag & O_CREAT) { /* initialize semaphore */
for (i = 0; i < value; i++)
if (write(sem->sem_fd[1], &c, 1) != 1)//向FIFO中逐个写入总共value个字节
goto error;
}
sem->sem_magic = SEM_MAGIC;
return(sem);
error:
save_errno = errno;//存在的意义:当下面代码close关闭出错时,有可能修改errno(小概率情况),此处这种写法,避免了此问题
if (oflag & O_CREAT)
unlink(pathname); /* if we created FIFO */
close(sem->sem_fd[0]); /* ignore error */
close(sem->sem_fd[1]); /* ignore error */
free(sem);
errno = save_errno;
return(SEM_FAILED);
}
/* end sem_open */
以下是mysem_close代码:
#include "unpipc.h"
#include "semaphore.h"
int
mysem_close(mysem_t *sem)
{
if (sem->sem_magic != SEM_MAGIC) {
errno = EINVAL;
return(-1);
}
sem->sem_magic = 0; /* in case caller tries to use it later */
//以下是利用close关闭两个FIFO描述符
if (close(sem->sem_fd[0]) == -1 || close(sem->sem_fd[1]) == -1) {
free(sem);
return(-1);
}
free(sem);
return(0);
}
/* end sem_close */
以下是mysem_unlink函数:
/* include sem_unlink */
#include "unpipc.h"
#include "semaphore.h"
int
mysem_unlink(const char *pathname)
{
return(unlink(pathname));
}
/* end sem_unlink */
以下mysem_post函数:
/* include sem_post */
#include "unpipc.h"
#include "semaphore.h"
int
mysem_post(mysem_t *sem)
{
char c;
if (sem->sem_magic != SEM_MAGIC) {
errno = EINVAL;
return(-1);
}
if (write(sem->sem_fd[1], &c, 1) == 1)//写一个字节,在往FIFO写入之间,须有对其进行读取操作
return(0);
return(-1);
}
/* end sem_post */
以下是mysem_wait函数:
/* include sem_wait */
#include "unpipc.h"
#include "semaphore.h"
int
mysem_wait(mysem_t *sem)
{
char c;
if (sem->sem_magic != SEM_MAGIC) {
errno = EINVAL;
return(-1);
}
if (read(sem->sem_fd[0], &c, 1) == 1)//如果FIFO为空,则调用进程阻塞在此处
return(0);
return(-1);
}
/* end sem_wait */
以上知识点来均来自steven先生所著UNP卷二(version2),刚开始学习网络编程,如有不正确之处请大家多多指正。