`
mylxiaoyi
  • 浏览: 327515 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

进程间通信之共享内存

阅读更多

共享内存

共享内存是第二种IPC工具。他允许两个无关的进程访问相同的逻辑内存。共享内存是在两个运行的程序之间传递数据的有效手段。尽管X/Open标准并没有要求,很可能绝大数的共享内存实现都是会将不同进程之间正在共享的内存安排在相同的物理内存中。

共享内存为在多个进程之间共享与传递数据提供一个有效的手段。因为他并没有提供同步的方法,所以通常我们需要使用其他的机制来同步对共享内存的访问。通常,我们也许会使用共享内存来提供对大块内存区的有效访问,并且传递少量的消息来同步对此内存的访问。

共享内存是由IPC为一个进程所创建并且出现在这个进程的地址空间中的一段特殊的地址序列。其他的进程可以将同样的共享内存段关联到他们自己的地址空间中。所有的进程都可以访问这段内存地址,就如同这段内存是由malloc所分配的。如果一个进程写入共享内存,这些改变立即就可以为访问相同共享内存的其他进程所见。

就其自身而言,共享内存并没有提供任何共享方法。并没有自动的方法来阻止在第一个进程完成写入共享内存之前第二个进程开始读取共享内存。同步访问是程序员的责任。图14-2显示共享内存是如何工作的。

每一个箭头显示的是每一个进程的逻辑地址空间到可用的物理内存的映射。实际的情形要更为复杂,因为可用的内存是由物理内存与交换到磁盘上的内存混合构成的。

用于共享内存的函数如下:

#include <sys/shm.h>
void *shmat(int shm_id, const void *shm_addr, int shmflg);
int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
int shmdt(const void *shm_addr);
int shmget(key_t key, size_t size, int shmflg);

与信号量相类似,通常需要在包含shm.h文件之前包含sys/types.h与sys/ipc.h这两个头文件。

shmget

我们使用shmget函数创建共享内存:

int shmget(key_t key, size_t size, int shmflg);

与信号量相类似,这个函数也提供了key,这可以有效的命名共享内存段,而且shmget函数会返回一个共享内存标识符,这个标识符可以用于后续的共享内存函数中。还有一个特殊的关键值,IPC_PRIVATE,这可以创建进程私有的共享内存。我们通常并不会使用这个值,而且与信号量相类似,我们会发现私有的共享内存在许多Linux系统上实际上并不是私有的。

第二个参数,size,以字节形式指定了所需要的内存数量。

第三个参数,shmflg,是由9个权限标记所组成的,这些标记的使用与用于创建文件的模型参数相同。IPC_CREAT定义了一个特殊位,必须与权限标记进行位或操作来创建一个新的共享内存段。设置IPC_CREAT标记并且传递一个已经存在的共享内存段并不是错误。如果不需要,IPC_CREAT只是简单的被忽略掉。

权限标记是十分有用的,因为这些权限标记可以允许创建共享内存所有者进程可以写入而其他用户所创建的进程只能读取的共享内存。我们可以应用这个特点通过将数据放入共享内存中,从而提供对于只读数据的有效访问,而不必担心数据被其他用户修改的风险。

如果共享内存成功创建,shmget会返回一个非负整数,共享内存标识符。如果失败,则会返回-1。

shmat

当我们第一次创建一个共享内存段时,他并不能为任何进程所访问。为了能够访问共享内存,我们必须将其与一个进程地址空间关联到一起。我们可以使用shmat函数来达到这一目的:

void *shmat(int shm_id, const void *shm_addr, int shmflg);

第一个参数,shm_id,是由shmget函数所返回的共享内存标识符。

第二个参数,shm_addr,是将要关联到当前进程的共享内存所在的位置。这个参数应总是一个空指针,从而可以允许系统来选择内存出现的地址。

第三个参数,shmflg,是一个位标记集合。两个可能的值为SHM_RND与SHM_RDONLY。前者与shm_addr联合,控制将被关联的共享内存所在的地址;而后者使得关联的内存只读。通常很少需要来控制被关联的内存所在的地址;我们通常应允许系统来为我们选择一个地址,否则就会使得程序变得高度硬件相关。

如果shmat调用成功,他会返回一个指向共享内存第一字节的指针。如果失败,则会返回-1。

共享内存将会依据所有者(共享内存的创建者),权限与当前进程的所有者而具有读或写权限。共享内存上的权限与文件上的权限相类似。

shmfgl & SHM_RDONLY为真的情况是这个规则的一个例外。此时共享内存并不可写,尽管权限已经允许了写访问。

shmdt

shmdt函数将共享内存与当前进程相分离。他传递一个指向由shmat所返回的地址的指针。如果成功,则会返回0;如果失败,则会返回-1。注意,分离共享内存并不会删除他;他只是使得内存对于当前进程不可用。

shmctl

共享内存的控制函数要比复杂的信号量控制函数简单得多:

int shmctl(int shm_id, int command, struct shmid_ds *buf);

shmid_ds结构至少具有下列成员:

struct shmid_ds {
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
}

第一个参数,shm_id,是由shmget所返回的标记符。

第二个参数,command,是要执行的动作。他可以有三个值:

命令 描述
IPC_STAT 设置shmid_ds结构中的数据反射与共享内存相关联的值。
IPC_SET 如果进程有相应的权限,将与共享内存相关联的值设置为shmid_ds数据结构中所提供的值。
IPC_RMID 删除共享内存段。

第三个参数,buf,是一个指向包含共享内存模式与权限的结构的指针。

如果成功,则返回0,如果失败,则会返回-1。X/Open并没有说明如果我们尝试删除一个已经关联的共享内存时会发生什么。通常,一个已经关联但是被删除的共享内存通常会继续发挥作用,直到他与最后一个进程相分离。然而,因为这个行为并没有被规范,所以最好不要依赖于他。

试验--共享内存


现在我们已经了解了共享内存函数,我们可以编写一些代码来使用这些函数。我们将会编写一对程序,shm1.c与shm2.c。第一个程序(消费者)将会创建一个共享内存段并且显示写入共享内存中的数据。第二个程序(生产者)将会关联已经存在的共享内存段并且允许我们进入内存段中的数据。

1 首先,我们创建一个通用头文件来描述我们希望传递的共享内存。我们将其命名为shm_com.h。

#ifndef _SHM_COM_H
#define _SHM_COM_H 1

#define TEXT_SZ 2048

struct shared_use_at
{
int written_by_you;
char some_text[TEXT_SZ];
};

#endif

这个文件定义了在消费者程序与生产者程序中都会用到的结构。当数据已经写入结构的其他部分并且认为我们需要传送2k文本时,我们使用一个int标记written_by_you来通知消费者。

2 我们的第一个程序用于消费者。在包含头文件之后,我们通过调用shmget函数,指定IPC_CREAT位来创建一个共享内存段(我们共享内存结构的大小):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <sys/types.h>
#include <sys/irc.h>
#include <sys/shm.h>

#include "shm_com.h"

int main()
{
int running = 1;
void *shared_memory = (void *)0;
struct shared_use_st *shared_stuff;
int shmid;

srand((unsigned int) getpid());

shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);

if(shmid == -1)
{
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
}

3 我们现在使用共享内存可以为程序所访问:

shared_memory = shmat(shmid, (void *)0, 0);
if(shared_memory == (void *)-1)
{
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}

printf("Memory attached at %X\n", (int)shared_memroy);

4 程序的接下来部分将shared_memroy段赋给shared_stuff,后者会输出written_by_you中的任何文本。程序继续循环直到written_by_you中的文本为end。sleep调用会强制消费者停留在其临界区中,这会使得生产者程序等待。

shared_stuff = (struct_shared_use_st *)shared_memory;
shared_stuff->written_by_you = 0;
while(running)
{
if(shared_stuff->written_by_you)
{
printf("You wrote: %s", shared_stuff->some_text);
sleep(rand() % 4);
shared_stuff->written_by_you = 0;
if(strncmp(shared_stuff->some_text, "end", 3)==0)
{
running = 0;
}
}
}

5 最后共享内存被分离并被删除:

if(shmdt(shared_memory)==-1)
{
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}

if(shmctl(shmid, IPC_RMID, 0)==-1)
{
fprintf(stderr, "shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}

exit(EXIT_SUCCESS);
}

6 我们的第二个程序,shm2.c,是生产者程序;他允许我们进入消费者的数据。这个程序与shm1.c程序十分相似:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#include "shm_com.h"

int main()
{
int runnint = 1;
void *shared_memory = (void *)0;
struct shared_use_st *shared_stuff;
char buffer[BUFSIZ];
int shmid;

shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);

if(shmid == -1)
{
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
}

shared_memory = shmat(shmid, (void *)0, 0);
if(shared_memory == (void *)-1)
{
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}

printf("Memory attached at %X\n", (int)shared_memory);

shared_stuff = (struct shared_use_st *)shared_memory;

while(running)
{
while(shared_stuff->written_by_you == 1)
{
sleep(1);
printf("waiting for client...\n");
}

printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);

strncpy(shared_stuff->some_text, buffer, TEXT_SZ);
shared_stuff->written_by_you = 1;

if(strncmp(buffer, "end", 3) == 0)
{
running = 0;
}
}

if(shmdt(shared_memory) == -1)
{
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}

exit(EXIT_SUCCESS);
}

当我们运行这些程序,我们会得到下面的输出:

$ ./shm1 &
[1] 294
Memory attached at 40017000
$ ./shm2
Memory attached at 40017000
Enter some text: hello
You wrote: hello
waiting for client...
waiting for client...
Enter some text: Linux!
You wrote: Linux!
waiting for client...
waiting for client...
waiting for client...
Enter some text: end
You wrote: end
$

工作原理


第一个程序,shm1,创建共享内存段并其关联到他的地址空间。我们在共享内存的第一部分揭示了shared_use_st结构。这个结构有一个标记,written_by_you,当数据可用时会设置这个标记。当设置了这个标记时,程序会读取文本,输出文本,并且清除标记来表示程序已经读取数据了。我们使用一个特殊的字符串,end,来进行由循环中的退出。程序然后分离共享内存并且删除他。

第二个程序,shm2,获得并关联共享内存段,因为他使用相同的键值,1234。然后他提示用户输入一些文本。如果设置了written_by_you标记,shm2就会知道客户端程序还没有读取前面输入的数据并且进行等待。当其他进程清除了这个标记,shm2会写入新的数据并且设置标记。他也使用字符串end来结束并分离共享内存段。

注意,我们必须提供我们自己的,相当粗糙的同步标记,written_by_you,这会导致一个低效的忙等待。在实际的程序中,我们会传递一个消息,或者使用管道,或者使用IPC消息(我们会在稍后讨论),生成信息,或是使用信号量来在程序的读取与写入部分提供同步。

分享到:
评论

相关推荐

    C#进程间通信之共享内存

    本文将重点探讨在C#环境中如何利用共享内存进行进程间通信,这对于理解和优化多进程应用的性能至关重要。 共享内存是一种高效的IPC机制,它允许多个进程直接读写同一块内存区域,而无需通过任何中间媒介。在C#中,...

    进程间通信之共享内存 shared memory 完整代码

    进程间通信之共享内存 shared memory ) 1 效率最高 2 存在竞态 七种进程间通信方式: 一 无名管道( pipe ) 二 有名管道( fifo ) 三 共享内存 shared memory 四 信号 sinal 五 消息队列 message queue ) ...

    Windows进程间通信之共享内存

    共享内存方式实现进程间通信。详细看我的博客:http://blog.csdn.net/pengguokan/article/details/8921346

    C# 进程间通信 共享内存

    共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。...1、进程间通信 2、共享内存 3、托管方式与非托管共享内存

    进程间通信之共享内存C#源代码

    在C#中,有多种进程间通信(Inter-Process Communication,IPC)的技术。以下是一些主要的通信方式: 1.命名管道(Named Pipes) 2.套接字(Sockets) 3.共享内存(Shared Memory) 4.信号量(Semaphore) 5.消息...

    Linux进程间通信之共享内存

    Linux进程间通信之共享内存.适用于任意两个进程间,本程序为基本模型,实现了共享内存.

    C#进程间通信-共享内存代码实例

    在C#中,实现进程间通信有多种方法,如管道、套接字、消息队列等,而共享内存是其中一种高效且直接的方式。本篇文章将深入探讨C#中的共享内存实现,并通过一个具体的代码实例来阐述其工作原理。 共享内存是一种让多...

    进程间通信

    七种进程间通信方式: 一....进程间通信之共享内存 shared memory 进程间通信之信号 sinal 进程间通信之消息队列 ( message queue ) 进程间通信之信号量( semophore ) 进程间通信之套接字( socket )

    进程间通信_共享内存

    用于进程间通信,使用共享内存方法。内部的共享内存大小的结构体什么的需要自己定义。

    VC用共享内存实现进程间通信

    C++ 用共享内存实现进程间通信 所用IDE为VS2003

    共享内存的进程间通信

    共享内存是一种高效的进程间通信(IPC)机制,它允许不同的进程访问同一块内存空间,从而实现数据的快速交换。在Windows环境下,如VC6.0这样的开发工具可以用来编写和测试这种通信方式的代码。 首先,我们要理解...

    linux无亲缘关系进程间通信(互斥锁+条件变量+共享内存)

    本示例中提到的“linux无亲缘关系进程间通信”着重讲解了如何利用互斥锁(Mutex)、条件变量(Condition Variable)以及共享内存(Shared Memory)这三种机制来实现非父子进程间的同步通信。 1. **互斥锁**:互斥锁...

    C++共享内存进程间通信 demo

    在提供的"共享内存样用于进程间通信"文件中,我们可以期待看到如何在C++代码中具体实现上述步骤的示例。这个示例将帮助开发者理解如何在实践中应用File Mapping进行进程间的高效通信。 总之,C++中的File Mapping是...

    c#共享内存demo-两个进程间通过共享内存实现通信

    分为3个工程:proA,proB,ShareMemLib 其中proA,proB是用wpf写的两个模拟进程程序 ShareMemLib将共享内存代码封装成lib,定义了发送者和监听者 两个进程在实例化Share...至此成功完成了两个进程间通过共享内存实现通信

    Linux环境进程间通信(五) 共享内存(上)

    Linux环境进程间通信(五) 共享内存(上)

    Qt共享内存实现进程间通信(ShareMemory).zip

    Qt共享内存实现进程间通信(QSharedMemory) 源代码

    linux讲解通过共享内存实现进程间的通信

    ### Linux共享内存实现进程间通信详解 #### 一、引言 在Linux系统中,进程间的通信(Inter-Process Communication, IPC)是一项重要的技术,它允许不同进程之间交换数据和同步执行。其中一种高效的进程间通信方法是...

    linux下C语言编程4-使用共享内存实现进程间通信.doc

    Linux 下 C 语言编程使用共享内存实现进程间通信 共享内存是 Linux 操作系统中的一种进程间通信机制,它允许不同的进程访问同一个内存区域,从而实现进程间的数据交换。在 C 语言中,使用共享内存可以通过 shmget、...

    使用共享内存及信号量实现进程间通信例子

    在计算机系统中,进程间通信(IPC,Inter-Process Communication)是多个进程协同工作时必不可少的一部分。本示例代码着重于使用共享内存和信号量来解决进程间的通信和同步问题,这是一种高效且灵活的方法,特别是在...

Global site tag (gtag.js) - Google Analytics