山东大学操作系统实验报告4进程同步实验.docx
《山东大学操作系统实验报告4进程同步实验.docx》由会员分享,可在线阅读,更多相关《山东大学操作系统实验报告4进程同步实验.docx(13页珍藏版)》请在冰点文库上搜索。
山东大学操作系统实验报告4进程同步实验
山东大学操作系统实验报告4进程同步实验
计算机科学与技术学院实验报告
实验题目:
实验四、进程同步实验
学号:
日期:
20120409
班级:
计基地12
姓名:
实验目的:
加深对并发协作进程同步与互斥概念的理解,观察和体验并发进程同步与互斥
操作的效果,分析与研究经典进程同步与互斥问题的实际解决方案。
了解Linux系统中IPC进程同步工具的用法,练习并发协作进程的同步与互斥操作的编程与调试技术。
实验内容:
抽烟者问题。
假设一个系统中有三个抽烟者进程,每个抽烟者不断地卷烟并抽烟。
抽烟者卷起并抽掉一颗烟需要有三种材料:
烟草、纸和胶水。
一个抽烟者有烟草,一个有纸,另一个有胶水。
系统中还有两个供应者进程,它们无限地供应所有三种材料,但每次仅轮流提供三种材料中的两种。
得到缺失的两种材料的抽烟者在卷起并抽掉一颗烟后会发信号通知供应者,让它继续提供另外的两种材料。
这一过程重复进行。
请用以上介绍的IPC同步机制编程,实现该问题要求的功能。
硬件环境:
处理器:
Intel®Core™i3-2350MCPU@2.30GHz×4
图形:
Intel®SandybridgeMobilex86/MMX/SSE2
内存:
4G
操作系统:
32位
磁盘:
20.1GB
软件环境:
ubuntu13.04
实验步骤:
(1)新建定义了producer和consumer共用的IPC函数原型和变量的ipc.h文件。
(2)新建ipc.c文件,编写producer和consumer共用的IPC的具体相应函数。
(3)新建Producer文件,首先定义producer的一些行为,利用系统调用,建立共享内存区域,设定其长度并获取共享内存的首地址。
然后设定生产者互斥与同步的信号灯,并为他们设置相应的初值。
当有生产者进程在运行而其他生产者请求时,相应的信号灯就会阻止他,当共享内存区域已满时,信号等也会提示生产者不能再往共享内存中放入内容。
(4) 新建Consumer文件,定义consumer的一些行为,利用系统调用来创建共享内存区域,并设定他的长度并获取共享内存的首地址。
然后设定消费者互斥与同步的信号灯,并为他们设置相应的初值。
当有消费进程在运行而其他消费者请求时,相应的信号灯就会阻止它,当共享内存区域已空时,信号等也会提示生产者不能再从共享内存中取出相应的内容。
运行的消费者应该与相应的生产者对应起来,只有这样运行结果才会正确。
结论分析与体会:
实现方式:
Consumer:
#include"ipc.h"
intmain(intargc,char*argv[]){
intrate=3;
intconsumerid=atoi(argv[1]);
buff_h=101;
buff_number=1;
cget_h=103;
cget_number=1;
shm_flg=IPC_CREAT|0644;
buff_ptr=(char*)set_shm(buff_h,buff_number,shm_flg);
cget_ptr=(int*)set_shm(cget_h,cget_number,shm_flg);
prod_h=201;
pmtx_h=202;
cons_h=301;
cmtx_h=302;
sem_flg=IPC_CREAT|0644;
sem_val=buff_number;
prod_sem=set_sem(prod_h,sem_val,sem_flg);
sem_val=0;
cons_sem=set_sem(cons_h,sem_val,sem_flg);
sem_val=1;
cmtx_sem=set_sem(cmtx_h,sem_val,sem_flg);
if(consumerid==0)
*cget_ptr=0;
while
(1){
if(buff_ptr[0]-'A'==consumerid){
down(cons_sem);
down(cmtx_sem);
sleep(rate);
if(buff_ptr[0]=='A'){
printf("%dTheconsumerhasglue.\nTheconsumergetstobaccoandpaper\n",getpid());
}
if(buff_ptr[0]=='B'){
printf("%dTheconsumerhaspaper.\nTheconsumergetstobaccoandglue\n",getpid());
}
if(buff_ptr[0]=='C'){
printf("%dTheconsumerhastobacco.\nTheconsumergetsglueandpaper\n",getpid());
}
*cget_ptr=(*cget_ptr+1);
if(*cget_ptr%2==0)
buff_ptr[0]='D';
else
buff_ptr[0]='E';
up(cmtx_sem);
up(prod_sem);
}
}
returnEXIT_SUCCESS;
}
Producer:
#include"ipc.h"
intmain(intargc,char*argv[]){
intrate=3;
intproducerid=atoi(argv[1]);
buff_h=101;
buff_number=1;
pput_h=102;
pput_number=1;
shm_flg=IPC_CREAT|0644;
buff_ptr=(char*)set_shm(buff_h,buff_number,shm_flg);
pput_ptr=(int*)set_shm(pput_h,pput_number,shm_flg);
prod_h=201;
pmtx_h=202;
cons_h=301;
cmtx_h=302;
sem_flg=IPC_CREAT|0644;
sem_val=buff_number;
prod_sem=set_sem(prod_h,sem_val,sem_flg);
sem_val=0;
cons_sem=set_sem(cons_h,sem_val,sem_flg);
sem_val=1;
pmtx_sem=set_sem(pmtx_h,sem_val,sem_flg);
if(producerid==0){
buff_ptr[0]='D';
*pput_ptr=0;
}
while
(1){
if(buff_ptr[0]-'D'==producerid){
down(prod_sem);
down(pmtx_sem);
*pput_ptr=(*pput_ptr+1)%3;
if(*pput_ptr==0){
buff_ptr[0]='A';
printf("%dTheproducergivestobaccoandpaper\n",getpid());
}
if(*pput_ptr==1){
buff_ptr[0]='B';
printf("%dTheproducergivestobaccoandglue\n",getpid());
}
if(*pput_ptr==2){
buff_ptr[0]='C';
printf("%dTheproducergivesglueandpaper\n",getpid());
}
sleep(rate);
up(pmtx_sem);
up(cons_sem);
}
}
returnEXIT_SUCCESS;
}
Ipc.h:
#include"ipc.h"
intget_ipc_id(char*proc_file,h_th){
FILE*pf;
intm,n;
charline[BUFSZ],colum[BUFSZ];
if((pf=fopen(proc_file,"r"))==NULL){
perror("Procfilenotopen");
exit(EXIT_FAILURE);
}
fgets(line,BUFSZ,pf);
while(!
feof(pf)){
m=n=0;
fgets(line,BUFSZ,pf);
while(line[m]=='')
m++;
while(line[m]!
='')
colum[n++]=line[m++];
colum[n]='\0';
if(atoi(colum)!
=h)
continue;
n=0;
while(line[m]=='')
m++;
while(line[m]!
='')
colum[n++]=line[m++];
colum[n]='\0';
m=atoi(colum);
fclose(pf);
returnm;
}
fclose(pf);
return-1;
}
intdown(intsem_id){
structsembufbuf;
buf.sem_op=-1;
buf.sem_number=0;
buf.sem_flg=SEM_UNDO;
if((semop(sem_id,&buf,1))<0){
perror("downerror");
exit(EXIT_FAILURE);
}
returnEXIT_SUCCESS;
}
intup(intsem_id){
structsembufbuf;
buf.sem_op=1;
buf.sem_number=0;
buf.sem_flg=SEM_UNDO;
if((semop(sem_id,&buf,1))<0){
perror("uperror");
exit(EXIT_FAILURE);
}
returnEXIT_SUCCESS;
}
intset_sem(h_tsem_h,intsem_val,intsem_flg){
intsem_id;
Sem_unssem_arg;
if((sem_id=get_ipc_id("/proc/sysvipc/sem",sem_h))<0){
if((sem_id=semget(sem_h,1,sem_flg))<0){
perror("semaphorecreateerror");
exit(EXIT_FAILURE);
}
sem_arg.val=sem_val;
if(semctl(sem_id,0,SETVAL,sem_arg)<0){
perror("semaphoreseterror");
exit(EXIT_FAILURE);
}
}
returnsem_id;
}
char*set_shm(h_tshm_h,intshm_number,intshm_flg){
intm,shm_id;
char*shm_buf;
if((shm_id=get_ipc_id("/proc/sysvipc/shm",shm_h))<0){
if((shm_id=shmget(shm_h,shm_number,shm_flg))<0){
perror("shareMemoryseterror");
exit(EXIT_FAILURE);
}
if((shm_buf=(char*)shmat(shm_id,0,0))<(char*)0){
perror("getshareMemoryerror");
exit(EXIT_FAILURE);
}
for(m=0;mshm_buf[m]=0;
}
if((shm_buf=(char*)shmat(shm_id,0,0))<(char*)0){
perror("getshareMemoryerror");
exit(EXIT_FAILURE);
}
returnshm_buf;
}
intset_msq(h_tmsq_h,intmsq_flg){
intmsq_id;
if((msq_id=get_ipc_id("/proc/sysvipc/msg",msq_h))<0){
if((msq_id=msgget(msq_h,msq_flg))<0){
perror("messageQueueseterror");
exit(EXIT_FAILURE);
}
}
returnmsq_id;
}
实验结果:
分析:
多进程的系统中避免不了进程间的相互关系。
进程互斥是进程之间发生的一种间接性作用,一般是程序不希望的。
通常的情况是两个或两个以上的进程需要同时访问某个共享变量。
我们一般将发生能够问共享变量的程序段称为临界区。
两个进程不能同时进入临界区,否则就会导致数据的不一致,产生与时间有关的错误。
解决互斥问题应该满足互斥和公平两个原则,即任意时刻只能允许一个进程处于同一共享变量的临界区,而且不能让任一进程无限期地等待。
进程同步是进程之间直接的相互作用,是合作进程间有意识的行为,典型的例子是公共汽车上司机与售票员的合作。
只有当售票员关门之后司机才能启动车辆,只有司机停车之后售票员才能开车门。
司机和售票员的行动需要一定的协调。
同样地,两个进程之间有时也有这样的依赖关系,因此我们也要有一定的同步机制保证它们的执行次序。
信号量机制就是其中的一种。
信号灯机制即利用pv操作来对信号量进行处理。
PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下:
P(S):
①将信号量S的值减1,即S=S-1;
②如果S³0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。
V(S):
①将信号量S的值加1,即S=S+1;
②如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。
PV操作的意义:
我们用信号量及PV操作来实现进程的同步和互斥。
信号量的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。
信号量的值与相应资源的使用情况有关。
当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。
信号量的值仅能由PV操作来改变。
一般来说,信号量S30时,S表示可用资源的数量。
执行一次P操作意味着请求分配一个单位资源,因此S的值减1;当S<0时,表示已经没有可用资源,请求者必须等待别的进程释放该类资源,它才能运行下去。
而执行一个V操作意味着释放一个单位资源,因此S的值加1;若S£0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。
使用多于4个的生产者和消费者,以各种不同的启动顺序、不同的执行速率检测以上示例程序和独立实验程序也能满足同步的要求。
因为使用信号量满足进程互斥的要求,任意时刻进入临界区的进程只有一个。
而进程是通过信号量唤醒阻塞进程,依然可以实现进程同步。
调试过程中遇到的主要问题及解决过程:
(1)在修改程序时,最开始使用了read()、schedual()、lock(),但是编译出现错误。
解决方法:
使用sleep()函数。
(2)exit()缺少头文件。
解决方法:
加头文件#include。
(3)sleep()等linux系统调用缺少头文件。
解决方法:
加头文件#include
体会和收获:
经过本次实验,初步了解操作系统的进程同步的过程。
我对生产者-消费者问题的解决办法有了更全面的认识,同时对调试代码更加熟练。
本次实验最大的体会就是,做东西要细心,在写代码的过程中,稍不留意就给后期调试工作带来很多问题。