本章介绍有名信号相关函数的使用,在参考书的第10章。本文主要分为两部分,第一部分为有名信号量相关函数的介绍,第二部分为在生产-消费(生产、消费各一个线程)的案例中使用有名信号量进行同步操作;演示的程序源代码都是从G-F所提供免费源码工程中提取出来的,这样每个函数的执行操作就会更加清楚;
第一部分:
1、有名信号量的相关函数
#include
sem_t *sem_open(const char *name, int oflag,.../*mode_t mode, unsigned int value */);
打开或者创建一个有名信号量,oflag的的参数设置同open()函数相类似
#include
int sem_close(sem_t *sem);
关闭信号量,但并不删除信号量。但信号量是随内核持续的,下面的demo演示就可看到进程执行结束,信号量依然随内核保持
#include
int sem_unlink(sem_t *sem);
从文件系统删除有名消耗量的名字
#include
int sem_wait(sem_t *sem); //信号量V操作,减1
int sem_post(sem_t *sem); //信号量P操作,加1
int sem_getvalue(sem_t *sem,int *valp); //返回 信号量sem的当前值
2、源码分析
semcreate源码:
include "pxsem.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);
}
semunlink源码:
#include "pxsem.h"
int
main(int argc, char **argv)
{
if (argc != 2)
err_quit("usage: semunlink ");
Sem_unlink(argv[1]);
exit(0);
}
semgetvalue源码:
#include "pxsem.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 error for /tmp/test: No such file or directory的提示错误,解决的方案是需要在
/dev/shm/目录下创建一个sem.tmp目录;详情可以看https://www.redhat.com/archives/phil-list/2003-January/msg00113.html
semcreate 创建一个有名信号量,随着semcreate进程的结束,可以看到信号量的值依然为1,说明fedora linux系统的有名信号量是随内核保持的。semwait程序把信号量进行减1操作;应用程序每次对信号量进行了关闭操作,但信号量还是存在,实际没有被删除。semunlink程序把信号量从系统进行删除。
sempost源码:
#include "pxsem.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);
}
先挂起的进程先被唤醒执行; fedora Linux由于系统缘故,演示不了信号量为负数的情况;
第二部分:
1、有名信号量在单个生产-单个消费者问题的应用,
/* include main */
#include "pxsem1.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]);
/* 4create three semaphores */
shared.mutex = Sem_open(Px_ipc_name(SEM_MUTEX), O_CREAT | O_EXCL,
FILE_MODE, 1);//信号量mutex初始值为1
shared.nempty = Sem_open(Px_ipc_name(SEM_NEMPTY), O_CREAT | O_EXCL,
FILE_MODE, NBUFF);//信号量mutex初始值为10
shared.nstored = Sem_open(Px_ipc_name(SEM_NSTORED), O_CREAT | O_EXCL,
FILE_MODE, 0);//信号量mutex初始值为0
/* 4create one producer thread and one consumer thread */
Set_concurrency(2);//第7章已经介绍过相关线程函数的作用
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);//缓冲区的空位大于0,那么就把nempty减1然后继续执行(初始值为10) /* wait for at least 1 empty slot */
Sem_wait(shared.mutex);
shared.buff[i % NBUFF] = i; /* store i into circular buffer */
printf("producter buff[%d] = %d\n", (i%NBUFF), shared.buff[i % NBUFF]);
Sem_post(shared.mutex);
Sem_post(shared.nstored);//生产完成,缓冲区已经生产的数据nstored累加1 /* 1 more stored item */
}
return(NULL);
}
void *
consume(void *arg)
{
int i;
//逻辑与生产者线程相反
for (i = 0; i < nitems; i++) {
Sem_wait(shared.mutex);
Sem_wait(shared.nstored);//缓冲区已生产的数据大于零,那么就把nstored减1然后继续执行 /* wait for at least 1 stored item */
if (shared.buff[i % NBUFF] != i)
printf("buff[%d] = %d\n", i, shared.buff[i % NBUFF]);
printf("consumer buff[%d] = %d\n", (i%NBUFF), shared.buff[i % NBUFF]);
Sem_post(shared.mutex);
Sem_post(shared.nempty); //缓冲区取出一个数据,那么就把nempty累加1 /* 1 more empty slot */
}
return(NULL);
}
/* end prodcons */
以下为生产50个数据程序执行的情况,
如果调换消费者线程中的以下两条语句的执行顺序,
Sem_wait(shared.mutex);
Sem_wait(shared.nstored);
在执行程序的过程中发生了死锁:
执行顺序的不恰当,会让程序死锁。