你的位置:首页 > 操作系统

[操作系统]深入理解进程间通信之共享内存


  共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。是针对其他通信机制运行效率较低而设计的。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。

  采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。对于像管道和消息队列等通信方式,则需要在内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次数据[1]:一次从输入文件到共享内存区,另一次从共享内存区到输出文件。实际上,进程之间在共享内存时,并不总是读写少量数据后就解除映射,有新的通信时,再重新建立共享内存区域。而是保持共享区域,直到通信完毕为止,这样,数据内容一直保存在共享内存中,并没有写回文件。共享内存中的内容往往是在解除映射时才写回文件的。因此,采用共享内存的通信方式效率是非常高的。

系统V共享内存原理

  进程间需要共享的数据被放在一个叫做IPC共享内存区域的地方,所有需要访问该共享区域的进程都要把该共享区域映射到本进程的地址空间中去。系统V共享内存通过shmget获得或创建一个IPC共享内存区域,并返回相应的标识符。内核在保证shmget获得或创建一个共享内存区,初始化该共享内存区相应的shmid_kernel结构体的同时,还将在特殊文件系统shm中,创建并打开一个同名文件,并在内存中建立起该文件的相应dentry及inode结构,新打开的文件不属于任何一个进程(任何进程都可以访问该共享内存区)。所有这一切都是系统调用shmget完成的。

注:每一个共享内存区都有一个控制结构struct shmid_kernel,shmid_kernel是共享内存区域中非常重要的一个数据结构,它是存储管理和文件系统结合起来的桥梁,定义如下:

struct shmid_kernel /* private to the kernel */{    struct kern_ipc_perm shm_perm; /* operation permission structure */  struct file *shm_file; /* pointer in kernel */  unsigned long shm_nattch; /* number of current attaches */  unsigned long shm_segsz; /* size of segment in bytes */  time_t shm_atim; /* last-attach time */  time_t shm_dtim; /* last-detach time */  time_t shm_ctim; /* last-change time */  pid_t shm_cprid; /* pid of creator */  pid_t shm_lprid; /* pid of last shmop() */};

 

系统V共享内存API

  头文件:
    #include <sys/ipc.h>
    #include <sys/shm.h>

  shmget()用来获得共享内存区域的ID,如果不存在指定的共享区域就创建相应的区域。shmat()把共享内存区域映射到调用进程的地址空间中去,这样,进程就可以方便地对共享区域进行访问操作。shmdt()调用用来解除进程对共享内存区域的映射。shmctl实现对共享内存区域的控制操作。

系统V共享内存限制

  在/proc/sys/kernel/目录下,记录着系统V共享内存的一下限制,如一个共享内存区的最大字节数shmmax,系统范围内最大共享内存区标识符数shmmni等,可以手工对其调整,但不推荐这样做。

系统V共享内存范例

/***** testwrite.c *******/#include <sys/ipc.h>#include <sys/shm.h>#include <sys/types.h>#include <unistd.h>typedef struct{  char name[4];  int age;} people;main(int argc, char** argv){  int shm_id,i;  key_t key;  char temp;  people *p_map;  char* name = "/dev/shm/myshm2";  key = ftok(name,0);  if(key==-1)    perror("ftok error");  shm_id=shmget(key,4096,IPC_CREAT);    if(shm_id==-1)  {    perror("shmget error");    return;  }  p_map=(people*)shmat(shm_id,NULL,0);  temp='a';  for(i = 0;i<10;i++)  {    temp+=1;    memcpy((*(p_map+i)).name,&temp,1);    (*(p_map+i)).age=20+i;  }  if(shmdt(p_map)==-1)    perror(" detach error ");}/********** testread.c ************/#include <sys/ipc.h>#include <sys/shm.h>#include <sys/types.h>#include <unistd.h>typedef struct{  char name[4];  int age;} people;main(int argc, char** argv){  int shm_id,i;  key_t key;  people *p_map;  char* name = "/dev/shm/myshm2";  key = ftok(name,0);  if(key == -1)    perror("ftok error");  shm_id = shmget(key,4096,IPC_CREAT);    if(shm_id == -1)  {    perror("shmget error");    return;  }  p_map = (people*)shmat(shm_id,NULL,0);  for(i = 0;i<10;i++)  {  printf( "name:%s\n",(*(p_map+i)).name );  printf( "age %d\n",(*(p_map+i)).age );  }  if(shmdt(p_map) == -1)    perror(" detach error ");}

View Code