Linuxc - posix信号量的应用(生产者-消费者)

insularisland 2019-11-05

linuxc-posix信号量的应用(生产者-消费者)

在unix环境高级编程中提到了两种信号量,两种信号量作用和应用场景基本相同,但是posix信号量更常用,跨平台能力强,性能也更优越,以下主要说posix信号量。

POSIX信号量

简介见《unix环境高级编程》第15.10小节。

信号量和互斥锁(mutex)的区别:互斥锁只允许一个线程进入临界区,而信号量允许多个线程同时进入临界区。

POSIX信号量有两种形式:命名的和未命名的。它们的差异在于创建和销毁的形式上,但其他工作一样。未命名信号量只存在于内存中,并要求能使用信号量的进程必须可以访问内存。这意味着他们只能应用在同一进程的线程,或者不同进程中已经映射相同内存内容到它们的地址空间中的线程。相反,命名信号量可以通过名字访问,因此可以被任何已知它们名字的进程中的线程使用

程序退出时,未命名信号量创建在进程空间中,随进程消亡,命名信号量需要手动清理,可以使用信号处理函数处理异常退出情况。

主要函数:

#include <semaphore.h>
//创建一个新的命名信号量或者使用一个现有信号量
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);

//初始化未命名的信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
//销毁未命名的信号量,只有用sem_init初始化的信号量才能用sem_destroy销毁。
int sem_destroy(sem_t *sem);

//信号量的减1操作(p操作)
int sem_trywait(sem_t *sem);//非阻塞,p失败,返回-1
int sem_wait(sem_t *sem);//阻塞
int sem_timedwait(sem_t *restrict sem, //p失败,阻塞一定时间
                  const struct timespec * restrict tsptr);
//信号量的加1操作(v操作)
int sem_post(sem_t *sem);
//检索信号量的值
int sem_getvalue(sem_t *restrict sem, int *restrict valp);

生产者-消费者

“生产者——消费者”问题是Linux多线程编程中的经典问题,主要是利用信号量处理线程间的同步和互斥问题。(以下使用posix信号量)

“生产者——消费者”问题描述如下:

​ 有一个有限缓冲区(这里用有名管道实现 FIFO 式缓冲区)和两个线程:生产者和消费者,它们分别不停地把产品放入缓冲区和从中拿走产品。一个生产者在缓冲区满的时候必须等待,一个消费者在缓冲区空的时候也不得不等待。另外,因为缓冲区是临界资源,所以生产者和消费者之间必须互斥进行。它们之间的关系如下:
Linuxc - posix信号量的应用(生产者-消费者)

