模拟进程通信

来源:互联网 发布:知乐作品集txt下载 编辑:程序博客网 时间:2024/05/08 09:09

1,实验要求
2次调用fork创建2个子进程,其中一个充当服务器server,另一个充当客户端client,来模拟操作系统内部进程间的通信。
客户端向服务器消息类型mtype为10到1的消息,服务端接收消息,收到类型为1的消息时则停止接收。
2,进程间通信的主要函数:

/*函数1功能:创建消息队列参数:MSGKEY是一个由用户提供的整数,用来标志这个资源的实例;0777|IPC_CREAT创建消息队列返回:消息队列的id*/msgqid=msgget(MSGKEY,0777|IPC_CREAT);//创建消息队列
/*函数2功能:接收消息参数:msgqid,指定消息队列id;&msg指向放置到来消息的缓冲区;1024限制能够被读到的数据的大小为1024字节,即1K;-10,指定接收消息队列里消息类型小于等于10的最低类型的第一个消息;0,表示消息发送不成功时不立即返回,需要等待。*/msgrcv(msgqid,&msg,1024,-10,0);//接收消息
/*函数3功能:发送消息参数:msgqid,指定消息队列的id;&msg指向放置到来消息的缓冲区;1024指定发送的最大数据量为1024字节,即1K;0,表示消息发送不成功时不立即返回,需要等待。*/msgsnd(msgqid,&msg,1024,0);       //发送消息
/*函数4功能:删除消息队列参数:msgqid,指定要删除的消息队列的id;IPC_RMID表示清空并删除消息队列;*/    msgctl(msgqid,IPC_RMID,0);

3 代码实现

/*Name:shiyan4.c *Created on 2015-10-14 *Author:Wanglin *Function:创建两个子进程,模拟服务器和客户端通信 */#include<stdio.h>#include<unistd.h>#include <sys/types.h>#include <sys/msg.h>#include <sys/ipc.h># include<string.h>#define MSGKEY 75struct msgform{    long mtype; //消息首部    char mtext[1024];//1k消息正文} msg;int msgqid;//消息队列id,用来访问消息队列int main(){    int p1,p2;    p1=fork();//创建server端    if(!p1)    {        //[server端]        msgqid=msgget(MSGKEY,0777|IPC_CREAT);//由server端创建消息队列        do        {            msgrcv(msgqid,&msg,1024,-10,0);//server端接收消息            printf("i=%d server:received\n",msg.mtype);//提示server端已接收            msg.mtype=1000;//为了区别应答消息与客户端发送的消息,修改应答消息类型为1000(只要不是1-10即可)            msgsnd(msgqid,&msg,1024,0);       /*发送应答消息msg*/        }        while(msg.mtype!=1);        msgctl(msgqid,IPC_RMID,0);//清空并删除消息队列    }    else    {        p2=fork();//创建lient端        if(!p2)        {            //[client端]            int i;            msgqid=msgget(MSGKEY,0777|IPC_CREAT);//系统检测到MSGKEY的消息队列已经存在,client与server共享这个消息队列            for(i=10; i>=1; i--)            {                msg.mtype=i;                msgsnd(msgqid,&msg,1024,0);       //往msgqid发送消息msg                printf("i=%d client:send\n",i);                msgrcv(msgqid,&msg,1024,1000,0);        //接收来自服务器进程的应答消息            }        }    }    /*wait(0)表示将父进程挂起,有子进程结束时被唤醒*/    wait(0);//其中一个子进程结束被唤醒    wait(0);//另一个子进程结束被唤醒    return 0;}

4,编译并运行代码

[root@localhost os]# vim shiyan4_V3.c[root@localhost os]# gcc shiyan4_V3.c[root@localhost os]# ./a.out [root@localhost os]# i=10 server:receivedi=10 client:sendi=9 server:receivedi=9 client:sendi=8 server:receivedi=8 client:sendi=7 server:receivedi=7 client:sendi=6 server:receivedi=6 client:sendi=5 server:receivedi=5 client:sendi=4 server:receivedi=4 client:sendi=3 server:receivedi=3 client:sendi=2 server:receivedi=2 client:sendi=1 server:receivedi=1 client:send

结果显示,server和client端交替出现,由于client端一共发送了10次消息,所以i从10到1;
实验结果与预期效果有些不符,预期应该是:先显示 client:sent,再显示server:received,结果与之相反。如何解释?client和server是并发执行的2个进程,如果不加锁,两者可以随时抢占对方的CPU,这两个进程的调度顺序取决于顺序的调度程序,不是由我们写的代码决定的。初始时,由server建立一个消息队列,消息队列为空,由client向消息队列中发送第一个i=10的消息,此时消息队列里面有了一个消息,server抢断client的cpu,接收消息队列中的消息,于是server抢在client的前面先输出“i=10 server:received”。
5,注意事项
(1)修改应答消息的mtype使其不与client发送的消息类型相同,此例中设置为1000。
(2)server端接收消息时,注意将msgrcv的第4个参数的合理范围【-999,-10】直接的整数。

 msgrcv(msgqid,&msg,1024,-10,0);//server端接收消息

(3)client端接收server的应答消息时,msgrcv的第4个参数必须和应答消息的mtype相同,即1000。

 msgrcv(msgqid,&msg,1024,1000,0);        //接收来自服务器进程的应答消息

修改应答消息的mtype的作用是用来区别这两类消息:一是server端的应答消息,消息类型mtype=1000,二是client端发送给server端的消息,消息类型1<=mtype<=10。
server端接收消息时,假设msgrcv的第4个参数为0,即接收消息队列里的第1个消息,那么它就可能接收到刚刚它返回给client端的应答消息,而这是不合理的,我们希望应答消息只由client端来接收。设置msgrcv的第三个参数为-10,表示只能接收消息类型小于等于10的消息,由于应答消息的消息类型被设置为1000,于是server就不会错接收到应答消息。
在client端接收应答消息时,指定msgrcv的第4个参数为1000,保证了client端只接收应答消息,而不会接收自己发送给server的消息,避免出错。
(4)程序结束时,记得清空和删除消息队列。倘若不删除,消息队列会缓存在内存中,可能会影响下次程序的运行。

msgctl(msgqid,IPC_RMID,0);//清空并删除消息队列

6,仍待解决的问题
这个程序还有很多可以拓展的地方。
(1)如何让进程正常退出?
程序执行结束后,并没有自动回到shell进程,人为中断(例如ctrl+C)才能回到shell,如何让其自动回到shell?

...i=1 server:receivedi=1 client:send//此处没有自动回到shell进程[root@localhost os],需要人为中断

(2)如何实现client端先sent,server端再received?
上面分析过,这两个进程随时都会相互打断,如果一定要让client:send在前, server:received在后,该如何实现?
(3)使用2个消息队列?
(4)现实中,一个服务器往往是给成百上千或者成千上万的客户端服务的,如何模拟多个client发送的情况呢?

1 0
原创粉丝点击