}
msgid=msgget(1234,0666|IPC_CREAT); while((p1=fork())==-1); if(p1==0) { execv(path,argv); exit(0); } else { wait(0); while(running) { msgrcv(msgid,&msg,BUFSIZ,msg_to_receive,0); printf(\,msg.some_text); if(strncmp(msg.some_text,\,3)==0) running=0; } msgctl(msgid,IPC_RMID,0); exit(0); }
子进程:
#include
#include
struct my_msg { long int my_msg_type; char some_text[MAX_TEXT]; }msg;
main() { int msgid,running=1; char buffer[BUFSIZ]; msgid=msgget(1234,0666|IPC_CREAT);
6
}
while(running) { puts(\); fgets(buffer,BUFSIZ,stdin); msg.my_msg_type=1; strcpy(msg.some_text,buffer); msgsnd(msgid,&msg,MAX_TEXT,0); if(strncmp(msg.some_text,\,3)==0) running=0; }
exit(0);
运行结果:
子进程运行,显示:“Enter some text:”;输入内容后,运行父进程,显示“You wrote:”和刚输入的内容。
(2)分别编写发送进程和接收进程,由发送进程发送消息,接收进程接收消息。采用先执行发送进程后执行接收进程的方式同步。以“end”作为结束消息。 发送方:
#include
main() { int msgid,running=1; char buffer[BUFSIZ]; msgid=msgget(1234,0666|IPC_CREAT); while(running) {
7
}
puts(\); fgets(buffer,BUFSIZ,stdin); msg.my_msg_type=1; strcpy(msg.some_text,buffer); msgsnd(msgid,&msg,MAX_TEXT,0); if(strncmp(msg.some_text,\,3)==0) running=0; }
exit(0);
接收方:
#include
struct my_msg { long int my_msg_type; char some_text[MAX_TEXT]; }msg;
main() { int msgid,running=1; long int msg_to_receive=0; msgid=msgget(1234,0666|IPC_CREAT); while(running) { msgrcv(msgid,&msg,BUFSIZ,msg_to_receive,0); printf(\,msg.some_text); if(strncmp(msg.some_text,\,3)==0) running=0; } msgctl(msgid,IPC_RMID,0); exit(0); }
运行结果:与上个实验类似。
(3)模拟C/S通信,要求如下。(选做)
8
① 模拟客户端(client端)程序client,其功能如下。 a) 显示下列服务功能菜单: Enter your choice: 1. Save money 2. Take money
b)接收用户键入的功能号进行选择;
c) 将用户键入的功能号作为一条信息发送至消息队列,然后结束。 ②模拟服务器端(Server端)程序server,其功能如下。 a) 从消息队列接收Client端发来的一条消息; b)根据消息作如下处理。
若消息为“1”,则创建子进程1,由子进程1加载服务模块save,该模块的作用为显示以下信息:
Your money was saved!
若消息为“2”,则创建子进程2,由子进程2加载服务模块take,该模块的作用为显示以下信息:
Please take your money!
c) 等待子进程终止后,Server进程删除消息队列,然后结束。
【实验提示】
1. C编译器gcc的使用
GCC(GNU Compiler Collection)是目前Linux下最常用的C语言编译器,能够编译用C\\C++等语言编写的程序。通过GCC源码文件生成可执行文件的过程要经过4个阶段,分别是预处理、编译、汇编和链接。
格式:gcc 【选项】 【目标文件】源文件
如不使用任何参数将生成一个名为a.out的文件,执行时输入./a.out(./表示在当前路径下)。
2. 进程控制的系统调用 fork系统调用创建新的子进程 格式: pid=int fork()
fork调用返回时,系统中已有两个用户级环境完全相同的进程存在,这两个进程从fork调用中得到的返回值不同,其中子进程得到的返回值为零,父进程得到的返回值是新创建子进程的进程标识号。
3.进程通信的系统调用
9
? pipe系统调用
格式: int pipe (filedes) int filedes [2]; ? 消息机制
?Msgid=msgget(key,msgflg) key_t key; int msgflg;
?Msgctl(msgid, cmd, buf) int msgid, cmd; msgqid_ds #buf;
?msgsnd(msgid, msgp, msgsz, msgflg) int msgid;
struct msgbuf *msgp; int msgsz, msgflg;
?msgrcv (msgid, msgp, msgsz, msgtyp, msgflg ); int msgid;
struct msgbuf *msgp; int msgsz; long msgtyp; int msgflg;
注:系统调用详细说明和使用示例可参考ppt资料。
【实验要求】
1. 观察实验结果,分析结果产生原因。
2. 实验结束一周后需提交实验报告和源程序文件。
【实验思考】
1. 实验中如何实现的父子进程的同步?
答:通过系统调用wait()和exit(),实现父子进程同步。 2. 管道通信的读写进程之间必须满足什么关系?
答:写进程负责将数据从管道写入端写入管道,读进程负责将数据从读出端读出管道。 3. 在使用消息缓冲通信进行通信时,发送和接收者之间的同步机制由谁提供?
答:操作系统根据用户进程提供的key值,在msgque中查找该key值的ipc_perm结构以及与该key值对应的消息队列头q_messages,该队列上挂的是一系列msg_msg数据结构,每个msg_msg数据结构指向一个消息缓冲区msgbuf,而该缓冲区msgbuf中存放的就是需要传递的消息正文。操作系统通过对消息队列链表结构msgque中三个队列的管理来实现消息的发送、接收以及发送与
10