这里要求使用有名管道来模拟有限缓冲区,并用信号量来解决“生产者——消费者”问题中的同步和互斥问题。

  1. 信号量分析

    这里使用3个信号量,其中两个信号量 avail 和 full 分别用于解决生产者和消费者线程之间的互斥问题。其中avail 表示缓冲区的空单元数,初始值为N;full 表示缓冲区非空单元数,初始值为 0 ; mutex 是互斥信号量 ,初始值为 1(当然也可以用互斥锁来实现互斥操作)。

  2. 画出流程图

    Linuxc - posix信号量的应用(生产者-消费者)

  3. 编写代码

    本实验的代码中缓冲区拥有3个单元,每个单元为5个字节。为了尽量体现每个信号量的意义,在程序中生产过程和消费过程是随机(采取0~5s 的随机事件间隔)进行的,而且生产者的速度比消费者的速度平均快两倍左右。生产者一次生产一个单元的产品(放入hello字符串),消费者一次消费一个单元的产品。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <pthread.h>
    #include <errno.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <semaphore.h>
    
    #define MYFIFO             "myfifo"
    #define BUFFER_SIZE     3
    #define UNIT_SIZE         5
    #define RUN_TIME         30
    #define DELAY_TIME_LEVELS 5
    
    void *producer(void *arg);//生产者
    void *customer(void *arg);//消费者
    
    int fd;
    time_t end_time;
    sem_t mutex, full, avail;
    
    int main(int argc, const char *argv[])
    {
        int ret;
        pthread_t thrd_prd_id,thrd_cst_id;
    
        srand(time(NULL));
        end_time = time(NULL) + RUN_TIME;
    
        //创建管道
        if ((mkfifo(MYFIFO, 0644) < 0) && (errno != EEXIST)) {
            perror("mkfifo error!");
            exit(-1);
        }
    
        //打开管道
        fd = open(MYFIFO, O_RDWR);
        if (fd == -1) {
            perror("open fifo error");
            exit(-1);
        }
    
        //初始化信号量
        ret = sem_init(&mutex, 0, 1);
        ret += sem_init(&avail, 0, BUFFER_SIZE);
        ret += sem_init(&full, 0, 0);
        if (ret != 0) {
            perror("sem_init error");
            exit(-1);
        }
    
        //创建两个线程
        ret = pthread_create(&thrd_prd_id, NULL, producer, NULL);
        if (ret != 0) {
            perror("producer pthread_create error");
            exit(-1);
        }
        ret = pthread_create(&thrd_cst_id, NULL, customer, NULL);
        if (ret != 0) {
            perror("customer pthread_create error");
            exit(-1);
        }
        pthread_join(thrd_prd_id, NULL);
        pthread_join(thrd_cst_id, NULL);
        sem_destroy(&mutex);
        sem_destroy(&avail);
        sem_destroy(&full);
        close(fd);
        unlink(MYFIFO);
    
        return 0;
    }
    
    void *producer(void *arg)
    {
        int real_write;
        int delay_time;
    
        while (time(NULL) < end_time) {
            delay_time = rand()%DELAY_TIME_LEVELS;
            sleep(delay_time);
            
            //p操作
            sem_wait(&avail);
            sem_wait(&mutex);
            printf("\nproducer have delayed %d seconds\n", delay_time);
    
            //生产者写入数据  执行的操作
            if (-1 == (real_write = write(fd, "hello", UNIT_SIZE))) {
                if (errno == EAGAIN) {
                    printf("The buffer is full, please wait for reading!\n");
                }
            } else {
                printf("producer writes %d bytes to the FIFO\n", real_write);
                printf("Now,the buffer left %d spaces!\n", avail);
            }
            //v操作
            sem_post(&full);
            sem_post(&mutex);
        }
        pthread_exit(NULL);
    }
    
    void *customer(void *arg)
    {
        unsigned char read_buffer[UNIT_SIZE];
        int real_read;
        int delay_time;
    
        while (time(NULL) < end_time) {
            delay_time = rand()%DELAY_TIME_LEVELS;
            sleep(delay_time);
            sem_wait(&full);
            sem_wait(&mutex);
            memset(read_buffer, 0, UNIT_SIZE);
            printf("\nCustomer have delayed %d seconds\n", delay_time);
    
            //消费 操作
            if (-1 == (real_read = read(fd, read_buffer, UNIT_SIZE))) {
                if (errno == EAGAIN) {
                    printf("The buffer is empty, please wait for writing!\n");
                }
            } else {
                printf("customer reads %d bytes from the FIFO\n", real_read);
            }
    
            sem_post(&avail);
            sem_post(&mutex);
        }
    
        pthread_exit(NULL);
    }

    执行结果如下:

    root@jonathan-pc:~/test# gcc posixsem.c -lpthread -o posixsme
    root@jonathan-pc:~/test# ./posixsme 
    
    producer have delayed 3 seconds
    producer writes 5 bytes to the FIFO
    Now,the buffer left 0 spaces!
    
    Customer have delayed 2 seconds
    customer reads 5 bytes from the FIFO
    
    producer have delayed 1 seconds
    producer writes 5 bytes to the FIFO
    Now,the buffer left 0 spaces!
    
    Customer have delayed 2 seconds
    customer reads 5 bytes from the FIFO
    
    producer have delayed 4 seconds
    producer writes 5 bytes to the FIFO
    Now,the buffer left 0 spaces!
    
    producer have delayed 0 seconds
    producer writes 5 bytes to the FIFO
    Now,the buffer left 0 spaces!
    
    producer have delayed 0 seconds
    producer writes 5 bytes to the FIFO
    Now,the buffer left 0 spaces!
    
    Customer have delayed 2 seconds
    customer reads 5 bytes from the FIFO
    
    Customer have delayed 0 seconds
    customer reads 5 bytes from the FIFO
    
    producer have delayed 0 seconds
    producer writes 5 bytes to the FIFO
    Now,the buffer left 0 spaces!
    
    producer have delayed 0 seconds
    producer writes 5 bytes to the FIFO
    Now,the buffer left 0 spaces!
    
    Customer have delayed 1 seconds
    customer reads 5 bytes from the FIFO
    
    Customer have delayed 1 seconds
    customer reads 5 bytes from the FIFO
    
    Customer have delayed 0 seconds
    customer reads 5 bytes from the FIFO
    
    producer have delayed 4 seconds
    producer writes 5 bytes to the FIFO
    Now,the buffer left 0 spaces!
    
    Customer have delayed 1 seconds
    customer reads 5 bytes from the FIFO
    
    producer have delayed 2 seconds
    producer writes 5 bytes to the FIFO
    Now,the buffer left 0 spaces!
    
    Customer have delayed 1 seconds
    customer reads 5 bytes from the FIFO
    
    producer have delayed 2 seconds
    producer writes 5 bytes to the FIFO
    Now,the buffer left 0 spaces!
    
    producer have delayed 1 seconds
    producer writes 5 bytes to the FIFO
    Now,the buffer left 0 spaces!
    
    Customer have delayed 4 seconds
    customer reads 5 bytes from the FIFO
    
    producer have delayed 1 seconds
    producer writes 5 bytes to the FIFO
    Now,the buffer left 0 spaces!
    
    Customer have delayed 1 seconds
    customer reads 5 bytes from the FIFO
    
    Customer have delayed 3 seconds
    customer reads 5 bytes from the FIFO
    
    producer have delayed 4 seconds
    producer writes 5 bytes to the FIFO
    Now,the buffer left 0 spaces!
    
    Customer have delayed 2 seconds
    customer reads 5 bytes from the FIFO
    
    producer have delayed 4 seconds
    producer writes 5 bytes to the FIFO
    Now,the buffer left 0 spaces!
    
    producer have delayed 0 seconds
    producer writes 5 bytes to the FIFO
    Now,the buffer left 0 spaces!
    
    Customer have delayed 0 seconds
    customer reads 5 bytes from the FIFO
    
    producer have delayed 3 seconds
    producer writes 5 bytes to the FIFO
    Now,the buffer left 0 spaces!
    
    Customer have delayed 4 seconds
    customer reads 5 bytes from the FIFO
    
    producer have delayed 4 seconds
    producer writes 5 bytes to the FIFO
    Now,the buffer left 0 spaces!

相关推荐