一.实验目的:
Linux的进程通信机构(IPC)允许在任意进程之间大量的交换数据。本实验的目的是了解和熟悉Linux支持的消息通信机制、共享存储区机制及信息量机制。
二.实验要求:
阅读Linux系统的msg.c,sem.c和shm.c等源码文件,熟悉Linux中的三种通信机制。
三. 实验内容:
- 消息的创建、发送和接受。(1)使用系统调用msgget( ),msgsnd( ),msgrev( )及msgctl()编制一长度为1K的消息的发送和接收程序。(2)观察上面程序,说明控制消息队列系统调用msgctl()在此起什么作用?
- 共享存储区的创建、附接和断接。
使用系统调用shmget( ),shmat( ),sgmdt( ),shmctl( ),编制一个与上述功能相同的程序。
3. 比较以上两种通信机制中数据传输的时间。
四.算法描述:
1. msgget (key,flag)函数:
获得一个消息的描述符,该描述符指定一个消息队列以便用于其他系统调用。该函数第一个参数是消息队列对象的关键字(key),函数将它与已有的消息队列对象的关键字进行比较来判断消息队列对象是否已经创建。而函数进行的具体操作是由第二个参数msgflg 控制的。它可以取下面的几个值:
IPC_CREAT:如果消息队列对象不存在,则创建,否则则进行打开操作;IPC_EXCL:和IPC_CREAT 一起使用(用“|”连接),如果消息对象不存在则创建之,否则产生一个错误并返回。
msgqid是该系统调用返回的描述符,失败则返回-1,
flag本身由操作允许权和控制命令值相或得到。
2. msgsnd(id ,msgp.size ,flag )函数:发送一消息。
其中id是返回消息队列的描述符;msgp是指向用户存储区的一个构造体指针,size指示由msgp指向的数据结构中字符数组的长度;即消息的长度。这个数组的最大值由MSG-MAX系统可调用参数来确定。flag 规定当核心用尽内部缓冲空间时应执行的动作;若在标志flag 中未设置IPC_NOWAIT位,则当该消息队列中的字节数超过一最大值时,或系统范围的消息数超过某一最大值时.调用msgsnd进程睡眠。若是设置IPC_NOWAIT,则在次情况下,msgsnd立即返回。
3.msgctl(id,cmd ,buf)函数:查询一个消息描述符的状态,设置它的状态及删除一个消息描述符。
其中函数调用成功时返回0,调用不成功时返回-1。
id用来识别该消息的描述符;cmd规定命令的类型。IPC-STAT将与id相关联的清息队列首标读入buf。
IPC_SET为这个消息序列设置有效的用户和小组标识及操作允许权和字节的数量。IPC_RMID删除id的消息队列。
buf是含有控制参数或查询结果的用户数据结构的地址。
4. shrnget(key ,size ,flag)函数:获得一个共享存储区。
其中size是存储区的字节数,key和 flag 与系统调用msgget中的参数含义相同。
5. shmat(id ,addr,flag )函数:从逻辑上将一个共享存储区附接到进程的虚拟地址空间上。
其中id是共享存储区的标识符,addr是用户要使用共享存储区附接的虚地址,若addr是0,系统选择一个适当的地址来附接该共享区。flag 规定对此区的读写权限,以及系统是否应对用户规定的地址做舍入操作。
6. shmdt(addr) 函数: 把一个共享存储区从指定进程的虚地址空间断开。
其中,当调用成功时,返回0值,调用不成功,返回-l,addr是系统调用shmat所返回的地址。
五.运行结果与分析:
先后创建两个进程SERVER和CLIENT,SERVER端接收消息,每接收一次显示一次(sercer)received,遇到消息为1时结束,并退出SERVER。CLIENT端发送消息:从10到1,每发一次消息显示一次(client)sent。因此SERVER和CLIENT应分别接收和发送了10条消息。运行结果如下图所示:
图1 消息的创建、发送和接收结果图
message 的传送和控制并不保证完全同步,当一个程序不在激活状态的时候,它完全可能继续睡眠,造成了上面的现象,在多次send message后才receive message。
接收端SERVER在每次收到消息进行处理之后,将共享区第一个字节置为-1,一直到遇到结束信号0,结束并退出SERVER。SERVER端每接收一次显示一次(sercer)received。发送端CLIENT在共享区第一个字节为-1时,可以发送请求;CLIENT填入9到0,期间等待接收端空闲。每发送一次数据显示一次(client)sent。运行结果如下图所示:
图8 共享存储区的创建、附接和断接图
运行的结果和预想的完全一样。但在运行的过程中,发现每当client 发送一次数据后,server要等待大约0.1秒才有响应。同样,之后client又需要等待约0.1秒才发送下一个数据。出现上述的应答延迟的原因是当client端发送了数据后,并没有任何措施通知server端数据已经发出,需要由client的查询才能感知,此时,client端并没有放弃系统的控制权,仍然占用CPU的时间片。只有当系统进行调度时,切换到了server进程,再进行应答。这个问题,也同样存在于server端到client的应答过程之中。
六.源程序:
1. 消息的创建、发送和接收
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#define MSGKEY 75
#define TEXT_SIZE 1030
struct msgform
{
long mytype;
char mtext[TEXT_SIZE];
}msg;
int msgqid, i;
void CLIENT()
{
int i;
msgqid = msgget(MSGKEY, 0666 | IPC_CREAT);
for (i = 10; i >= 1; i--)
{
msg.mytype = i;
if(msgsnd(msgqid, &msg, TEXT_SIZE, 0) == -1)
{
printf("Client Sent Error: %d \r\n", errno);
}
else
{
printf("(client)sent %d \r\n" ,i);
}
}
exit(0);
}
void SERVER()
{
msgqid = msgget(MSGKEY, 0666 | IPC_CREAT);
do
{
if(msgrcv(msgqid, &msg, TEXT_SIZE, 0, 0) == -1)
{
printf("Server Received Error: %d \r\n", errno);
}
else
{
printf("(server)received: %d \r\n", (int)msg.mytype);
}
} while (msg.mytype != 1);
msgctl(msgqid, IPC_RMID, 0);
exit(0);
}
void main()
{
// Server
while ((i = fork()) == -1);
if (!i)
{
// Server
SERVER();
}
// Client
while ((i = fork()) == -1);
if (!i)
{
// Client
CLIENT();
}
wait(0);
wait(0);
}
2.共享存储区的创建、附接和断接
#include <sys/types.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define SHMKEY 75
int shmid, i;
int* addr;
void CLIENT()
{
int i;
shmid = shmget(SHMKEY, 1024, 0777|IPC_CREAT); //获取共享区
addr = shmat(shmid, 0, 0); //共享区起始地址为addr
for(i = 9; i >= 0; i--)
{
while(*addr != -1);
printf("(client)sent: %d \n",i);
*addr = i;
}
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 %d \r\n",(*addr));
}while((*addr) != 0);
shmctl(shmid, IPC_RMID, 0);
exit(0);
}
int main()
{
while((i = fork()) == -1);
if(!i)
{
SERVER();
}
while((i = fork()) == -1);
if(!i)
{
CLIENT();
}
wait(0);
wait(0);
return 0;
}