山东大学操作系统实验5进程互斥实验Word文档下载推荐.docx
《山东大学操作系统实验5进程互斥实验Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《山东大学操作系统实验5进程互斥实验Word文档下载推荐.docx(18页珍藏版)》请在冰豆网上搜索。
ubuntu11.02
gcc4.8
实验步骤:
1、了解实验目的,掌握与进程间通信IPC中的3个对象:
共享内存、信号灯数组、消息队列及有关的系统调用,并熟练掌握。
2、阅读实验题目并分析实验的需求。
实验题目:
理发店问题
(1)首先创建ipc.h文件,在里面定义生产者/消费者共用的IPC函数的原型和变量。
(2)然后创建ipc.c文件,在里面定义生产者/消费者共用的IPC的具体的相应函数。
(3)声明两个消息队列,当沙发空闲时则会将空闲的消息放入相应的队列中,让顾客可以进入沙发中,当理发师空闲时,也会将相应的消息放入队列中,从而可以让顾客到理发椅上进行理发。
(4)在消息队列中实现,当等待室里的人数少于十三个时,若从消息队列中获得信息,则顾客可以进入,当沙发上有空位时,在等待室的人可以进入沙发等待。
(5)创建customer文件cons,里面声明两个消息队列,当获得允许进入的信息时,顾客进入等待室,当顾客理完发则会离开,同时向消息队列中发送信息。
(6)创建barbour文件bar,在里面声明三个队列,当理发师就绪则会向队列中发送消息,若顾客收到消息就会去理发,当顾客离开时,也会向队列中发送一条消息。
同时理发师理发与收账是互斥的。
通过上述的文件就可以实现相应的功能。
3、分析清楚实验要求,便开始用编程语言进行编程,将分析的思路用编程语言实现。
4、完成编写程序,要进行运行调试,找到编写中的错误,进行进一步的修改,直到程序运行过程中不再出现错误。
5、完成程序的调试与修改,保存程序,对编程的过程进行总结,找到编程中的不足,并完成实验报告。
实现方式:
编写以下文件直接在命令行
Makefile:
hdrs=ipc.h
opts=-g-c
c_src=cons.cipc.c
c_obj=cons.oipc.o
p_src=bar.cipc.c
p_obj=bar.oipc.o
all:
producerconsumer
consumer:
$(c_obj)
gcc$(c_obj)-oconsumer
cons.o:
$(c_src)$(hdrs)
gcc$(opts)$(c_src)
producer:
$(p_obj)
gcc$(p_obj)-oproducer
bar.o:
$(p_src)$(hdrs)
gcc$(opts)$(p_src)
clean:
rmconsbar*.o
ipc.h:
#include<
stdio.h>
stdlib.h>
sys/types.h>
sys/ipc.h>
sys/shm.h>
sys/sem.h>
sys/msg.h>
#defineBUFSZ256
intget_ipc_id(char*proc_file,key_tkey);
char*set_shm(key_tshm_key,intshm_num,intshm_flag);
intset_msq(key_tmsq_key,intmsq_flag);
intset_sem(key_tsem_key,intsem_val,intsem_flag);
intdown(intsem_id);
intup(intsem_id);
/*
信号灯控制用的共同体
*/
typedefunionsemuns{
intval;
}Sem_uns;
typedefstructmsgbuf{
longmtype;
charmtext[1];
}Msg_buf;
key_tbuff_key[2];
intbuff_num;
int*buff_ptr[2];
key_tpput_key[2];
intpput_num;
int*pput_ptr[2];
key_tprod_key;
key_tpmtx_key;
intprod_sem;
intpmtx_sem;
key_tcons_key;
key_tcmtx_key;
intcons_sem;
intcmtx_sem;
intsem_val;
intsem_flg;
intshm_flg;
ipc.c:
#include"
ipc.h"
intget_ipc_id(char*proc_file,key_tkey){
FILE*pf;
inti,j;
charline[BUFSZ],colum[BUFSZ];
if((pf=fopen(proc_file,"
r"
))==NULL){
perror("
Procfilenotopen"
);
exit(EXIT_FAILURE);
}
fgets(line,BUFSZ,pf);
while(!
feof(pf)){
i=j=0;
fgets(line,BUFSZ,pf);
while(line[i]=='
'
)
i++;
while(line[i]!
='
)
colum[j++]=line[i++];
colum[j]='
\0'
;
if(atoi(colum)!
=key)
continue;
j=0;
i=atoi(colum);
fclose(pf);
returni;
fclose(pf);
return-1;
}
/*
*信号灯上的down/up操作
*semid:
信号灯数组标识符
*semnum:
信号灯数组下标
*buf:
操作信号灯的结构
intdown(intsem_id){
structsembufbuf;
buf.sem_op=-1;
buf.sem_num=0;
buf.sem_flg=SEM_UNDO;
if((semop(sem_id,&
buf,1))<
0){
perror("
downerror"
returnEXIT_SUCCESS;
intup(intsem_id){
buf.sem_op=1;
uperror"
*set_sem
函数建立一个具有
n
个信号灯的信号量
*
如果建立成功,返回
一个信号灯数组的标识符
sem_id
输入参数:
*sem_key
信号灯数组的键值
*sem_val
信号灯数组中信号灯的个数
*sem_flag
信号等数组的存取权限
intset_sem(key_tsem_key,intsem_val,intsem_flg){
intsem_id;
Sem_unssem_arg;
if((sem_id=get_ipc_id("
/proc/sysvipc/sem"
sem_key))<
0){
if((sem_id=semget(sem_key,1,sem_flg))<
0){
perror("
semaphorecreateerror"
exit(EXIT_FAILURE);
}
//设置信号灯的初值
sem_arg.val=sem_val;
if(semctl(sem_id,0,SETVAL,sem_arg)<
0){
semaphoreseterror"
}
returnsem_id;
*set_shm
个字节
的共享内存区
如
果建立成功,返回
一个指向该内存区首地址的指针
shm_buf
*shm_key
共享内存的键值
*shm_val
共享内存字节的长度
*shm_flag
共享内存的存取权限
char*set_shm(key_tshm_key,intshm_num,intshm_flg){
inti,shm_id;
char*shm_buf;
if((shm_id=get_ipc_id("
/proc/sysvipc/shm"
shm_key))<
if((shm_id=shmget(shm_key,shm_num,shm_flg))<
0){
shareMemoryseterror"
if((shm_buf=(char*)shmat(shm_id,0,0))<
(char*)0){
getshareMemoryerror"
for(i=0;
i<
shm_num;
i++)
shm_buf[i]=0;
}
if((shm_buf=(char*)shmat(shm_id,0,0))<
(char*)0){
returnshm_buf;
intset_msq(key_tmsq_key,intmsq_flg){
intmsq_id;
if((msq_id=get_ipc_id("
/proc/sysvipc/msg"
msq_key))<
if((msq_id=msgget(msq_key,msq_flg))<
0){
messageQueueseterror"
returnmsq_id;
Cons:
intmain(intargc,char*argv[]){
sem_val=1;
sem_val=0;
buff_num=13;
pput_num=4;
sem_val=buff_num;
buff_key[0]=104;
pput_key[0]=105;
buff_key[1]=106;
pput_key[1]=107;
prod_key=201;
pmtx_key=202;
cons_key=301;
cmtx_key=302;
shm_flg=IPC_CREAT|0644;
sem_flg=IPC_CREAT|0644;
prod_sem=set_sem(prod_key,sem_val,sem_flg);
cons_sem=set_sem(cons_key,sem_val,sem_flg);
cmtx_sem=set_sem(cmtx_key,sem_val,sem_flg);
buff_ptr[0]=(int*)set_shm(buff_key[0],buff_num,shm_flg);
pput_ptr[0]=(int*)set_shm(pput_key[0],pput_num,shm_flg);
buff_ptr[1]=(int*)set_shm(buff_key[1],buff_num,shm_flg);
pput_ptr[1]=(int*)set_shm(pput_key[1],pput_num,shm_flg);
down(cons_sem);
down(cmtx_sem);
sleep(3);
if(*pput_ptr[1]<
4){
buff_ptr[1][*pput_ptr[1]]=getpid();
*pput_ptr[1]=*pput_ptr[1]+1;
printf("
%dpeoplecometothebarbershop\n"
getpid());
sleep(3);
%dpeoplesitonthesofa\n"
else{
if(*pput_ptr[0]<
13){
buff_ptr[0][*pput_ptr[0]]=getpid();
*pput_ptr[0]=*pput_ptr[0]+1;
printf("
%dpeoplecometotheshop\n"
else{
theshopisfullthepeopleleave\n"
up(cmtx_sem);
up(prod_sem);
return1;
}
Bar:
intmain(intargc,char*argv[]){
intn=0;
if(argv[1]!
=NULL)
n=atoi(argv[1]);
intrate=1;
sem_flg=IPC_CREAT|0644;
pmtx_sem=set_sem(pmtx_key,sem_val,sem_flg);
while
(1){
down(prod_sem);
down(pmtx_sem);
intpid;
inti;
if(*pput_ptr[1]>
0){
rate=0;
pid=buff_ptr[1][0];
------------------------------------\n"
barber%dwaswakedupandstartworkingon%dpeople\n"
getpid(),pid);
for(i=0;
i<
*pput_ptr[1]-1;
i++){
buff_ptr[1][i]=buff_ptr[1][i+1];
}
if(*pput_ptr[0]>
buff_ptr[1][*pput_ptr[1]-1]=buff_ptr[0][0];
printf("
people%dsitsonthesofa\n"
buff_ptr[0][0]);
for(i=0;
*pput_ptr[0]-1;
buff_ptr[0][i]=buff_ptr[0][i+1];
}
*pput_ptr[0]=*pput_ptr[0]-1;
else{
*pput_ptr[1]=*pput_ptr[1]-1;
people%dfinishsandpaytothebarber%d\n"
pid,getpid());
people%dfinishspayingandleavetheshop\n"
pid);
barber%dissleeping\n"
if(rate!
=1){
rate=1;
barber%dstartssleeping\n"
up(pmtx_sem);
up(cons_sem);
实验结果:
理发师1
理发师2
理发师3
顾客进程1
顾客进程2
总结:
通过进程同步实验,我明白了进程互斥机制,
(1)非对称性互斥是指并发进程有且仅有一个可以执行某一段代码(称为临界区),具体是那个进程进入临界区是有临界区之前的代码(称为进入区)决定的,当进入临界区的调度机制是非对称,即某些进程具有优先进入的权利称为非对称的,例如此时有一个理发师从沙发室叫走了一个顾客,需要一个顾客从等候室进入沙发室,此时恰巧新来了以顾客,此时顾客进程和理发师进程都想要进入临界区(修改或查询等候室顾客数目),但按照正常人的思维,肯定是理发师先访问临界区,修改等候室顾客数目,然后是新来顾客访问临界区,查询是否可以进入理发店。
(2)进程饥饿指当等待时间给进程推进和响应带来明显影响称为进程饥饿。
产生饥饿的主要原因是:
在一个动态系统中,对于每类系统资源,操作系统需要确定一个分配策略,当多个进程同时申请某类资源时,由分配策略确定资源分配给进程的次序。
有时资源分配策略可能是不公平的,即不能保证等待时间上界的存在。
在这种情况下,即使系统没有发生死锁,某些进程也可能会长时间等待.当等待时间给进程推进和响应带来明显影响时,称发生了进程饥饿,当饥饿到一定程度的进程所赋予的任务即使完成也不再具有实际意义时称该进程被饿死。
举个例子,当有多个进程需要打印文件时,如果系统分配打印机的策略是最短文件优先,那么长文件的打印任务将由于短文件的源源不断到来而被无限期推迟,导致最终的饥饿甚至饿死。
(3)本实验的饥饿现象可能发生的情况事当顾客数目较少的情况下某一理发师进程可能一直被阻塞,即其余理发师持续服务;
还有一种情况就是由于调度不合理,理完发的顾客会以为没有理发师收钱一直阻塞在交钱收费队列上。
(4)解决进程饥饿现象的唯一方法是修改进入临界区进程选择机制,打破不公平调度机制,使得想进入临界区的进程可以在有限等待中进入临界区。
(5)linux系统中进程可以通过共享内存和消息传递机制通信,通过消息传递队列,具体实现是发送进程发送信息给内核,然后内核实现了转发功能,接受进程从内核那里接收到发送进程所发送的信息。
适合传递少量信息,不适合传递大量信息,大量信息适合用共享内存机制传递。
收获和体会:
1.实验过程中在编写代码的时候要仔细认真,定义变量的时候要看明白变量的作用范围。
2.
实验时实验的步骤要记清楚,这样在执行实验的时候不会出差错
3.
要善于将之前学过的知识用在后面的学习中,比如进程的创建这里就可以用到
4.
学会分析题意,转