模拟进程通信

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 75
struct 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:received
i=10 client:send
i=9 server:received
i=9 client:send
i=8 server:received
i=8 client:send
i=7 server:received
i=7 client:send
i=6 server:received
i=6 client:send
i=5 server:received
i=5 client:send
i=4 server:received
i=4 client:send
i=3 server:received
i=3 client:send
i=2 server:received
i=2 client:send
i=1 server:received
i=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:received
i=1 client:send
//此处没有自动回到shell进程[root@localhost os],需要人为中断

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值