Linux下信号量实现进程同步、互斥(生产者消费者问题)

操作系统分析的一个小实验

运用Linux平台下的进程间同步和互斥机制,设计实现一个生产者进程和一个消费者进程通过共享内存交换数据,并能达到以下要求:一个仓库可以存放K件物品。生产者每生产一件产品,将产品放入仓库,仓库满了就停止生产。消费者每次从仓库中去一件物品,然后进行消费,仓库空时就停止消费。

因为有生产者、消费者以及仓库,所以需要三个信号量:full、empty和mutex分别表示仓库的库存的同步信号量、仓库为空的同步信号量和正在对仓库进行操作的互斥信号量。其初值分别为0、仓库的容量(程序中使用MAX_BUFFRT_SIZE表示)和0。流程图如图所示。

生产者:

p(empty) -> p(mutex) -> v(mutex) -> v(full)

消费者:

p(full) -> p(mutex) -> v(mutex) ->v(empty)

环境如下:Ubuntu14.04.3,gcc(4.8.2),codeblocks(13.12)

//copyright @Vista(njupt)
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/*KEY 为申请信号量的键值,通过键值可以用于不同进程间的通信*/
#define KEY (key_t)14010322
/*仓库大小*/
#define MAX_BUFFER_SIZE	10

/*union semun 常用于semctl的最后一个参数,
 有的系统上sem.h已包含,可能会因为重复而报错,
 如果报错请将其去除。*/
union semun {
    int val;
    struct semid_ds *buf;
    ushort *array;
};

/*仓库,buffer数组用于存贮生产的商品编号(product)
 write用于记录生产者生产的下一个商品应存贮在仓库的位置
 read用于记录消费者消费的下一个商品在仓库中的位置
 很明显这是个循环队列,write、read分别为头尾指针*/
typedef struct _tagShareBuffer{
    int buffer[MAX_BUFFER_SIZE];
    int writer;
    int reader;
}SHAREBUFFER;

static void p(int semid ,int semNum);
static void v(int semid ,int semNum);

int main()
{
    int	  shmid;                        //共享内存的id
    char* shmPtr;
    int	semid;                          //信号量指针
    int product = 1;                    //产品编号,从1开始
    SHAREBUFFER* pSharebuffer;          //共享内存的指针
    int i;
    
    /*申请信号量*/
    if ((semid = semget(KEY,3,IPC_CREAT|0660)) == -1)
    {
        printf("semget error! \n");
        return -1;
    }
    
    /*三个信号量的初始赋值*/
    union semun arg[3];
    arg[0].val = 1;                     //mutex(0)
    arg[1].val = MAX_BUFFER_SIZE;       //empty(1)
    arg[2].val = 0;                     //full(2)
    
    for(i=0;i<3;i++)
        semctl(semid,i,SETVAL,arg[i]);
    
    /*显示三个信号量的初值*/
    for(i=0;i<3;i++)
        printf("The semval(%d) = %d\n",i,semctl(semid,i,GETVAL,NULL));
    
    /*申请共享内存*/
    if ((shmid = shmget(IPC_PRIVATE,sizeof(SHAREBUFFER),0600)) < 0)
    {
        printf("shmget error!\n");
        return -1;
    }
    
    
    if((shmPtr = (char*)shmat(shmid,0,0)) == (void*)-1)
    {
        printf("shmat error!\n");
        return -1;
    }
    
    memset((void*)shmPtr,0,sizeof(SHAREBUFFER));
    
    pSharebuffer = (SHAREBUFFER*)shmPtr;
    
    /*创建进程*/
    pid_t pid = fork();
    if (pid < 0){
        printf("creat process error.\n");
        return -1;
    }
    if (pid == 0){
        /*子进程,消费者*/
        while(1)
        {
            
            p(semid ,2);	//P(full)
            p(semid ,0);	//P(mutex)
            
            /*输出三个信号量的值和消费仓库中的产品编号以及位置*/
            product = pSharebuffer->buffer[pSharebuffer->reader];
            for(i=0;i<3;i++)
                printf("son:The semval(%d) = %d\n",i,semctl(semid,i,GETVAL,NULL));
            printf("son:release the product from buffer[%d] = %d;\n",pSharebuffer->reader,product);
            
            /* reader++ */
            pSharebuffer->reader = (pSharebuffer->reader + 1) % MAX_BUFFER_SIZE;
            
            v(semid ,0);	//V(mutex)
            v(semid ,1);    //V(empty)
            
            sleep(1);       //调节消费速度
        }
    }
    else {
        //父进程,生产者
        while(1)
        {
            p(semid ,1);	//P(empty)
            p(semid ,0);	//P(mutex)
            
            /*输出三个信号量的值和生产仓库中的产品编号以及位置*/
            pSharebuffer->buffer[pSharebuffer->writer] = product;
            for(i=0;i<3;i++)
                printf("parents:The semval(%d) = %d\n",i,semctl(semid,i,GETVAL,NULL));
            printf("parents:Produced the product into buffer[%d] = %d;\n",pSharebuffer->writer,product);
            /* 产品编号++ */
            product++;
            /* write++ */
            pSharebuffer->writer = (pSharebuffer->writer + 1) % MAX_BUFFER_SIZE;
            
            v(semid ,0);	//V(mutex)
            v(semid, 2);    //V(full)
            
            sleep(1);       //调节生产速度
        }
    }
    return 0;
}

/* p操作 */
void p(int semid ,int semNum){
    struct sembuf sb;
    sb.sem_num = semNum;
    sb.sem_op = -1;
    sb.sem_flg = SEM_UNDO;
    semop(semid, &sb, 1);
}

/* v操作 */
void v(int semid ,int semNum){
    struct sembuf sb;
    sb.sem_num = semNum;
    sb.sem_op = 1;
    sb.sem_flg = SEM_UNDO;
    semop(semid, &sb, 1);
}


实验的结果如下。值得注意的是,如果输出的结果总是差不多的,那么可能要调节一下生产或者是消费的速度,用sleep就好了。有意义进程的并发性,结果几乎是随机的。 Linux下信号量实现进程同步、互斥(生产者消费者问题)_第1张图片

参考文献:

http://blog.csdn.net/yaozhiyi/article/details/7561759

《Linux程序设计(第四版)》

最后说一下,《Linux程序设计》这本书写的真不错,很值得学习。
博主还是菜鸟,有不对或需要改进的地方还请大牛们多多指教。


你可能感兴趣的:(操作系统)