POSIX信号量之生产者消费者问题

时间:2022-08-06 15:13:34

信号量

       信号量主要提供对进程间共享资源访问控制机制,相当于内存中标志,进程可以根据它断定是否能够访问某些共享资源,同时,进程也可以修改标志。除了用于访问控制外,还可用于进程同步。信号量同步的原理就是操作系统中的PV原语。一次P操作使信号量减1,而一次V操作使信号量加1.当sem>=0时,该进程(线程)具有公共资源的访问权限;相反,当sem<0时,该进程(线程)就会阻塞直到sem>=0为止。

       以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。  在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用

         抽象的来讲,信号量的特性如下:信号量是一个非负整数(车位数),所有通过它的线程/进程(车辆)都会将该整数减一(通过它当然是为了使用资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态。在信号量上我们定义两种操作: Wait(等待) 和 Release(释放)。当一个线程调用Wait操作(P操作)时,它要么得到资源然后将信号量减一,要么一直等下去(指放入阻塞队列),直到信号量大于等于一时。Release(释放)(V 操作)实际上是在信号量上执行加操作,对应于车辆离开停车场,该操作之所以叫做“释放”是因为释放了由信号量守护的资源。

      若用于互斥,几个进程往往只设置一个信号量;若用于同步操作,往往会设置多个信号量,并安排不同的初始值来实现它们之间的顺序执行。  

      信号量有两组调用函数。一种叫做System V信号量,常用于进程的同步;另一种来源于POSIX,常用于线程同步。这里主要讲POSIX信号量,基本的系统调用有四个:sem_init(),sem_wait(),sem_post(),sem_destory(),头文件为semaphore.h . 

生产者消费者问题

      有一个有限缓冲区和两个线程:生产者和消费者。他们分别把产品放入缓冲区和从缓冲区中拿走产品。当一个生产者在缓冲区满时必须等待,当一个消费者在缓冲区空时页必须等待。程序中有三个信号量,其中avail和full分别用于解决生产者和消费者线程之间的同步问题,mutex用于这两个线程之间的互斥问题。avail初始化为N(有界缓冲区的空单元数),mutex初始化为1,full初始化为0.

程序如下:

/*product.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <fcntl.h>
#define FIFO "./myfifo3"
#define N 5
int lock_var;
time_t end_time;
char buf_r[100];
sem_t mutex,full,avail;
int fd;
void productor(void *arg);
void consumer(void *arg);
int flag;

int main(int argc,char *argv[])
{
pthread_t id1,id2;
int ret;
flag=0;
end_time=time(NULL)+30;
unlink(FIFO);
//创建有名管道
if(mkfifo(FIFO,O_CREAT|O_EXCL)<0)
printf("cannot creat myfifo\n");
printf("Preapring for reading bytes...\n");
memset(buf_r,0,sizeof(buf_r));
//打开管道,无阻赛
fd=open(FIFO,O_RDWR|O_NONBLOCK,0777);
if(fd==-1)
{
perror("open");
exit(1);
}
//初始化互斥信号量mutex为1
ret=sem_init(&mutex,0,1);
//初始化avail信号量为N
ret=sem_init(&avail,0,N);
//初始化full信号量为0
ret=sem_init(&full,0,0);
if(ret!=0)
{
perror("sem_init");
}
//创建两个线程
ret=pthread_create(&id1,NULL,(void *)productor,NULL);
if(ret!=0)
perror("pthread cread1");
ret=pthread_create(&id2,NULL,(void *)consumer,NULL);
if(ret!=0)
perror("pthread cread2");
pthread_join(id1,NULL);
pthread_join(id2,NULL);

exit(0);
}

void productor(void *arg)
{
int nwrite;
while(time(NULL)<end_time)
{
//p操作avail和mutex
sem_wait(&avail);
sem_wait(&mutex);
//生产者写入数据
if((nwrite=write(fd,"hello",5))==-1)
{
if(errno==EAGAIN)
printf("The FIFO has not been read yet.Please try later\n");
}
else
{
flag++;
printf("%dst write hello to the FIFO\n",flag);
}
//V操作
sem_post(&full);
sem_post(&mutex);
sleep(1);
}
}

void consumer(void *arg)
{
int nread;
while(time(NULL)<end_time)
{
//p操作full和mutex
sem_wait(&full);
sem_wait(&mutex);
//生产者写入数据
if((nread=read(fd,buf_r,5))==-1)
{
if(errno==EAGAIN)
printf("No data yet\n");
}
else
{
printf("%dst read %s from the FIFO\n",flag,buf_r);
}
//V操作
sem_post(&avail);
sem_post(&mutex);
sleep(1);
}

}




wugang@ubuntu:~/Code$ sudo ./product
Preapring for reading bytes...
1st write hello to the FIFO
1st read hello from the FIFO
2st write hello to the FIFO
2st read hello from the FIFO
3st write hello to the FIFO
3st read hello from the FIFO
4st write hello to the FIFO
4st read hello from the FIFO
5st write hello to the FIFO
5st read hello from the FIFO
6st write hello to the FIFO
6st read hello from the FIFO
7st write hello to the FIFO
7st read hello from the FIFO
8st write hello to the FIFO
8st read hello from the FIFO
9st write hello to the FIFO
9st read hello from the FIFO
10st write hello to the FIFO
10st read hello from the FIFO
11st write hello to the FIFO
11st read hello from the FIFO
12st write hello to the FIFO
12st read hello from the FIFO
13st write hello to the FIFO
13st read hello from the FIFO
14st write hello to the FIFO
14st read hello from the FIFO
15st write hello to the FIFO
15st read hello from the FIFO
16st write hello to the FIFO
16st read hello from the FIFO
17st write hello to the FIFO
17st read hello from the FIFO
18st write hello to the FIFO
18st read hello from the FIFO
19st write hello to the FIFO
19st read hello from the FIFO
20st write hello to the FIFO
20st read hello from the FIFO
21st write hello to the FIFO
21st read hello from the FIFO
22st write hello to the FIFO
22st read hello from the FIFO
23st write hello to the FIFO
23st read hello from the FIFO
24st write hello to the FIFO
24st read hello from the FIFO
25st write hello to the FIFO
25st read hello from the FIFO
26st write hello to the FIFO
26st read hello from the FIFO
27st write hello to the FIFO
27st read hello from the FIFO
28st write hello to the FIFO
28st read hello from the FIFO
29st write hello to the FIFO
29st read hello from the FIFO
30st write hello to the FIFO
30st read hello from the FIFO



...

/*product.c*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <fcntl.h>
#define FIFO "./myfifo3"
#define N 5
int lock_var;
time_t end_time;
char buf_r[100];
sem_t mutex,full,avail;
int fd;
void productor(void *arg);
void consumer(void *arg);
int flag;

int main(int argc,char *argv[])
{
    pthread_t id1,id2;
    int ret;
    flag=0;
    end_time=time(NULL)+30;
    unlink(FIFO);
    //创建有名管道
    if(mkfifo(FIFO,O_CREAT|O_EXCL)<0)
        printf("cannot creat myfifo\n");
    printf("Preapring for reading bytes...\n");
    memset(buf_r,0,sizeof(buf_r));
    //打开管道,无阻赛
    fd=open(FIFO,O_RDWR|O_NONBLOCK,0777);
    if(fd==-1)
    {
        perror("open");
        exit(1);
    }
    //初始化互斥信号量mutex为1
    ret=sem_init(&mutex,0,1);
    //初始化avail信号量为N
    ret=sem_init(&avail,0,N);
    //初始化full信号量为0
    ret=sem_init(&full,0,0);
    if(ret!=0)
    {
        perror("sem_init");
    }
    //创建两个线程
    ret=pthread_create(&id1,NULL,(void *)productor,NULL);
    if(ret!=0)
        perror("pthread cread1");
    ret=pthread_create(&id2,NULL,(void *)consumer,NULL);
    if(ret!=0)
        perror("pthread cread2");
    pthread_join(id1,NULL);
    pthread_join(id2,NULL);

    exit(0);
}

void productor(void *arg)
{
    int nwrite;
    while(time(NULL)<end_time)
    {
        //p操作avail和mutex
        sem_wait(&avail);
        sem_wait(&mutex);
        //生产者写入数据
        if((nwrite=write(fd,"hello",5))==-1)
        {
           if(errno==EAGAIN)
           printf("The FIFO has not been read yet.Please try later\n");
        }
        else
           {
              flag++;
              printf("%dst write hello to the FIFO\n",flag);
           }
        //V操作
        sem_post(&full);
        sem_post(&mutex);
        sleep(1);
    }
}

void consumer(void *arg)
{
     int nread;
    while(time(NULL)<end_time)
    {
        //p操作full和mutex
        sem_wait(&full);
        sem_wait(&mutex);
        //生产者写入数据
        if((nread=read(fd,buf_r,5))==-1)
        {
           if(errno==EAGAIN)
           printf("No data yet\n");
        }
        else
        {
             printf("%dst read %s from the FIFO\n",flag,buf_r);
        }
        //V操作
        sem_post(&avail);
        sem_post(&mutex);
        sleep(1);
    }

}


wugang@ubuntu:~/Code$ sudo ./product
Preapring for reading bytes...
1st write hello to the FIFO
1st read hello from the FIFO
2st write hello to the FIFO
2st read hello from the FIFO
3st write hello to the FIFO
3st read hello from the FIFO
4st write hello to the FIFO
4st read hello from the FIFO
5st write hello to the FIFO
5st read hello from the FIFO
6st write hello to the FIFO
6st read hello from the FIFO
7st write hello to the FIFO
7st read hello from the FIFO
8st write hello to the FIFO
8st read hello from the FIFO
9st write hello to the FIFO
9st read hello from the FIFO
10st write hello to the FIFO
10st read hello from the FIFO
11st write hello to the FIFO
11st read hello from the FIFO
12st write hello to the FIFO
12st read hello from the FIFO
13st write hello to the FIFO
13st read hello from the FIFO
14st write hello to the FIFO
14st read hello from the FIFO
15st write hello to the FIFO
15st read hello from the FIFO
16st write hello to the FIFO
16st read hello from the FIFO
17st write hello to the FIFO
17st read hello from the FIFO
18st write hello to the FIFO
18st read hello from the FIFO
19st write hello to the FIFO
19st read hello from the FIFO
20st write hello to the FIFO
20st read hello from the FIFO
21st write hello to the FIFO
21st read hello from the FIFO
22st write hello to the FIFO
22st read hello from the FIFO
23st write hello to the FIFO
23st read hello from the FIFO
24st write hello to the FIFO
24st read hello from the FIFO
25st write hello to the FIFO
25st read hello from the FIFO
26st write hello to the FIFO
26st read hello from the FIFO
27st write hello to the FIFO
27st read hello from the FIFO
28st write hello to the FIFO
28st read hello from the FIFO
29st write hello to the FIFO
29st read hello from the FIFO
30st write hello to the FIFO
30st read hello from the FIFO