实现信号量(二) pipe实现信号量

来源:互联网 发布:mac safari开启无痕 编辑:程序博客网 时间:2024/04/30 05:02

        上一篇开篇,实现了线程结构体。现在来开启模拟信号量吧。

        本文是用pipe管道来实现信号量。

        用管道实现信号量的原理是:当管道里没有内容时,调用read函数读,会造成线程的阻塞。用pv操作来描述的话,当用户调用p操作申请一个资源时,内部实现是调用read函数读取管道里的一个字符。如果能读取,就马上返回。否则阻塞线程。当用户调用v操作释放一个资源时,内部实现是往管道写入一个字符。

        pipe_sem.hpp文件

#ifndef PIPE_SEM_HPP#define PIPE_SEM_HPP#include<pthread.h>typedef struct pipe_sem_tag{    int fd[2];    pthread_mutex_t mutex;    int valid; //同线程结构体中的一样。}pipe_sem_t;//num是信号量的初始值int pipe_sem_init(pipe_sem_t* pip, int num);int pipe_sem_p(pipe_sem_t* pip);int pipe_sem_tryP(pipe_sem_t* pip);int pipe_sem_v(pipe_sem_t* pip);int pipe_sem_destroy(pipe_sem_t* pip);#endif // PIPE_SEM_HPP


        pipe_sem.cpp文件

#include"pipe_sem.hpp"#include<sys/time.h>#include<sys/select.h>#include<errno.h>#include<unistd.h>#include<fcntl.h>#include<pthread.h>#define PIPE_SEM_VALID 0x78da//用管道实现信号量的原理是:当管道里没有内容时,调用read函数读,会造成线程的阻塞//用pv操作来描述的话,当用户调用p操作申请一个资源时,内部实现是调用read函数读取//管道里的一个字符。如果能读取,就马上返回。否则阻塞线程。//当用户调用v操作释放一个资源时,内部实现是往管道写入一个字符。//这个函数不是线程安全的。本系列文章中的所有init函数都不是线程安全的。//使用者应该在创建多个线程前就初始化好这些结构体。int pipe_sem_init(pipe_sem_t* pip, int num){    int status;    if( pip == NULL || num < 0 )        return EINVAL;    status = pipe(pip->fd);    if( status != 0 )        return errno;    status = pthread_mutex_init(&pip->mutex, NULL);    if( status != 0 )        goto error;//通过向管道写入num个字符,模拟信号量的初始值为num    while( num != 0 )    {        status = write(pip->fd[1], "v", 1);;        if( status != 1 )            goto error;        --num;    }    pip->valid = PIPE_SEM_VALID;    return 0;    error:        close(pip->fd[0]); //ignore the error        close(pip->fd[1]); //ignore the error        return status;}void cleanup_unlock(void* arg){    pthread_mutex_t *mutex = (pthread_mutex_t*)arg;    pthread_mutex_unlock(mutex);}int pipe_sem_p(pipe_sem_t* pip){    static char ch;    int status, rt;    if( pip == NULL || pip->valid != PIPE_SEM_VALID)        return EINVAL;    //when one thread run the this function, another thread run the    //pipe_sem_tryP, it will be wrong  if don't use the mutex.    //because pipe_sem_tryP function will change to fd[0] to non block    status = pthread_mutex_lock(&pip->mutex); //得加锁    if( status != 0 )        return status;    //因为read是可取消点。所以当线程在read中睡眠的时候,    //其他线程调用thread_cancel取消这个睡眠的线程时,    //睡眠的线程将苏醒,然后继续锁住mutex, 之后就退出终止。    //所以,要设定一个清理函数,发生这种情况时,在清理函数中解锁。    pthread_cleanup_push(cleanup_unlock, &pip->mutex);    while( (status = read(pip->fd[0], &ch, 1) ) == -1 )    {        if( errno == EINTR ) //被一个信号中断。遵循POSIX.1标准OS的不会这种情况            continue;        else            break;    }    if( status == - 1)        rt =  errno;    else if( status == 0 ) //the end of pipe。即管道的写端被关闭了        rt = EPIPE;    else        rt = 0;    pthread_cleanup_pop(0);    pthread_mutex_unlock(&pip->mutex); //ignore the error    return rt;}//通过fcntl函数把文件描述字fd[1]修改为非阻塞的。从而实现尝试功能。int pipe_sem_tryP(pipe_sem_t* pip){    int status, rt;    int flags = 0;    static char ch;    if( pip == NULL || pip->valid != PIPE_SEM_VALID )        return EINVAL;    //when one thread run the this function, another thread run the    //pipe_sem_p, it will be wrong  if don't use the mutex.    //because this function will change to fd[0] to non block    status = pthread_mutex_lock(&pip->mutex);    if( status != 0 )        return status;    pthread_cleanup_push(cleanup_unlock, &pip->mutex);    if( (flags = fcntl(pip->fd[0], F_GETFL, 0)) < 0 )    {        pthread_mutex_unlock(&pip->mutex);        return errno;    }    flags |= O_NONBLOCK; //turn it to non block    if( (flags = fcntl(pip->fd[0], F_SETFL, flags)) < 0 )    {        pthread_mutex_unlock(&pip->mutex);        return errno;    }    status = read(pip->fd[0], &ch, 1);    if( status == 0 ) //the end of pipe        rt =  EPIPE;    else if( status > 0 )        rt = 0;    else        rt = errno;    pthread_cleanup_pop(0);    error:    flags &= ~O_NONBLOCK; //turn it to block    fcntl(pip->fd[0], F_SETFL, flags);    pthread_mutex_unlock(&pip->mutex);    return rt;}int pipe_sem_v(pipe_sem_t* pip){    if( pip == NULL || pip->valid != PIPE_SEM_VALID)        return EINVAL;    return write(pip->fd[1], "v", 1);}int pipe_sem_destroy(pipe_sem_t* pip){    if( pip == NULL || pip->valid != PIPE_SEM_VALID)        return EINVAL;    close(pip->fd[0]); //ignore the error    close(pip->fd[1]); //ignore the errorpip->valid = 0;    return pthread_mutex_destroy(&pip->mutex);}



        下面给出测试代码。
