操作系统分析的一个小实验
运用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);
}
参考文献:
http://blog.csdn.net/yaozhiyi/article/details/7561759
《Linux程序设计(第四版)》最后说一下,《Linux程序设计》这本书写的真不错,很值得学习。
博主还是菜鸟,有不对或需要改进的地方还请大牛们多多指教。