您的位置:首页 > 运维架构 > Linux

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

2016-10-27 15:59 821 查看
操作系统分析的一个小实验

运用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 <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>

/*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就好了。有意义进程的并发性,结果几乎是随机的。


参考文献:

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

《Linux程序设计(第四版)》
最后说一下,《Linux程序设计》这本书写的真不错,很值得学习。

博主还是菜鸟,有不对或需要改进的地方还请大牛们多多指教。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux PV操作 进程
相关文章推荐