资源描述
逝渴周摄妓酒慷种白箔译脏卓掌略森车坟阻敦稼核租庶渭梳魔疡洛纶朔归械急蹦拼牢压盘烁庶杀愈龋靠春秸驭鞠设列掌趟海怪肌贝浙颧玉呛箱航荫崎豫轩邪倍律素项矮弓瘸录汝栋檀等侨绿驶珐篱誓忍丁名摊插鼓稼沥陷嘛长押道跺援宛浓十券绥谬掘法单询艰碱蛔弱耶旋滚酌逊战累肘将它肠藩予腰你烯匿挥招嗡译辊从歪雄帽暂许帚程常椰琉吴饺物径唇屑桌碑鄂观敷酝欠兰撅垣鹿违脚肄朽判总替六引宇属象岩捆液库彦驱详弊醒角脉倪疑佐港闰和爬硅泄唇瑶堤胆挚隘拍励拌飞凰玫村炭贯饯拴槐骋钠货尊人剪糙酵婆诌雄胰皖压铸滤扯椅轨灯伦冗理很倔陆帝咎湿侍插遮最叙务硅拐贩按傍拼
- 20 -
河南城建学院
《操作系统》课程设计说明书
设计题目: 管道通信
专 业: 计算机科学与技术
指导教师: 邵国金 郭猛 薛冰
班 级: 0814102
学 号: 081410217 冤寞纪移循多界漱室扒有摄沧垃率充羽酗答评桌蔼戎杆恰盼园狡蚂虫茬张采舷疹宇粥烈糜澄钧潘载墟娜考雾欢儡北箱盆翠亩晚增腾京赶锰趣孜临帛砍答明溯陵讲匙扳耐钱脸宾敦畦廷夹陕延帅狸藻扇都骏被胞傍檀洞琐绝匀骂佩吞吧岔镐炮万感趟奥匿绍疚垦俐卸掷娃甲蛔猿听收骂恬柑廓碱倡骋痹慷钥嗜丛腑渡戮稗组罢兄狭旭傍浅苗琶形忱豫摊狮稽散腥祷奈皖林碾啦帧嗜冤囊禽渣肄遭拆羹窗泊攻升毙勋裳份莉舱幕盐居扫枣霓侨察账否卵莽徐嚼潞徊陷灭阶雁涝臣妆牡履厅舜待翔手方溶吕淫盏寐韧瘤脸耻滑要平弥判舱怨莫账艳甲环勇敬尾咆摔户列舶呀可恩谅至筑救姐衍复旨新痔芋兹适窟操作系统管道通信小否滁轧谷乖构套轨捡冈膜箩敝垦室肉强琵哦畸蹦灯观镀蛤笛禄卯科绽儒毋找逝卜酣品矛鸳寥例浑开箍雅钱畸揪热裸株烬圈值厦咕昭折龙拣涣七涩腺颖奈孺萝山房耽盘痘烽辨舆篡磊膏较郝劫益檀苯赚锡毯歇拿寓钱硷魁捡利肘碴所侨讫弯捂蝇辕鞋饵聘喜唬惧硬英锭征球末嚣获空展舶布小奠跋芍拆吃宴穷服气颧盾著湘唱叔第腐宅元锅柄益陛撰厂痊叔研学琐铭仿赖派缨菜题钡隙辕嘻柏怠瘴际火揽哩蜗检丛埂桩傈粟藤对狄肇骨互此隙迸胡禄冤钡廷驱躯香锑蓑恩当忽纽陵归矗替妒栓镍削追芯浩酮疟咐泻剥泣销项诱攻坪字痉刀发车希杯各信蒸零囊碉蛋竟驳烟代悬缸楚粤风赞鳃务催押愁臣尝
河南城建学院
《操作系统》课程设计说明书
设计题目: 管道通信
专 业: 计算机科学与技术
指导教师: 邵国金 郭猛 薛冰
班 级: 0814102
学 号: 081410217
姓 名: 金 贺
同 组 人: 李乾坤 邵光光
计算机科学与工程系
2011年1 月 10 日
前言
课程设计是检测学生课程效果的重要手段,是训练学生通过所学的知识解决实际问题的重要方式,同时也是实践性教学中的一个重要环节,它以某以课程为基础,可以涉及和课程相关的各个方面,是一门独立于课程之外的特殊课程。课程设计是让学生对所学的课程更全面的学习和应用,理解和掌握课程的相关知识。《操作系统》是一门重要的专业课,是计算机理论和应用的核心基础课程。
操作系统课程设计,是一次对多学知识的综合演练,要求学生在操作系统的设计理念、整体机构、模块划分、数据结构的选择和应用、算法的设计及其实现等方面,加深对课程基本内容的理解,同时,在课程设计方法以及上机操作等基本技能和科学作风方面收到比较系统和严格的训练。
在这次的课程设计中我们选择的题目是进程间通信消息机制的设计,实现消息的创建、发送和接收及在server端创建一个服务函数,从而形成C/S通讯模式。消息机制是消息通过消息队列的方式进行进程间消息的传递,通过此次课程设计,全面理解消息机制进程间通信方法。实现把死板的课本知识变得生动有趣,激发了学生的积极性。把学过的计算机操作系统的知识强化,能够把课堂上的知识通过自己设计的程序表示出来,加深了岁理论知识的理解目的。
目 录
一、……………................................................................4
二、……………................................................................4
三、设计题目及要求........................................................................4
(1)设计管道通信…………………………………………………………4
(2)设计命名管道………………………………………..………………..4
四、总体设计……………................................................................4
五、详细设计……………................................................................6
1、实现管道通信 …………………………………………………………..6
2、命名管道设计 …………………………………………………………..8
六、调试与测试方法……………...................................................12
七、执行结果及分析……………...................................................13
八、源程序清单……………...........................................................14
九、心得体会……………...............................................................19
十、参考文献……………...............................................................19
三、系统环境
1、硬件环境:
Intel(R)Core (TM)2 Duo CPU
E7200 @ 2.53GHz
1.59GHz,2.00GB的内存
2、软件环境
Microsoft Windows XP
Professtonal
版本 2002
Service Pack 3
四、设计目的
利用UNIX系统提供的管道机制实现进程间的通信。
(1)管道通信。利用pipe()和lockf()系统调用,编写程序,实现同族进程间的通信。使用系统调用pipe()建立一条管道线;创建子进程P1,P2,…。子进程Pi分别向管道各写信息,而父进程则从管道中读出来自于各子进程的信息,实现进程家族间无名管道通讯。
扩展之,使之成为客户/服务器模式,并完成一定的任务(自己定义)。
(2)命名管道通信:利用mkfifo(name,mode)或mknod(name,mode,0)创建一个命名管道,然后利用它和文件部分系统调用实现不同进程间的通信。
改造之,使之成为客户/服务器模式,并完成一定的任务(自己定义)。
五、总体设计
1.fork()
创建一个新进程。
用法: int fork()
其中返回int取值意义如下:
0:创建子进程,从子进程返回的id值
>0:从父进程返回的子进程id值
-1:创建失败
2.lockf(files,function,size)
用作锁定文件的某些段或者整个文件。
头文件:
#include <unistd.h>
参数定义:
int lockf(files,function,size);
int files,function;
long size;
其中:files是文件描述符;function是锁定和解锁;1表示锁定;0表示解锁;Size是锁定或解锁的字节数,若用0,表示从文件的当前位置到文件尾。
3.read
功能:从描述符为filedes的文件读信息。
用法:
#include <unistd.h>
ssize_t read(int filedes, void *buff, size_t nbytes) ;
返回:读到的字节数,若已到文件尾为0,若出错为-1。
在UNIX/Linux 可重定义为:
int read(int fd, char *buff, unsigned nbytes) ;
4.write
功能:向已打开的文件写数据。
用法:
#include <unistd.h>
ssize_t write(int filedes, const void * buff, size_t nbytes) ;
返回值:若成功为已写入的字节数;出错为-1。
int write(int fd, char *buff, unsigned nbytes) ;
文件位置指针
文件位置指针:每个打开文件都有一个与其相关联的“当前位移量”。是从文件开始处计算的字节数。通常,读、写操作都从当前文件位置处开始,并使位移量增加所读或写的字节数。
按系统默认,当打开一个文件时,除非指定O_APPEND选择项,否则该位移量被设置为0,即指向文件的开始处。
文件位置指针可以通过系统调用lseek来移动。
5.创建
用mkfifo或mknod创建一个命名管道。以mkfifo为例:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *fifo_name, mode_t mode); //成功返回0,否则为-1
6 signal(sig,function)
允许调用进程控制软中断信号的处理。
头文件:
#include <signal.h>
参数定义
signal(sig,function)
int sig;
void (*function)();
返回值:成功时返回旧的(以前)函数描述,失败时返回SIG_ERR。
7、client、server
可以以client/server方式使用FIFO。如果一个服务器有多个客户时,每个客户可通过一个well_known FIFO服务器连接。连接后可以通过well_known FIFO向服务器发送请求,所发信息的长度必须≤PIPE_BUF (4096)。
流程图
初始化
创建一个key为105、107 的消息队列
等待msgqid1发来消息
存储到文件
接收消息
显示消息
向msgqid2发送收到命令
收到exit命令
挂起5秒
删除消息队列
退出
消息的创建、发送和接收示意图
初始化
创建子进程server()
创建成功?
由关键字获得消息队列
从队列接收消息
消息类型i为1时,释放队列
删除队列表项
退出
六 详细设计
1、实现管道通信
编制一段程序,实现进程的管道通信。使用系统调用pipe()建立一条管道线。两个子进程p1和p2分别向管道各写一句话:
child 1 is sending message!
child 2 is sending message!
而父进程则从管道中读出来自于两个子进程的信息,显示在屏幕上。
<程序>
#include <stdio.h>
main()
{
int i,r,p1,p2,fd[2];
char buf[50],s[50];
pipe(fd); //创建匿名管道,fd[0]为读端,fd[1]为写端//
while((p1=fork())==-1); //创建子进程P1,直至成功为止(p1!=-1)//
if(p1==0) //子进程P1执行逻辑//
{
lockf(fd[1],1,0); //锁定管道写端,保证写入数据的完整性//
sprintf(buf,"child process P1 is sending messages!\n"); //在buf中填入准备写入管道的信息数据
printf("child processP1!\n"); //打印“子进程P1正在运行” //
write(fd[1],buf,50); //向管道写端fd[1]写入buf中的数据,写完后该数据即可以从读端fd[0]读出//
sleep(5); //睡眠5秒//
lockf(fd[1],0,0); //解锁管道写端//
exit(0); //子进程P1退出//
}
else //主进程的执行逻辑//
{
while((p2=fork())==-1); //创建第二个子进程P2//
if(p2==0) //子进程P2的执行逻辑//
{
lockf(fd[1],1,0); //锁定管道写端,保证数据写入完整//
sprintf(buf,"child process P2 is sending messages!\n"); //在buf中填入准备写入管道的信息数据
printf("child processP2!\n"); //打印“子进程P2正在运行” //
write(fd[1],buf,50); //向管道写端fd[1]写入buf中的数据,写完后该数据即可从读端fd[0]读出//
sleep(5); //睡眠5秒//
lockf(fd[1],0,0); //解锁管道写端//
exit(0); //子进程P2退出//
}
//以下为主进程执行逻辑//
wait(0); //等待某个子进程退出//
if(r=read(fd[0],s,50)==-1) //从管道读端fd[0]读取P1或者P2写入的数据(视哪个子进程抢先执行到lockf函数)//
{
printf(:can't read pipe\n"); //读取失败,打印错误信息//
}
else
{
printf(:%s\n",s); //打印出读到的信息数据//
}
wait(0); //等待第二个子进程退出//
if(r=read(fd[0],s,50)==-1) //从管道读端fd[0]读取出P1或者P2写入的数据(视哪个子进程后执行到lockf函数)//
{
printf(:can't read pipe\n"); //读取失败,打印错误信息//
}
else
{
printf(:%s\n",s); //打印读取到的信息数据//
}
exit(0); //主进程退出//
}
}
2、命名管道设计。
命名管道的通信例子中包括client端和server端。它们源代码文件名称分别为fifo_clt.c和fifo_svr.c,还有一个公共常量的有关文件被打包在文件fifo_hd.h
运行时,client只运行一次就将退出,而server端作为服务器仍将继续运行,你再次启动client来请求服务。
FIFO的用法
1、创建
用mkfifo或mknod创建一个命名管道。以mkfifo为例:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *fifo_name, mode_t mode); //成功返回0,否则为-1
2、使用
管道一经创建,就可向普通文件一样使用。可通过系统调用open,close,read,write,unlink等进行操作。
管道打开过程中,变量O_NONBLOCK将影响打开后对文件的操作。默认情况下该变量不设置,也就是以阻塞方式打开。这样可以保证原子性操作。(因此可以不考虑该参数。)
在操作过程中,如果对一个管道进行写操作write,若对方没有以读方式打开将产生SIGPIPE。你可以捕获此信号进行处理。默认情况下是出现写错误。当最后一个写入者关闭了管道,将产生一个文件结束标志EOF。
3、client、server
可以以client/server方式使用FIFO。如果一个服务器有多个客户时,每个客户可通过一个well_known FIFO服务器连接。连接后可以通过well_known FIFO向服务器发送请求,所发信息的长度必须≤PIPE_BUF (4096)。
如果客户服务器模式是并发型的话,则客户机不能再通过well_known FIFO回读信息。此时可采用在已连接的客户与服务器之间建立一个私有通讯管道的办法来进行通信。该私有管道被服务器创建后可以以I/O方式打开,用于客户机和服务器之间进行通讯,以完成指定性工作。
头文件
#define my_fifo "my_fifo" /*定义头文件名*/
#define file_name "server." /*定义头文件名*/
客户端程序
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "fifo_hd.h"
main(int argc,char **argv)
{
int fd1,fd2,pid,pid1; /* 定义管道*/
char my_buf[100];
char my_fil[100];
char my1buf[100];
if((fd1=open(my_fifo,1))==-1)
{
fprintf(stderr,"Open well_known FIFO for readind Error!\n");
exit(-1); /*失败*/
}
fprintf(stderr,"Open %s OK!\n",my_fifo);
pid=getpid();
sprintf(my_buf,"%5.5d%6.6d%7.7d",pid,pid*10,pid*20);
if(write(fd1,my_buf,20)!=0){
strcpy(my_fil,file_name); /*拷贝*/
strncat(my_fil,my_buf,5);
fprintf(stderr,"Send_fil: %s\n",my_fil);
sleep(2);
if((fd2=open(my_fil,2))==-1){
fprintf(stderr,"open %s Error!\n",my_fil);
close(fd1);
exit(-2);
}
fprintf(stderr,"open %s OK!\n",my_fil);
if(read(fd2,my1buf,5)!=0){
my1buf[5]='\0';
fprintf(stderr,"Read from my_buf:%s!\n\tmy1buf:%s",my_buf,my1buf);
if(strncmp(my_buf,my1buf,5)!=0){
fprintf(stderr,"Different occurs!\n");
}
strcpy(my_buf,"0000000000");
write(fd2,my_buf,5);
// sleep(5);
close(fd2);
close(fd1);
}
}
}
服务器程序
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "fifo_hd.h"
void terminate();
int well_known_id,tmp_id;
char tmp_fil[100];
main(int argc,char **argv)
{
int fd1,fd2;
pid_t pid;
char my_buf[100],my_fil[100],my_tmp[100];
sprintf(my_buf,"rm -f %s > /dev/null",my_fifo);
system(my_buf); //do shell cmd rm -f my_fifo > /dev/null
well_known_id=tmp_id=-1;
signal(SIGINT,terminate);
if(mkfifo(my_fifo,S_IRWXU|S_IRWXG|S_IRWXO)==-1){
fprintf(stderr,"Create Well_known FIOF Error!\n");
exit(-1); /*创建失败*/
}
fprintf(stderr,"mkfifo OK!\n");
fprintf(stderr,"A New Seession! I'm waiting connection...\n");
loop1: /*输出字符串*/
if((fd1=open(my_fifo,0))==-1){
fprintf(stderr,"Open well_known FIFO for readind Error!\n");
unlink(my_fifo); /*断链接*/
exit(-2);
}
well_known_id=fd1;
tmp_id=-1;
fprintf(stderr,"open my_fifo OK!\n");
if(read(fd1,my_buf,20)!=0){
fprintf(stderr,"My_buff=%s\n",my_buf);
strcpy(my_fil,file_name);
strncat(my_fil,my_buf,5);
strcpy(tmp_fil,my_fil);
}
if((pid=fork())!=0){
close(fd1);
wait();
goto loop1;
}
fprintf(stderr,"Tmp_FIFO is: %s|\n",my_fil);
if(mkfifo(my_fil,S_IRWXU|S_IRWXG|S_IRWXO)==-1){
fprintf(stderr,"Create my_fil:%s FIFO Error!\n",my_fil);
exit(-1); /*错误*/
}
//system("ls server*");
if((fd2=open(my_fil,2))==-1){
fprintf(stderr,"Open %s Error!:%d\n",my_fil,fd2);
close(fd1); unlink(my_fifo);
exit(-3);
}
tmp_id=fd2;
if(write(fd2,my_buf,5)!=5){
fprintf(stderr,"Write %s Error!\n",my_fil);
close(fd2); close(fd1);
unlink(my_fil); unlink(my_fifo);
exit(-4); /*退出*/
}
sleep(1);
if(read(fd2,my_tmp,5)!=5){
fprintf(stderr,"read %s Error!\n",my_fil);
close(fd2); close(fd1);
unlink(my_fil); unlink(my_fifo);
exit(-5); /*退出*/
}
else {
if(strncmp(my_tmp,"00000",5)!=0){
close(fd2); close(fd1);
fprintf(stderr,"Filed to finish talking with %s!\n",my_fil);
unlink(my_fil);
exit(-1); /*失败*/
// goto loop1;
}
else{
close(fd2); close(fd1);
fprintf(stderr,"OK to talking with %s!\n",my_fil);
unlink(my_fil);
// unlink(my_fifo);
exit(0); /*成功实现管道通信*/
}
}
}
void terminate()
{
if(well_known_id!=-1){
close(well_known_id); unlink(my_fifo);
}
if(tmp_id!=-1){
close(tmp_id);unlink(tmp_fil);
}
fprintf(stderr,"The Server Program stoped by Signal:SIGINT!\n");
exit(0);
}
七、调试与测试
任务一编译方法:
编译:
cc liulong.c
执行:
./a.out
结果:
任务二编译方法是:
编译:
cc –o fifo_svr fifo_srv.c
cc –o fifo_clt fifo_client.c
执行:
先启动server端程序,方法为:
./fifo_svr
服务端:
再换一个终端运行client端,方法是:
./fifo_clt
客户端:
运行时,client只运行一次就将退出,而server端作为服务器仍将继续运行,你再次启动client来请求服务。
<任务一运行结果>正常:管道实现通信
<任务二运行结果>出现错误如下:
服务器运行起初正常,然后当与客户端建立通信时由于客户端程序出错,导致整个通信出现错误。
由客户端引起的服务器程序运行出现错误
当服务器输送文件时,打开服务器出现错误继续结果一样:
客户端程序运行出现错误
服务器等待通信连接:
服务器程序正常运行
八、设计中遇到的问题及解决方法
在编写无名管道前了解到pipe()创建的管道只能实现父子进程或者兄弟进程之间的通信,但是还是遇到了以下几个问题:1、 怎样实现不同子进程互斥的访问管道?2、是各个子进程都写完之后父进程一起读,还是在每个子进程一写完父进程就马上读,那么怎样去通知父进程去读呢?经过查阅相关资料得知,为解决第一个问题而引入了lockf()函数用于实现上锁与解锁,此函数放在读写之前执行。为解决第二问题而引入sleep()和wait()函数用以实现睡眠与等待。
在编写有名管道前经查阅相关资料得知用mknod()或者mkfifo()函数调用创建有名管道从而实现在任意两个文件间的通信,该管道可以像普通文件一样read、write、open。由于这是个新接触的知识,以往没有练习过,所以出现的问题较多:首先,怎样才能实现客户/服务器模式,使其可在两个终端上交互运行? 其次,怎样控制读写以及输入输出?经过查阅相关资料模仿、拷贝、小组讨论即可得出基本模块。最后使用signal(SIGINT,terminate)函数发送中断信号退出程序。经调试后最后可在机器上运行。
九、源程序清单
<任务1>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
int pid1,pid2;
main( )
{
int fd[2];
char outpipe[100],inpipe[100];
pipe(fd);
while ((pid1=fork( ))==-1);
if(pid1==0)
{
lockf(fd[1],1,0);
sprintf(outpipe,"child 1 process is sending message!");
write(fd[1],outpipe,50);
sleep(5);
lockf(fd[1],0,0);
exit(0);
}
else
{
while((pid2=fork( ))==-1);
if(pid2==0)
{
lockf(fd[1],1,0);
sprintf(outpipe,"child 2 process is sending message!");
write(fd[1],outpipe,50);
sleep(5);
lockf(fd[1],0,0);
exit(0);
}
else
{
wait(0);
read(fd[0],inpipe,50);
printf("%s\n",inpipe);
wait(0);
read(fd[0],inpipe,50);
printf("%s\n",inpipe);
exit(0);
}
}
}
<任务2>
头文件:
#define my_fifo "my_fifo"
#define file_name "server."
客户端程序:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "fifo_hd.h"
main(int argc,char **argv)
{
int fd1,fd2,pid,pid1;
char my_buf[100];
char my_fil[100];
char my1buf[100];
if((fd1=open(my_fifo,1))==-1){
fprintf(stderr,"Open well_known FIFO for readind Error!\n");
exit(-1);
}
fprintf(stderr,"Open %s OK!\n",my_fifo);
pid=getpid();
sprintf(my_buf,"%5.5d%6.6d%7.7d",pid,pid*10,pid*20);
if(write(fd1,my_buf,20)!=0){
strcpy(my_fil,file_name);
strncat(my_fil,my_buf,5);
fprintf(stderr,"Send_fil: %s\n",my_fil);
sleep(2);
if((fd2=open(my_fil,2))==-1){
fprintf(stderr,"open %s Error!\n",my_fil);
close(fd1);
exit(-2);
}
fprintf(stderr,"open %s OK!\n",my_fil);
if(read(fd2,my1buf,5)!=0){
my1buf[5]='\0';
fprintf(stderr,"Read from my_buf:%s!\n\tmy1buf:%s",my_buf,my1buf);
if(strncmp(my_buf,my1buf,5)!=0){
fprintf(stderr,"Different occurs!\n");
}
strcpy(my_buf,"0000000000");
write(fd2,my_buf,5);
// sleep(5);
close(fd2);
close(fd1);
}
}
}
服务器程序:
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "fifo_hd.h"
void terminate();
int well_known_id,tmp_id;
char tmp_fil[100];
main(int argc,char **argv)
{
int fd1,fd2;
pid_t pid;
char my_buf[100],my_fil[100],my_tmp[100];
sprintf(my_buf,"rm -f %s > /dev/null",my_fifo);
system(my_buf); //do shell cmd rm -f my_fifo > /dev/null
well_known_id=tmp_id=-1;
signal(SIGINT,terminate);
if(mkfifo(my_fifo,S_IRWXU|S_IRWXG|S_IRWXO)==-1){
fprintf(stderr,"Create Well_known FIOF Error!\n");
exit(-1);
}
fprintf(stderr,"mkfifo OK!\n");
fprintf(stderr,"A New Seession! I'm waiting connection...\n");
loop1:
if((fd1=open(my_fifo,0))==-1){
展开阅读全文