进程间通信是操作系统中重要的内容,也是操作系统的基础内容,下面通过简单的进程间通信实验初步了解进程间通信。
实现内容:
(1) 消息的创建、发送和接收
①使用系统调用msgget(),msgrev(),msgsnd()及msggctl()编制一长度为1K的消息的发送和接收程序。
②观察上面的程序,说明控制消息队列系统调用msgctl()在此起了什么作用?
(2)共享存储区的创建、附接和断接
使用系统调用shmget(),shmat(),shmdt(),shmctl(),编制一个与上述功能相同的程序。
比较上述(1)、(2)两种消息通信机制中的数据传输的时间。
上述实现内容的算法描述如下
(1)为了便于操作和观察结果,用一个程序作为“引子”,先后fork()两个子进程,SERVER和CLIENT ,进行通信。SERVER端建立-一个Key为75的消息队列,等待其他进程发来的消息。当遇到类型为1的消息,则作为结束信号,取消该队列,并退出SERVER。SERVER每接收到一个消息后显示一句“(server )received".CLIENT端使用key为75的消息队列,先后发送类型从10到1的消息,然后退出。最后的一个消息,即是SERVER端需要的结束信号. CLIENT每发送一条消息后显示- -句“(client )sent"。父进程在SERVER和CLIENT均退出后结束。
(2)为了便于操作和观察结果,用一个程序作为“引子”,先后fork()两个子进程,SERVER和CLIENT ,进行通信。SERVER端建立-一个Key为75的共享区,并将第-一个字节置为-1.作为数据空的标志。等待其他进程发来的消息。当该字节的值发生变化时,表示收到了信息,进行处理。然后再次把它的值设为一1。如果遇到的值为0,则视为结束信号,取消该队列,并退出SERVER. SERVER每接收到- -次数据后显示“(server )received" .CLIENT端建立一个Key为75的共享区,当共享取得第一个字节为一1时,Server端空闲,可发送请求。CLIENT随即填入9到0。期间等待Server端的再次空闲。进行完这些操作后,CLIENT退出。CLIENT每发送- -次数据后显示“(client)sent".父进程在SERVER和CLIENT均退出后结束。
此处给出实现效果和分析:
1、消息的创建、发送和接收
(1)首先利用fork()创建两个进程,父进程中是SERVER,子进程是CLIENT,进行通信。
(2)SERVER端建立了一个key为75的消息队列,等待其他进程发来的消息。当遇到类型为1的消息,则作为结束的信号,取消该队列,并退出SERVER,SERVER接收到一个消息后会显示“(server)receive”。
(3)CLIENT端使用Key为75的消息队列,先后发送类型从10到1的消息,然后退出。最后一个消息,类型为1,也是作为SERVER端需要结束的信号。CLIENT每发送一条消息后会显示一句“(client)sent”。
结果分析:按照预期,应该是当CLIENT端发送一个消息后SERVER端接受一条消息,应该是两者交替进行的,但实际出现的情况是在先由CLIENT发送两条消息,然后SERVER接收一条消息,此后两者交替发送和接收消息,最后SERVER一次接收两条消息。CLIENT和SERVER分别发送和接收了10条消息,这与预期设想的一致。造成上面这种情况的缘由可能是在Message的传送和控制并不保证完全的同步,当一个程序不在激活的状态的时候,它完全可能继续睡眠,只有在多次发送之后才接收信息。
在此调用msgctl是用于删除id的消息队列。
2、共享存储区的创建、附接和断接
(1)首先利用fork()创建两个进程,父进程中是SERVER,子进程是CLIENT,进行通信。
(2)SERVER端建立了一个key为75的共享区,并将第一个字节置为-1,作为数据空的标志。等待其他进程发来的消息,当该字节的值发生变化的时候,表示收到了消息,然后进行处理,并再次把他的值设为-1,如果遇到值为0,则表示结束信号,取消该队列,并退出。SERVER接收到一个消息后会显示“(server)receive”。
(3)CLIENT端使用Key为75的共享区,当共享取得第一个字节为-1的时候,表示Server端是空闲的状态,这时发送请求。CLIENT随即填入9到0,期间等待Server端再次空闲,进行完这些操作后退出。最后一个消息,为0,也是作为SERVER端需要结束的信号。CLIENT每发送一条消息后会显示一句“(client)sent”。
实验结果与预期设想的是一致的,Client和Server是交替进行发送和接收的。
3、比较上述(1)、(2)两种消息通信机制中的数据传输的时间.
从图4.1和图4.2中可以看出利用第一种方式传输数据比利用第二种方式传输数据所用的时间要少一些,此处只是传输少量的数据,当进行大量的数据传输时,因共享区的数据传输受到了系统硬件的支持,不会消耗多余的资源;而进行消息传递,因为需要由软件进行控制和实现,需要消耗一定的CPU资源,因此共享区会更加适合于频繁和大量的数据传输,而在同步方面,因消息的传递机制本书就带有同步的控制,当等到消息的时候,进程进入睡眠状态,不会再消耗CPU资源,而共享区如果不借助于其他的机制进行同步,接受数据一方必须不断的进行查询,会浪费大量CPU资源。
最后给出实现源码(在putty下实现):
进程管理:
#include<stdio.h>
#include<sys/types.h>
#include<sys/msg.h>
#include<sys/wait.h>
#include<sys/ipc.h>
#include<stdlib.h>
#include<unistd.h>
#include<time.h>
#define MSGKEY 75
clock_t start,finish;
double total_time;
struct msgform /*消息结构*/
{
int mtype;
char text[1030];
}msg;
int msgid;
void CLIENT()
{
int i;
msgid=msgget(MSGKEY,0777);
for(i=10;i>=1;i--)
{
msg.mtype=i;
printf("(client)sent%d\n",i);
//sleep(1);
msgsnd(msgid,&msg,sizeof(msg),0); /*发送消息msg入msgid消息队列*/
}
exit(0);
}
void SERVER()
{
int i=1;
msgid=msgget(MSGKEY,0777|IPC_CREAT); /*由关键字获得消息队列*/
do
{
msgrcv(msgid,&msg,sizeof(msg),0,0); /*从队列msgid接受消息msg*/
printf("(server)receive%d\n",i);
i++;
//sleep(1);
}while(msg.mtype!=1); /*消息类型为1时,释放队列*/
msgctl(msgid, IPC_RMID,0);
finish = clock();
total_time = (double)(finish - start)/CLOCKS_PER_SEC;
printf("End to transfer!,and the time is %f\n",total_time);
exit(0);
}
void main(){
start = clock();
pid_t pid1,pid2;
printf("Start to transfer:\n");
while((pid1=fork())==-1);/*创建一个进程*/
if(pid1>0)SERVER();
else{
CLIENT();
}
wait(0);
wait(0);
}
进程通信:
#include<stdio.h>
#include<sys/types.h>
#include<sys/shm.h>
#include<sys/wait.h>
#include<sys/ipc.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/msg.h>
#include<time.h>
#define SHMKEY 75
int shmid;
int *addr;
clock_t start,finish;
double total_time;
void CLIENT(){
int i;
shmid=shmget(SHMKEY,1024,0777); /*获取共享区,长度为1024*/
addr=shmat(shmid,0,0); /*共享区起始地址为addr*/
for(i=9;i>=0;i--)
{
while(*addr!= -1);
printf("(client)sent%d\n",i);
*addr=i;
}
printf("client is over!\n");
exit(0);
}
void SERVER()
{
shmid=shmget(SHMKEY,1024,(0777|IPC_CREAT));
addr=shmat(shmid,0,0);
do
{
*addr=-1;
while(*addr == -1);
printf("(server)received\n");
}while(*addr);
shmctl(shmid,IPC_RMID,0);
printf("server is over!\n");
finish = clock();
total_time = (double)(finish - start)/CLOCKS_PER_SEC;
printf("End to transfer!,and the time is %f\n",total_time);
exit(0);
}
void main(){
start = clock();
pid_t pid1,pid2;
printf("Start to transfer:\n");
while((pid1=fork())==-1);/*创建一个进程*/
if(pid1>0)SERVER();
else{
CLIENT();
}
//system("ipcs -m");
wait(0);
wait(0);
}