实验二进程同步实验.docx
《实验二进程同步实验.docx》由会员分享,可在线阅读,更多相关《实验二进程同步实验.docx(17页珍藏版)》请在冰豆网上搜索。
![实验二进程同步实验.docx](https://file1.bdocx.com/fileroot1/2023-2/2/fe41461d-37ea-4110-90af-b384e22dec60/fe41461d-37ea-4110-90af-b384e22dec601.gif)
实验二进程同步实验
实验二进程同步
一、实验目的:
掌握根本的同步算法,理解经典进程同步问题的本质;学习使用Linux的进程同步机制,掌握相关API的使用方法;能利用信号量机制,采用多种同步算法实现不会发生死锁的哲学家进餐程序。
二、实验平台:
虚拟机:
VMWare9以上
操作系统:
Ubuntu12.04以上
编辑器:
Gedit|Vim
编译器:
Gcc
三、实验容:
〔1〕以哲学家进餐模型为依据,在Linux控制台环境下创立5个进程,用semget函数创立一个信号量集〔5个信号量,初值为1〕,模拟哲学家的思考和进餐行为:
每一位哲学家饥饿时,先拿起左手筷子,再拿起右手筷子;筷子是临界资源,为每一支筷子定义1个互斥信号量;想拿到筷子需要先对信号量做P操作,使用完释放筷子对信号量做V操作。
伪代码描述:
semaphorechopstick[5]={1,1,1,1,1};
•第i位哲学家的活动可描述为:
do{
printf("%disthinking\n",i);
printf("%dishungry\n",i);
wait(chopstick[i]);//拿左筷子
wait(chopstick[(i+1)%5]);//拿右筷子
printf("%diseating\n",i);
signal(chopstick[i]);//放左筷子
signal(chopstick[(i+1)%5]);//放右筷子
…
}while[true];
运行该组进程,观察进程是否能一直运行下去,假设停滞那么发生了什么现象?
并分析原因。
〔2〕解决哲学家进餐问题可采用如下方法:
a.仅当哲学家的左、右两只筷子均可用时,才允许他拿起筷子进餐;b.至多只允许有4位哲学家同时去拿左边的筷子,最终能保证至少有一位哲学家能够进餐;c.规定奇数号哲学家先拿起他左手的筷子,然后再拿起他右手的筷子,而偶数号哲学家那么先拿起他右手的筷子,然后再拿起他左手的筷子。
方法a在例如程序中给出,请用方法b和c写出不会发生死锁的哲学家进餐程序。
〔3〕设计程序,实现生产者/消费者进程(线程)的同步与互斥。
在该程序中创立4个进程〔或线程〕模拟生产者和消费者,实现进程(线程)的同步与互斥。
实验结果:
使用a方法结果哲学家就餐问题
使用b方法解决哲学家就餐问题
源码如下:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
unionsemun
{
intval;
structsemid_ds*buf;
unsignedshort*array;
structseminfo*_buf;
};
#defineERR_EXIT(m)\
do{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
//获取互斥信号量
voidwait_mutex(intmutex)
{
structsembufsb={0,-1,0};
semop(mutex,&sb,1);//对互斥信号量进展操作
}
//取得筷子
voidwait_v(intsemid,intnum)
{
structsembufsb={num,-1,0};
semop(semid,&sb,1);
}
//释放筷子
voidsignal_p(intsemid,intnum)
{
structsembufsb={num,1,0};
semop(semid,&sb,1);
}
//释放互斥变量mutex
voidsignal_mutex(intsemid0)
{
structsembufsb={0,1,0};
semop(semid0,&sb,1);
}
//ph函数
voidph(intnum,intsemid,intsemid0)
{
intleft=num;
intright=(num+1)%5;
for(;;)
{
printf("%disthinking\n",num);
sleep
(1);
printf("%dishungry\n",num);
sleep
(1);
//wait操作,控制哲学家最多4人能进餐
wait_mutex(semid0);
wait_v(semid,left);
wait_v(semid,right);
printf("%diseating\n",num);
sleep
(1);
//signal操作
signal_p(semid,right);//释放右筷子
signal_p(semid,left);//释放左快子
signal_mutex(semid0);//释放互斥信号量
}
}
//主函数
intmain(intargc,char*argv[])
{
intsemid,semid0;
//创立两个信号量集
semid0=semget(IPC_PRIVATE,1,IPC_CREAT|0666);
semid=semget(IPC_PRIVATE,5,IPC_CREAT|0666);
//
unionsemunsu;
su.val=1;
inti;
for(i=0;i<5;i++)
{
//semctl()系统调用在一个信号量集(或集合中的单个信号量)上执行各种控制操作
semctl(semid,i,SETVAL,su);
}
//设定semid0信号量的初始值
unionsemunsu0;
su0.val=4;
semctl(semid0,0,SETVAL,su0);
//创立4个子进程
intnum=0;
pid_tpid;
for(i=1;i<5;i++)
{
pid=fork();
if(pid<0){ERR_EXIT("fork");}
if(pid==0){num=i;break;}
}
//第num个哲学家要做的事
ph(num,semid,semid0);
return0;
}
执行结果
使用c方法解决哲学家就餐问题
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
unionsemun
{
intval;
structsemid_ds*buf;
unsignedshort*array;
structseminfo*__buf;
};
#defineERR_EXIT(m)\
do{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
//取得筷子
voidwait_v(intsemid,intnum)
{
structsembufsb={num,-1,0};
semop(num,&sb,1);
}
//释放筷子
voidsignal_p(intsemid,intnum)
{
structsembufsb={num,-1,0};
semop(num,&sb,1);
}
//科学家要做的事
voidph(intsemid,intnum)
{
for(;;)//死循环
{
//判断哲学家的编号是奇数还是偶数
//奇数先申请左边的筷子,偶数先申请右边的筷子
if(num%2!
=0)
{//判断奇数
printf("%disthinking\n",num);
sleep
(1);
printf("%dishungry\n",num);
sleep
(1);
//wait操作
wait_v(semid,num);
wait_v(semid,(num+1)%5);
printf("%diseating\n",num);
sleep
(1);
//signal操作
signal_p(semid,(num+1)%5);
signal_p(semid,num);
}
if(num%2==0)
{//判断偶数
printf("%disthinking\n",num);
sleep
(1);
printf("%dishungry\n",num);
sleep
(1);
//wait操作
wait_v(semid,(num+1)%5);
wait_v(semid,num);
//signal操作
signal_p(semid,num);
signal_p(semid,(num+1)%5);
}
}
}
intmain(intargc,char*argv[])
{
intsemid;
//创立5个信号量
semid=semget(IPC_PRIVATE,5,IPC_CREAT|0666);
unionsemunsu;
su.val=1;
inti;
for(i=0;i<5;i++)
{
//注意第二个参数也是索引
semctl(semid,i,SETVAL,su);
}
//创立4个子进程
pid_tpid;
intnum=5;
for(i=0;i<4;i++)
{
pid=fork();
if(pid<0){ERR_EXIT("fork");}
if(pid==0){num=i;break;}
}
//哲学家要做的事
ph(semid,num);
return0;
}
生产者和消费者的同步与互斥
源代码如下:
#include
#include
#include
#include
#include
#defineN2//消费者或者生产者的数目
#defineM10//缓冲数目
intin=0;//生产者放置产品的位置
intout=0;//消费者取产品的位置
intbuff[M]={0};//缓冲初始化为0,开场时没有产品
sem_tempty_sem;//同步信号量,当满了时阻止生产者放产品
sem_tfull_sem;//同步信号量,当没产品时阻止消费者消费
pthread_mutex_tmutex;//互斥信号量,一次只有一个线程访问缓冲
intproduct_id=0;//生产者id
intprochase_id=0;//消费者id
/*打印缓冲情况*/
voidprint()
{
inti;
for(i=0;iprintf("%d",buff[i]);
printf("\n");
}
/*生产者方法*/
void*product()
{
intid=++product_id;
while
(1)
{
//用sleep的数量可以调节生产和消费的速度,便于观察
sleep
(1);
//sleep
(1);
sem_wait(&empty_sem);
pthread_mutex_lock(&mutex);
in=in%M;
printf("product%din%d.like:
\t",id,in);
buff[in]=1;
print();
++in;
pthread_mutex_unlock(&mutex);
sem_post(&full_sem);
}
}
/*消费者方法*/
void*prochase()
{
intid=++prochase_id;
while
(1)
{
//用sleep的数量可以调节生产和消费的速度,便于观察
sleep
(1);
//sleep
(1);
sem_wait(&full_sem);
pthread_mutex_lock(&mutex);
out=out%M;
printf("prochase%din%d.like:
\t",id,out);
buff[out]=0;
print();
++out;
pthread_mutex_unlock(&mutex);
sem_post(&empty_sem);
}
}
intmain()
{
pthread_tid1[N];
pthread_tid2[N];
inti;
intret[N];
//初始化同步信号量
intini1=sem_init(&empty_sem,0,M);
intini2=sem_init(&full_sem,0,0);
if(ini1&&ini2!
=0)
{
printf("seminitfailed\n");
exit
(1);
}
//初始化互斥信号量
intini3=pthread_mutex_init(&mutex,NULL);
if(ini3!
=0)
{
printf("mutexinitfailed\n");
exit
(1);
}
//创立N个生产者线程
for(i=0;i{
ret[i]=pthread_create(&id1[i],NULL,product,(void*)(&i));
if(ret[i]!
=0)
{
printf("product%dcreationfailed\n",i);
exit
(1);
}
}
//创立N个消费者线程
for(i=0;i{
ret[i]=pthread_create(&id2[i],NULL,prochase,NULL);
if(ret[i]!
=0)
{
printf("prochase%dcreationfailed\n",i);
exit
(1);
}
}
//销毁线程
for(i=0;i{
pthread_join(id1[i],NULL);
pthread_join(id2[i],NULL);
}
exit(0);
}
执行结果:
实验总结
哲学家进餐的问题是操作系统信号量同步的经典例题了。
这次我通过解决哲学家进餐的哲学问题从而对进程同步有一个更好的理解,解决这个问题书中给出了三种解决方法。
我在实验中也是用这三种方法去定义信号量解决死锁问题。
通过信号量的获取与wait操作去控制进餐,a方法是控制哲学家左右手都有筷子时才能进餐,b中那么是通过互斥信号量的获取,假设没有信号量便不能执行,而且只有四个哲学家能同时进餐也防止了死锁的出现。
c中是让奇数的哲学家先拿左筷子执行wait和signal操作,偶数号的虽然也执行该操作但是只能拿右筷子。