福州大学操作系统实验报告-2015年版Word文件下载.docx
《福州大学操作系统实验报告-2015年版Word文件下载.docx》由会员分享,可在线阅读,更多相关《福州大学操作系统实验报告-2015年版Word文件下载.docx(27页珍藏版)》请在冰点文库上搜索。
四、关键代码:
五、实验代码分析 2
六、调试过程 2
七、实验结果:
八、实验结果分析:
26
计算机操作系统实验一
并发程序设计
掌握在程序中创建新进程的方法, 观察并理解多道程序并发执行的现象。
fork():
建立子进程。
子进程得到父进程地址空间的一个复
制。
返回值:
成功时,该函数被调用一次,但返回两次,fork()对子进程返回0,对父进程返回子进程标识符(非0值)。
不成功时对父进程返回-1,没有子进程。
35
5
出现
#include<
stdio.h>
#include<
unistd.h>
voidmain(void)
{
int x=5;
/*fork调用,创建了一个新的进程,这个进程共享父进程的数据和堆栈空间等,这之后的代码指令为子进程创建了一个拷贝。
fock调用是一个复制进程,fock调用后,新进程的入口就在fock的下一条语句。
*/
if(fork())//fork()返回给子进程0,给父进程为子进程的pid
x+=30;
//父进程执行到,x=35
printf("
%d\n"
x);
//输出35
}
else
//子进程执行到,输出5
//父进程,子进程都输出,不过输出的是自己空间里的x,父进程35,子进程5
Linux下一个进程在内存里有三部分的数据,就是"
代码段"
、"
堆栈段"
和"
数据段"
。
"
,顾名思义,就是存放了程序代码的数据,假如机器中有数个进程运行相同的一个程序,那么它们就可以使用相同的代码段。
存放的就是子程序的返回地址、子程序的参数以及程序的局部变量。
而数据段则存放程序的全局变量,常数以及动态数据分配的数据空间(比如用malloc之类的函数取得的空间)。
系统如果同时运行数个相同的程序,它们之间就不能使用同一个堆栈段和数据段。
仔细分析后,我们就可以知道:
一个程序一旦调用fork函数,系统就为一个新的进程准备了前述三个段,首先,系统让新的进程与旧的进程使用同一个代码段,因为它们的程序还是相同的,对于数据段和堆栈段,系统则复制一份给新的进程,这样,父进程的所有数据都可以留给子进程,但是,子进程一旦开始运行,虽然它继承了父进程的一切数据,但实际上数据却已经分开,相互之间不再有影响了,也就是说,它们之间不再共享任何数据了。
fork()不仅创建出与父进程代码相同的子进程,而且父进程在fork执行点的所有上下文场景也被自动复制到子进程中,包括:
——全局和局部变量
——打开的文件句柄
——共享内存、消息等同步对象
由进程见并行执行,故父子进程间的输出是乱序的。
在经过上百次的实验下,并没有发现全部的预计结果。
五、实验结果分析
现在操作系统在设计时大多都应用了轮转调度算法,也就是说,进程按分配到是时间片执行,当一个进程执行的时间片到了,即使该进程还未执行完就要被挂起,去执行另一个进程。
在父进程或子进程执行当中,如果时间片到,则会中断,去执行另一个进程,造成输出结果的交叉。
因此就出现了父子进程结果的交叉。
在linux系统中,fork函数创建子进程所需的资源一般是足够的,因此对于子进程创建不成功的情况基本上是不会出现的,而且由于本题的程序耗时很短,因此时间片对于每一个进程也是绝对够用的,因此父子进程结果交叉出现的可能性是比较小的。
计算机操作系统实验二
进程通信(实验2)
掌握用邮箱方式进行进程通信的方法,并通过设计实现简
单邮箱理解进程通信中的同步问题以及解决该问题的方法。
邮箱机制类似于日常使用的信箱。
对于用户而言使用起来
比较方便,用户只需使用send()向对方邮箱发邮件receive()从自己邮箱取邮件,send()和receive()的内部操作用户无需关心。
因为邮箱在内存中实现,其空间有大小限制。
其实send()和receive()的内部实现主要还是要解决生产者与消费者问题。
receive()
Send()
信箱B
信箱A
进程B
进程A
【实验内容】:
进程通信的邮箱方式由操作系统提供形如send()和
receive()的系统调用来支持,本实验要求学生首先查找资料了解所选用操作系统平台上用于进程通信的系统调用具体形式,然后使用该系统调用编写程序进行进程间的通信,要求程序运行结果可以直观地体现在界面上。
在此基础上查找所选用操作系统平台上支持信号量机制的系统调用具体形式,运用生产者与消费者模型设计实现一个简单的信箱,该信箱需要有创建、发信、收信、撤销等函数,至少能够支持两个进程互相交换信息,比较自己实现的信箱与操作系统本身提供的信箱,分析两者之间存在的异同。
1、信号量机制
信号量主要用来控制多个进程对共享资源的访问。
当一个进程要访问某个共享资源时,按下列步骤进行:
(1)检测控制这个资源的信号量的值。
(2)如果信号量的值时正数,就可以使用这个资源。
进程将信号量的值减一,表示它正在使用资源的某个小单元。
(3)如果信号量的值为零,那么这个进程进入睡眠状态,直到信号量的值重新大于零时被唤醒,转入第一步操作。
2、共享内存机制
共享内存是允许多个进程共享一块内存,由此来达到交换信息的进程通信机制。
共享内存机制是最快的一种进程通信机制。
共享内存需要一定的同步机制控制多个进程对同一块内存的读写。
当一个进程在写数据时,不允许其他的进程写数据或读数据,这可以通过信号量控制实现。
二、设计方案
Send
共享内存
Receive
综合信号量和共享内存机制,用信号量来控制多个进程对共享内存的读写操作,一次只能读写一次。
信号量S1=1,S2=0;
Send Receive
A:
<
a1>
B:
<
b1>
P(S1) P(S2)
发送数据或删除数据 读取数据
V(S2) V(S1)
GOTO A GOTO B
三、实验代码
1、系统调用实现邮箱通信
程序代码A:
string.h>
sys/types.h>
sys/ipc.h>
sys/msg.h>
stdlib.h>
#defineMSGKEY1 100#defineMSGKEY2200typedefstructmsgform
long mtype;
char mtxt[100];
}msg;
//消息结构
intmain(intagrc,char**argv)
msgamsg;
intpid,*pint,msgqid1,msgqid2;
intcmd,msglen,msgtype;
//创建消息队列1用于发送消息给B,或者获得qidmsgqid1=msgget(MSGKEY1,IPC_CREAT|0666);
if(msgqid1==-1)
error\n"
);
return0;
//创建消息队列2用于接收B消息,或者获得qidmsgqid2=msgget(MSGKEY2,IPC_CREAT|0666);
if(msgqid2==-1)
return0;
while
(1)
//输入操作命令1代表发送消息,2代表接收消息printf("
pleaseinputcmd(send=1,receive=2)\n"
scanf("
%d"
&
cmd);
if(cmd==1)//发送消息
pleaseinputmessagetype:
\n"
scanf("
amsg.mtype);
//输入消息类型getchar();
pleaseinputmessage:
%s"
amsg.mtxt);
//输入消息msglen=strlen(amsg.mtxt);
msgsnd(msgqid1,&
amsg,msglen,0);
//发送消息
if(cmd==2)//接收消息
memset(&
amsg,0,sizeof(msg));
printf("
msgtype);
//输入消息类型if(msgrcv(msgqid2,&
amsg,100,msgtype,IPC_NOWAIT)==-1)//接收消息
printf("
msgrcvfailed!
ReceiveamessagefromB:
\ntype:
%d\n%s\n"
msgtype,amsg.mtxt);
if(cmd==3)//结束通信,销毁消息队列
msgctl(msgqid1,IPC_RMID,0);
msgctl(msgqid2,IPC_RMID,0);
break;
程序代码B:
intmain(intargc,char**argv)
msgbmsg;
//创建消息队列1用于接收消息,或者获得qid
msgqid1=msgget(MSGKEY1,IPC_CREAT|0666);
error"
//创建消息队列2用于发送消息给A,或者获得qidmsgqid2=msgget(MSGKEY2,IPC_CREAT|0666);
//输入命令,1代表发送消息,2代表接收printf("
if(cmd==1)//发送消息
bmsg.mtype);
//输入消息类型printf("
bmsg.mtxt);
//输入消息msglen=strlen(bmsg.mtxt);
msgsnd(msgqid2,&
bmsg,msglen,0);
if(cmd==2)//接收消息
bmsg,0,sizeof(msg));
//输入接收的消息类型if(msgrcv(msgqid1,&
bmsg,100,msgtype,IPC_NOWAIT)==-1)
ReceivethemessagefromA:
msgtype,bmsg.mtxt);
if(cmd==3)//命令3销毁队列
2、验证系统调用消息队列实现的邮箱通信
A:
发送消息
B:
接收消息
尝试接收已接收的消息失败:
连续发送消息
连续接收消息(逆序接收)
删除消息队列-结果:
3、自己实现邮箱通信:
文件a.c,定义信号量调用结构及信号量操作函数的系统调用形式:
sys/sem.h>
sys/shm.h>
#defineshmsz256
unionsemun//信号量固定结构
intval;
structsemid_ds*buf;
unsignedshort*array;
};
voidinit_a_semaphore(intsid,intsemnum,intinitval) //信号量初始化函数
unionsemunsemopts;
semopts.val=initval;
semctl(sid,semnum,SETVAL,semopts);
//信号量操作函数
intsemaphore_p(intsem_id)//p操作的封装
structsembufsb;
sb.sem_num=0;
sb.sem_op=-1;
sb.sem_flg=SEM_UNDO;
if(semop(sem_id,&
sb,1)==-1)
semaphore_pfailed.\n"
return1;
intsemaphore_v(intsem_id)//v操作的封装
sb.sem_op=1;
if(semop(sem_id,&
semaphore_vfailed.\n"
文件r.c:
读取消息的程序
#include"
a.c"
intmain()
char*shm;
intshmid;
intproducer,consumer,i;
if((consumer=semget((key_t)1234,1,IPC_CREAT|0660))==-1)//创建或者获取信号量id
semgetfailed.\n"
exit
(1);
init_a_semaphore(consumer,0,1);
//初始信号量if((producer=semget((key_t)2345,1,IPC_CREAT|0660))==-1)
init_a_semaphore(producer,0,0);
if((shmid=shmget(1,shmsz,0660|IPC_CREAT))==-1)//创建或者获取共享内存id
shmgetfailed.\n"
shm=(char*)shmat(shmid,0,0);
//将共享内存映射到自己进程空间
for(i=0;
;
i++)
PleaseselectR(read)orE(end)\n"
chars=getchar();
getchar();
if(s=='
R'
)
semaphore_p(producer);
//p操作,让其他进程无法操作给内存
Datareceiverd:
%s\n"
shm);
//读取消息sleep
(1);
semaphore_v(consumer);
//v操作,释放给内存操作权限
elseif(s=='
E'
)break;
semctl(producer,0,IPC_RMID,0);
semctl(consumer,0,IPC_RMID,0);
文件w:
发送消息到共享内存
char*shm,*s;
charreadbuf[shmsz];
if((consumer=semget((key_t)1234,1,IPC_CREAT|0660))==-1)/创建或者获取信号量id
if((producer=semget((key_t)2345,1,IPC_CREAT|0660))==-1)
//映射到自己进程空间for(i=0;
chars;
PleaseSelectD(delet)orS(send)orE(end)\n"
s=getchar();
if(s=='
S'
)//发送消息
Entertext:
fgets(readbuf,shmsz,stdin);
semaphore_p(consumer);
sprintf(shm,"
Message%4dfromproducer%dis\"
%s\"
i,getpid(),readbuf);
semaphore_v(producer);
)break;
D'
semaphore_p(consumer);
sprintf(shm,"
NOmessage"
1、信号量实现:
创建或打开信号量集的系统函数原型:
intsemget(key_tkey,intnsems,intflag)
key相当于是一个信号量集的标识符,nsems是信号量集中创建的信号量的数量,flag设置为IPC_CREAT,执行创建或打开操作。
调用成功返回信号量集合标识符,否则返回-1。
2、信号量操作:
intsemop(intsemid,structsembuf*sops,size_tnops)
semid为信号集的标识符;
sops为sembuf结构的数组,nops为数组中元素的个数。
sembuf结构中记录了对信号集的一个操作。
P/V原语的操作主要是用到这个函数。
3、信号量控制:
intsemctl(intsemid,intsemnum,intcmd,[unionsemnunarg])
semid为信号量集合的标识;
semnum信号集中某个信号的编号;
cmd给出操作命令,可取值如SETVAL(设置信号量的值),IPC_RMID(删除指定的信号量集合)。
4、共享内存的创建与打开
intshmget(key_tkey,intsize,intflag)
函数shmget除可用于创建一个新的共享内存外,也可用于打开一个已存在的共享内存。
参数key表示所创建或打开的共享内存的关键字,参数size表示共享内存的大小。
参数flag表示调用函数的操作类型,也可用于设置共享内存的访问权限。
5、共享内存的操作
当一个共享内存创建或打开后,某个进程如果要使用它则必须将此共享内存区域附加到它的地址空间。
void*shmat(intshmid,void*addr,intflag);
参数shimid表示要附加的共享内存段的引用标识符。
参数flag用于表示
shmat函数的操作方式。
如果flag设置了SHM_RDONLY位,该内存区域被设置为只读,否则设置为可读写。
参数addr和flag共同决定共享内存区域要附加到的地址值。
一般addr取0,系统自动查找进程地址空间。
①(最后一次读操作被挂起,等待数据)发送方:
接收方:
②(写操作被挂起,等待接收进程读数据)发送方:
运行发送进程和接收进程,发送方在一个循环中运行,如果选择发送数据,则它首先强制对信号量