操作系统 通过共享内存进行进程间通信
共享内存是两个或多个进程之间共享的内存。每个进程都有自己的地址空间;如果任何一个进程希望将自己地址空间中的某些信息与其他进程进行通信,那么只有通过进程间通信(IPC)技术才能实现。
共享内存是最快的进程间通信机制。操作系统将一个内存段映射到多个进程的地址空间中,从而可以在该内存段中进行读写操作,而无需调用操作系统函数。
对于交换大量数据的应用程序来说,共享内存比消息传递技术(如消息队列)要优越得多,后者需要每次数据交换都进行系统调用。为了使用共享内存,我们需要执行两个基本步骤:
- 向操作系统请求一个可以在进程之间共享的内存段。
- 将该内存段的一部分或整个内存与调用进程的地址空间关联起来。
共享内存段是由多个进程共享的物理内存部分。在该区域中,进程可以设置结构,其他进程可以对其进行读写。当在两个或多个进程中建立共享内存区域时,不能保证这些区域将被放置在相同的基地址上。在需要同步时,可以使用信号量。
例如,一个进程的共享区域可能从地址0x60000开始,而另一个进程使用0x70000。重要的是要理解,这两个地址引用了完全相同的数据片段。因此,在第一个进程的地址0x60000存储数字1意味着第二个进程在0x70000处具有值为1的数据。这两个不同的地址引用了完全相同的位置。
为什么选择通过共享内存进行进程间通信
通常,相关的进程通信是使用管道或命名管道来执行的。而无关的进程通信可以使用命名管道或通过流行的共享内存和消息队列的IPC技术来执行。
但是,管道、FIFO和消息队列的问题在于两个进程之间的信息交换是通过内核进行的,工作方式如下:
- 服务器从输入文件中读取数据。
- 服务器使用管道、FIFO或消息队列将这些数据写入消息。
- 客户端从IPC通道读取数据,再将数据从内核的IPC缓冲区复制到客户端的缓冲区。
- 最后,将数据从客户端的缓冲区复制出来。
总共需要四次数据复制(2次读取和2次写入)。因此,共享内存通过允许两个或更多进程共享一个内存段来提供一种方式。使用共享内存只需将数据从输入文件复制两次,一次是从输入文件复制到共享内存,一次是从共享内存复制到输出文件。
使用共享内存进行IPC的功能
使用共享内存进行IPC的两个函数 shmget() 和 shmat() 。shmget()函数用于创建共享内存段,而shmat()函数用于将共享段附加到进程的地址空间。
1. shmget()函数
第一个参数指定了唯一的标识共享段的数字(称为键)。第二个参数是共享段的大小,例如1024字节或2048字节。第三个参数指定了共享段的许可。成功时,shmget()函数返回一个有效的标识符,而失败时返回-1。
语法
#include
#include
int shmget (key_t key, size_t size, int shmflg);
2. shmat()函数
shmat()函数用于将由shmid指定的共享内存标识符所创建的共享内存段连接到调用进程的地址空间。第一个参数是shmget()函数在成功时返回的标识符。第二个参数是要连接到调用进程的地址的位置。第二个参数为NULL表示系统将自动选择一个合适的地址。第三个参数如果第二个参数为NULL,则为’0’。否则,该值由SHM_RND指定。
语法
#include
#include
void *shmat(int shmid, const void *shmaddr, int shmflg);
IPC使用共享内存如何工作
进程使用shmget()
创建一个共享内存段。共享内存段的原始所有者可以使用shmctl()
将所有权分配给另一个用户。它还可以撤销此分配。具有适当权限的其他进程可以使用shmctl()
对共享内存段执行各种控制功能。
一旦创建,可以使用shmat()
将共享段连接到进程地址空间。可以使用shmdt()
将其分离。连接的进程必须具有shmat()
的适当权限。连接后,进程可以按照连接操作中请求的权限读取或写入段。同一进程可以多次连接相同的共享段。
共享内存段由一个具有唯一ID并指向物理内存区域的控制结构描述。段的标识符称为shmid
。共享内存段控制结构和原型的结构定义可以在<sys/shm.h>
中找到。
示例
我们将编写两个使用共享内存进行IPC的程序作为示例。程序1将创建共享段,将其连接并在其中写入一些内容。然后程序2将连接至共享段并读取程序1写入的值。
程序1: 该程序创建一个共享内存段,将其连接并将一些内容写入共享内存段。
#include
#include
#include
#include
#include
int main()
{
int i;
void *shared_memory;
char buff[100];
int shmid;
shmid=shmget((key_t)2345, 1024, 0666|IPC_CREAT);
//creates shared memory segment with key 2345, having size 1024 bytes. IPC_CREAT is used to create the shared segment if it does not exist. 0666 are the permissions on the shared segment
printf("Key of shared memory is %d\n",shmid);
shared_memory=shmat(shmid,NULL,0);
//process attached to shared memory segment
printf("Process attached at %p\n",shared_memory);
//this prints the address where the segment is attached with this process
printf("Enter some data to write to shared memory\n");
read(0,buff,100); //get some input from user
strcpy(shared_memory,buff); //data written to shared memory
printf("You wrote : %s\n",(char *)shared_memory);
}
结果
Key of shared memory is 0
Process attached at 0x7ffe040fb000
Enter some data to write to shared memory
Hello World
You wrote: Hello World
它是如何工作的?
在上述程序中, shmget() 函数创建了一个大小为1024字节,键为2345的段,并为所有用户设置了读写权限。它返回段的标识符,该标识符存储在 shmid 中。这个标识符在 shmat() 中被用来将共享段附加到进程的地址空间中。
shmat() 中的NULL意味着操作系统会自动将共享段附加到这个进程的适当地址。然后,使用 read() 系统调用从用户那里读取一些数据,并最终使用 strcpy() 函数将其写入共享段。
程序2: 该程序将自己附加到程序1中创建的共享内存段,并读取共享内存的内容。
#include
#include
#include
#include
#include
int main()
{
int i;
void *shared_memory;
char buff[100];
int shmid;
shmid=shmget((key_t)2345, 1024, 0666);
printf("Key of shared memory is %d\n",shmid);
shared_memory=shmat(shmid,NULL,0); //process attached to shared memory segment
printf("Process attached at %p\n",shared_memory);
printf("Data read from shared memory is : %s\n",(char *)shared_memory);
}
输出
Key of shared memory is 0
Process attached at 0x7f76b4292000
Data read from shared memory is: Hello World
它是如何工作的?
在这个程序中,shmget()函数生成了与程序1中创建的相同段的标识符。请记住给出相同的键值。唯一的变化是不要写IPC_CREAT,因为共享内存段已经创建。接下来,shmat()函数将共享段附加到当前进程。
之后,从共享段打印数据。在输出中,您将看到执行程序1时写入的相同数据。