#include "pipe_sem.hpp"#include"Thread.hpp"#include  <stdio.h>#include  <string.h>#include  <sys/types.h>#include  <fcntl.h>#include  <stdlib.h>#include  <unistd.h>#include  <errno.h>#defineNBUFF 8#define BUFFSIZE 4096struct {/* data shared by producer and consumer */  struct {    chardata[BUFFSIZE];/* a buffer */    ssize_tn;/* count of #bytes in the buffer */  } buff[NBUFF];/* NBUFF of these buffers/counts */  pipe_sem_t nempty, nfull;/* semaphores, not pointers */  pipe_sem_t writer_mutex, reader_mutex;} shared;int writer_index = 0, reader_index = 0;int fd;/* input file to copy to stdout */void* produce(void *), *consume(void *);void* produce_tryP(void *arg);//运行测试程序时要输入一个文件名作为参数int main(int argc, char **argv){    Thread_t tid_produce1, tid_produce2, tid_produce3;    Thread_t tid_consume1, tid_consume2;    if (argc != 2)    {        printf("use <pathname> as pramater \n");        exit(1);    }    fd = open(argv[1], O_RDONLY);    if( fd == -1 )    {        printf("cann't open the file\n");        return -1;    }    pipe_sem_init(&shared.writer_mutex, 1);    pipe_sem_init(&shared.reader_mutex, 1);    pipe_sem_init(&shared.nempty, NBUFF);    pipe_sem_init(&shared.nfull, 0);    thread_init(&tid_produce1);    thread_init(&tid_produce2);    thread_init(&tid_produce3);    thread_init(&tid_consume1);    thread_init(&tid_consume2);    thread_create(&tid_consume1, NULL, consume);    thread_create(&tid_consume2, NULL, consume);    thread_create(&tid_produce1, NULL, produce);    thread_create(&tid_produce2, NULL, produce);    thread_create(&tid_produce3, NULL, produce_tryP);    thread_start(&tid_consume1, NULL);    thread_start(&tid_consume2, NULL);    thread_start(&tid_produce1, NULL);    thread_start(&tid_produce2, NULL);    thread_start(&tid_produce3, NULL);    thread_join(&tid_consume1, NULL);    thread_join(&tid_consume2, NULL);    thread_join(&tid_produce1, NULL);    thread_join(&tid_produce2, NULL);    thread_join(&tid_produce3, NULL);    thread_destroy(&tid_consume1);    thread_destroy(&tid_consume2);    thread_destroy(&tid_produce1);    thread_destroy(&tid_produce2);    thread_destroy(&tid_produce3);pipe_sem_destroy(&shared.writer_mutex);pipe_sem_destroy(&shared.reader_mutex);    pipe_sem_destroy(&shared.nempty);    pipe_sem_destroy(&shared.nfull);    exit(0);}void *produce(void *arg){    while( 1 )    {        pipe_sem_p(&shared.nempty);/* wait for at least 1 empty slot *///这个本来可以用互斥量的。不过为了多测试pipe_sem_t,就使用信号量了        pipe_sem_p(&shared.writer_mutex);//读取文件的内容        shared.buff[writer_index].n = read(fd, shared.buff[writer_index].data, BUFFSIZE);//文件的内容已经读取完毕。        if( shared.buff[writer_index].n == 0 )        {//离开前,要记得解锁            pipe_sem_v(&shared.nfull);            pipe_sem_v(&shared.writer_mutex);             return NULL;        }        writer_index = (writer_index+1)%NBUFF; //循环使用缓冲区        pipe_sem_v(&shared.nfull);        pipe_sem_v(&shared.writer_mutex);    }    return NULL;}void* produce_tryP(void *arg){    int status;    while( 1 )    {        /* wait for at least 1 empty slot */        while( 1 )        {            status = pipe_sem_tryP(&shared.nempty);            if( status == 0 )                break;            else if( status == EAGAIN )            {                usleep(10*1000); //sleep 10 毫秒                continue;            }            else                return NULL;        }        pipe_sem_p(&shared.writer_mutex);        shared.buff[writer_index].n = read(fd, shared.buff[writer_index].data, BUFFSIZE);        if( shared.buff[writer_index].n == 0 )        {            pipe_sem_v(&shared.nfull);            pipe_sem_v(&shared.writer_mutex);            return NULL;        }        writer_index = (writer_index+1)%NBUFF;        pipe_sem_v(&shared.nfull);        pipe_sem_v(&shared.writer_mutex);    }    return NULL;}void* consume(void *arg){    while( 1 )    {        pipe_sem_p(&shared.nfull);        pipe_sem_p(&shared.reader_mutex);//缓冲区里面没有数据了。        if( shared.buff[reader_index].n == 0)    {pipe_sem_v(&shared.nempty);pipe_sem_v(&shared.reader_mutex);return NULL;}        write(STDOUT_FILENO, shared.buff[reader_index].data, shared.buff[reader_index].n);        reader_index = (reader_index+1)%NBUFF;        pipe_sem_v(&shared.nempty);        pipe_sem_v(&shared.reader_mutex);    }    return NULL;}

        测试结果:


0 0
原创粉丝点击