进程间通信之共享内存实验报告,进程间通信之共享内存函数

  进程间通信之共享内存实验报告,进程间通信之共享内存函数

  共享内存是第二个IPC工具。他允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个运行的程序之间传输数据的有效手段。尽管X/Open标准并不要求这样做,但大多数共享内存实现可能会在同一个物理内存中的不同进程之间安排共享内存。

  共享内存为在多个进程间共享和传输数据提供了一种有效的方法。因为他没有提供同步方法,所以我们通常需要使用其他机制来同步对共享内存的访问。通常,我们可以使用共享内存来提供对大内存区域的有效访问,并发送少量消息来同步对该内存的访问。

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

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

  每个箭头显示了每个进程的逻辑地址空间到可用物理内存的映射。实际情况更复杂,因为可用内存是物理内存和交换到磁盘的内存的混合。

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

  #包含sys/shm.h

  void *shmat(int shm_id,const void *shm_addr,int shm flg);

  int shmctl(int shm_id,int cmd,struct sh mid _ ds * buf);

  int shm dt(const void * shm _ addr);

  int shmget(key_t key,size_t size,int shm flg);

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

  获取共享内存

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

  int shmget(key_t key,size_t size,int shm flg);

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

  第二个参数size以字节为单位指定所需的内存量。

  第三个参数shmflg由9个权限标记组成,它们的使用方式与用于创建文件的模型参数相同。IPC_CREAT定义了一个特殊的位,必须用permission标志对其进行bite或operated操作,才能创建一个新的共享内存段。设置IPC_CREAT标志并传递现有的共享内存段不是错误。否则,IPC_CREAT将被忽略。

  权限标签非常有用,因为它们允许创建共享内存,共享内存可以由所有者进程写入,但只能由其他用户创建的进程读取。我们可以应用这个特性,通过将数据放在共享内存中来提供对只读数据的有效访问,而不用担心数据被其他用户修改的风险。

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

  连接共享内存

  当我们第一次创建共享内存段时,它不能被任何进程访问。为了能够访问共享内存,我们必须将它与进程地址空间相关联。为此,我们可以使用shmat函数:

  void *shmat(int shm_id,const void *shm_addr,int shm flg);

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

  第二个参数shm_addr是与当前进程相关联的共享内存的位置。这个参数应该总是一个空指针,它允许系统选择内存出现的地址。

  第三个参数shmflg是一组位标记。两个可能的值是SHM_RND和SHM_RDONLY。前者结合shm_addr控制要关联的共享内存的地址;后者使相关的存储器成为只读的。很少需要控制相关存储器的地址;我们通常应该允许系统为我们选择一个地址,否则程序将变得高度依赖硬件。

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

  根据所有者(共享内存的创建者)、权限和当前进程的所有者,共享内存将具有读或写权限。共享内存上的权限类似于文件上的权限。

  shfglshm _ rdonly为true是此规则的一个例外。此时,共享内存是不可写的,尽管权限已经允许写访问。

  拆卸共享内存

  函数的作用是:将共享内存和当前进程分开。他传递一个指向shmat返回的地址的指针。如果成功,将返回0;如果失败,它将返回-1。请注意,分离共享内存不会删除它;他只是让当前进程无法使用内存。

  控制共享内存

  共享内存的控制功能比复杂的信号量控制功能简单得多:

  int shmctl(int shm_id,int command,struct sh mid _ ds * buf);

  Shmid_ds结构至少有以下成员:

  结构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

  #定义文本_SZ 2048

  结构共享使用

  {

  int written _ by _ you

  char some _ TEXT[TEXT _ SZ];

  };

  #endif

  该文件定义了消费者和生产者程序中使用的结构。当数据已经被写入结构的其他部分,并且我们认为我们需要传输2k文本时,我们使用_by_you编写的int标记来通知消费者。

  我们的第一个项目是面向消费者的。包含头文件后,我们通过调用shmget函数并指定IPC_CREAT位来创建一个共享内存段(共享内存结构的大小):

  #包含stdio.h

  #包含stdlib.h

  #包括unistd.h

  #包含字符串. h

  #包含sys/types.h

  #include sys/irc.h

  #包含sys/shm.h

  #包含 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 _ FAILURE);

  }

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

  shared_memory=shmat(shmid,(void *)0,0);

  if(shared_memory==(void *)-1)

  {

  fprintf(stderr, shmat failed/n );

  退出(退出_失败);

  }

  printf(内存附加在“%X/n”,(int)shared _ memroy);

  四程序的接下来部分将共享_记忆段赋给分享_素材,后者会输出你写的中的任何文本。程序继续循环直到你写的中的文本为结束。睡眠调用会强制消费者停留在其临界区中,这会使得生产者程序等待。

  shared _ stuff=(struct _ shared _ use _ ST *)shared _ memory;

  shared _ stuff-written _ by _ you=0;

  (跑步时)

  {

  if(shared _ stuff-write _ by _ you)

  {

  printf(你写了:%s ,shared _ stuff-some _ text);

  sleep(rand()% 4);

  shared _ stuff-written _ by _ you=0;

  if(strncmp(shared _ stuff-some _ text, end ,3)==0)

  {

  跑步=0;

  }

  }

  }

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

  if(shmdt(shared _ memory)=-1)

  {

  fprintf(stderr, shmdt failed/n );

  退出(退出_失败);

  }

  if(shmctl(shmid,IPC_RMID,0)==-1)

  {

  fprintf(stderr, shmctl(IPC_RMID)失败/n’);

  退出(退出_失败);

  }

  退出(退出_成功);

  }

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

  #包含标准视频

  #包含标准库

  #包括unistd.h

  #包含字符串。h

  #包含sys/types.h

  #包含sys/ipc.h

  #包含sys/shm.h

  #包含shm_com.h

  int main()

  {

  int run int=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 );

  退出(退出_失败);

  }

  shared_memory=shmat(shmid,(void *)0,0);

  if(shared_memory==(void *)-1)

  {

  fprintf(stderr, shmat failed/n );

  退出(退出_失败);

  }

  printf(内存附加在“%X/n”,(int)shared _ Memory);

  shared _ stuff=(struct shared _ use _ ST *)shared _ memory;

  (跑步时)

  {

  while(shared _ stuff-written _ by _ you==1)

  {

  睡眠(1);

  printf(正在等待客户端./n’);

  }

  printf(输入一些文本:);

  fgets(buffer,BUFSIZ,stdin);

  strncpy(shared_stuff- some_text,buffer,TEXT _ SZ);

  shared _ stuff-written _ by _ you=1;

  if(strncmp(buffer, end ,3)==0)

  {

  跑步=0;

  }

  }

  if(shmdt(shared _ memory)=-1)

  {

  fprintf(stderr, shmdt failed/n );

  退出(退出_失败);

  }

  退出(退出_成功);

  }

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

  $ ./shm1

  [1] 294

  内存连接在40017000

  $ ./shm2

  内存连接在40017000

  输入一些文本:你好

  你写道:你好

  等待客户.

  等待客户.

  输入一些文本:Linux!

  你写的:Linux!

  等待客户.

  等待客户.

  等待客户.

  输入一些文本:结束

  你写道:结束

  $

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

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

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

进程间通信之共享内存实验报告,进程间通信之共享内存函数