【实验目的】
1、了解和熟悉共享存储机制。
2、学会用共享存储区方法进行通信。
实验要求:了解熟悉LINUX支持的共享存储区机制。
【实验内容】
1、编制一长度为 1k 的共享存储区发送和接收的程序。
2、 编程在主进程中创建两个子进程,在子进程 shmw()中创建一个系统 V 共享内存区,并在其中写入格式化数据;在子进程 shmr()中访问同一个系统 V 共享内存区,读出其中的格式化数据。
【实验环境】(含主要设计设备、器材、软件等)
Pc电脑一台,虚拟机中LINUX环境
【实验步骤、过程】(含原理图、流程图、关键代码,或实验过程中的记录、数据等)
1.使用 shmget 函数创建或访问一个共享内存段,通过 SHMKEY 指定共享内存的键值。如果共享内存段不存在,则创建一个新的共享内存段。
在 server 函数中,通过 shmat 函数将共享内存段连接到当前进程的地址空间。然后通过一个 do-while 循环,不断等待并接收来自客户端的数据。当客户端写入数据后,将数据打印出来。
在 client 函数中,同样通过 shmat 函数将共享内存段连接到当前进程的地址空间。然后通过一个循环,向共享内存写入数据(递减的整数),并在写入后将共享内存中的标志设为 -1 表示已写入数据。当写入值为-1时,退出循环。
在 main 函数中,使用 fork 创建两个子进程。一个子进程执行 server 函数,另一个子进程执行 client 函数。父进程通过 wait 等待两个子进程的结束。
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#define SHMKEY 75
int shmid,i; int *addr;
void client( )
{ int i;
shmid=shmget(SHMKEY,1024,0777);
addr=shmat(shmid,0,0);
for (i=9;i>=0;i--)
{
while (*addr!=-1);
printf("(client) sent\n");
*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\n");
}while (*addr);
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);
}
2.使用 shmget 函数创建一个共享内存段,通过 shmat 函数将共享内存连接到当前进程的地址空间。共享内存的结构包括一个整数 ber 和一个字符数组 text。
在 shmw 函数中,向共享内存写入一个整数值123和一个字符串 "This is a message from shmw.",然后使用 shmdt 函数分离共享内存,退出进程。
在 shmr 函数中,通过 shmat 函数将共享内存连接到当前进程的地址空间,读取共享内存中的整数值和字符串,然后打印输出。接着使用 shmdt 函数分离共享内存,并使用 shmctl 函数删除共享内存段。
在 main 函数中,通过两次 fork 创建两个子进程。第一个子进程执行 shmw 函数,向共享内存写入数据。第二个子进程稍微延迟一秒后执行 shmr 函数,读取共享内存中的数据。父进程通过 wait 等待两个子进程的结束.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#define SHMKEY 75
int shmid,*ber;
char *text;
void shmw()
{
shmid = shmget(SHMKEY, 1024, 0777 | IPC_CREAT);
ber = (int *)shmat(shmid, 0, 0);
text = (char *)(ber + 1);
*ber = 123;
strcpy(text, "This is a message from shmw.");
shmdt(ber);
exit(0);
}
void shmr()
{
shmid = shmget(SHMKEY, 1024, 0777);
ber = (int *)shmat(shmid, 0, 0);
text = (char *)(ber + 1);
printf("Received number: %d\n", *ber);
printf("Received text: %s\n", text);
shmdt(ber);
shmctl(shmid, IPC_RMID, 0);
exit(0);
}
int main()
{
int pid1,pid2;
while((pid1 = fork())==-1);
if (pid1 == 0)
{
shmw();
}
while((pid2 = fork())==-1);
if (pid2 == 0)
{
sleep(1);
shmr();
}
wait(0);
wait(0);
return 0;
}
扩展:使用了共享内存来实现一个服务端和多个客户端之间的简单进程通信。
定义了一个包含客户端ID和消息的结构体,用于在服务端和客户端之间共享信息。
server 函数:获取共享内存标识符和连接到共享内存段。进入一个无限循环,等待接收来自客户端的消息。当客户端发送消息时,检查共享内存中的 client_id 是否为0,表示有新消息。如果有新消息,打印接收到的消息内容。
重置共享内存中的 client_id 和消息内容,准备接收下一条消息。处理完消息后休眠2秒,模拟处理过程。
client 函数:获取共享内存标识符和连接到共享内存段。进入一个无限循环,不断检查服务端是否空闲。如果服务端空闲,设置客户端ID和消息内容,并通知服务端有新消息。
main 函数:创建子进程服务端pid1。在父进程中,创建多个客户端进程pid2。等待所有子进程结束。
#include <sys/types.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define SHMKEY 75
struct SharedMemory {
int client_id;
char message[100];
};
int shmid;
struct SharedMemory *sm;
void server()
{
shmid = shmget(SHMKEY, sizeof(struct SharedMemory), 0777 | IPC_CREAT);
sm = (struct SharedMemory *)shmat(shmid, 0, 0);
printf("Server is running...\n");
while (1)
{
while (sm->client_id == 0)
sleep(1);
printf("Server received message:%s\n",sm->message);
sm->client_id = 0;
sm->message[0] = '\0';
sleep(2);
}
shmdt(sm);
exit(0);
}
void client(int client_id)
{
shmid = shmget(SHMKEY, sizeof(struct SharedMemory), 0777);
sm= (struct SharedMemory *)shmat(shmid, 0, 0);
printf("Client %d is running...\n", client_id);
while (1)
{
if (sm->client_id == 0)
{
sm->client_id = client_id;
sprintf(sm->message,"Hello from client %d!", client_id);
}
}
shmdt(sm);
exit(0);
}
int main() {
int pid1, pid2, client_count = 3;
while((pid1 = fork())==-1);
if (pid1 == 0)
server();
else
{
for (int i = 1; i <= client_count; ++i)
{
while((pid2 = fork())==-1);
if (pid2 == 0)
client(i);
}
for (int i = 0; i < client_count + 1; ++i)
wait(0);
}
}
【实验结果或总结】(对实验结果进行相应分析,或总结实验的心得体会,并提出实验的改进意见)
1.交替打印出 “(client) sent” 和 “(server) received”,这表明client成功地将消息写入到共享内存,server成功地从共享内存中读取了消息。client客户端在每次循环中等待服务器将 *addr 设置为-1,然后发送一条消息到共享内存中。server收到消息,然后进行处理。一直循环,直到遇到的值为 0,则视为为结束信号,取消该队列。
2.shnw函数在共享内存中写入了一个整数值123和一条字符串消息,shnr函数从共享内存中读取了先前写进程写入的整数值和字符串消息,并在标准输出中显示了这两个值。所以整数123和字符串“This is a message from shmw.”成功被读取并打印。
3.(扩展)服务器进程通过共享内存检测客户端是否有消息发送过来,如果有,则输出接收到的消息。服务器会不断轮询检查是否有客户端发送消息,并在接收到消息后清空消息内容。由于 sleep(2) 的存在,服务器每隔2秒输出一次消息。每个客户端进程都会输出相应的提示信息,表示客户端正在运行。因为没有设置退出条件,因此进程会一直运行,需要使用系统的中断信号退出。
此次实验,主要掌握消息传递的思想和原理,同时也要了解一些在消息的发送与接收中经常用到的函数,其中一些函数在理论课上的三大经典问题实现互斥访问的时候需要用到,通过此次实验,让我对这些函数的用法和消息的发送与接收有了进一步的理解和思考。