linux进程间通信—共享内存

共享内存:
(一)共享内存时进程间通信方式最简单之一,共享内存允许两个及两个以上的进程访问同一块内存。就如同malloc()函数向不同进程返回了指向同一个物理内存区域的指针,当一个进程改变这个指针所指向的内容时,其他进程也会察觉到这个改变。
(二)共享内存的特点:
1.共享内存时进程间共享数据的一种最快的方法。当一个进程向共享内存区域写入了数据,共享个内存区域的其他进程就可以立即查看其中的内容。
2.使用共享内存要注意多个进程堆一个存储区访问的互斥。如果一个进程向共享内存去写数据,则在它做完这一步操作前,别的进程不应当去读,写这些数据。
(三)常用函数
1.创建共享内存
(1)所需头文件

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

(2)int shmget(key_t key, size_t size, int shmflg);//功能时创建或者打开一块共享内存区
(3)参数:

  • key:进程间通信键值,ftok()的返回值
  • size:该共享内存区的长度(字节)
  • shmflg:标识函数的行为及共享内存的权限,取值如下
  • IPC_CREAT:如果不存在就创建
  • IPC_EXCL:如果已经存在则返回失败
  • 位或权限位:共享内存位或权限位后可以设置共享内存的访问权限
  • 返回:
  • 成功:共享内存标识符
  • 失败:-1
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define BUFSZ 1024

int main()
{
  int shmid;
  key_t key;

  key = ftok("./",2019);
  if(key == -1)
  {
    perror("ftok error");
  }
  
  shmid = shmget(key,BUFSZ,IPC_CREAT|0666);
  if(shmid<0)
  {
    perror("shmget error");
    exit(-1);
  }
  return 0;
}

可以看到多了一个共享内存区
linux进程间通信—共享内存
2.共享内存映射
(1)所需头文件:

#include <sys/type.h>
#include <sys/shm.h>

(2)void* shmat(int shmid,const void* shmaddr,int shmflg);//将一个共享内存段映射到调用进程的数据段中。即让进程和共享内存建立一种联系,让进程某个指针指向共享内存
(3)参数

  • shmid:共享内存标识符,shmget()的返回值

  • shmaddr:共享内存映射地址,一般使用NULL

  • shmflg:共享内存段的访问权限和映射条件(通常为0)具体取值如下:

  • 0:共享内存具有可读可写权限

  • SHM_RDONLY:只读

  • SHM_RND:(shmaddr非空才有效)

(4)返回值:

  • 成功:共享内存映射段地址(相当于这个指针就指向次共享内存)
  • 失败:-1
    3.解除共享内存映射
    (1)所需头文件
#include <sys/type.h>
#include <sys/shm.h>

(2)int shmdt(const void* shmaddr);//将共享内存和当前进程分离,但不删除共享内存,相当于让之前的指向此共享内存的指针不指向这里。
(3)参数:

  • shmaddr:共享内存映射地址
    (4)返回值:
  • 成功:0
  • 失败:-1
    4.共享内存控制
    (1)所需的头文件:
#include <sys/ipc.h>
#include <sys/shm.h>

(2)int shmctl(int shmid,int cmd,struct shmid_ds* buf);//共享内存属性的控制
(3)参数

  • shmid:共享内存标识符
  • cmd:函数功能的控制,其取值如下:
  • IPC_RMID:删除(常用)。
  • IPC_SET:设置shmid_ds参数,相当于把共享内存原来的属性值替换为buff里的属性值。
  • IPC_STAT:保存shmid_ds参数,把共享内存原来的属性值备份到buf里。
  • SHM_LOCK:锁定共享内存段(超级用户);用于锁定内存,禁止内存交换,并不代表共享内存被锁定后禁止与其他进程建立联系,真正的意义是:被锁定的内存不允许被交换到虚拟内存中,这样做的优势在于让共享内存一直处于内存中,从而提高程序性能。
  • SHM_UNLOCK:解锁共享内存段
  • buf:shmid_ds数据类型的地址,用来存放或修改共享内存的属性。

(4)返回值:

  • 成功:0
  • 失败:-1

(四)实战
创建一个A进程,在A进程中创建一个共享内存,并向其写入数据,再创建一个B进程,通过B进程从共享内存中读取数据。
write.c

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define BUFSZ 512
int main()
{
  key_t key;
  int shmid;
  //创建key值
  key = ftok("/",2019);
  if(key == -1)
  {
    perror("ftok error");
  }

  //创建共享内存
  shmid = shmget(key,BUFSZ,IPC_CREAT|0666);
  if(shmid<0)
  {
    perror("shmget error");
    exit(-1);
  }

  //映射
  char* shmadd = "123";
  shmadd =(char*) shmat(shmid,NULL,0);
  if(shmadd <(char*)0)
  {
    perror("shmat error");
  }

  //拷贝数据到共享内存区
  printf("strcpy");
  bzero(shmadd,BUFSZ);
  strcpy(shmadd,"hello world");
  return 0;
}

read.c

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define BUFSZ 512
int main()
{
  key_t key;
  int shmid;
  int ret;
  //创建key值
  
  key = ftok("/",2019);
  if(key == -1)
  {
    perror("ftok error");
  }
  
  //查看共享内存
  system("ipcs -m");

  //打开共享内存
  shmid = shmget(key,BUFSZ,IPC_CREAT|0666);
  if(shmid<0)
  {
    perror("shmget error");
    exit(1);
  }

  //映射
  fflush(stdout);
  char* shmadd =(char*)shmat(shmid,NULL,0);

  //读共享内存区数据
  printf("%s\n",shmadd);
  //分离共享内存和当前进程
  ret = shmdt(shmadd);
  if(ret<0)
  {
    perror("delete false");
  }
  else 
  {
    printf("delete success");
  }

  //删除共享内存
  shmctl(shmid,IPC_RMID,NULL);

  //查看共享内存
  system("ipcs -m");
  return 0;
}

运行结果如下:
linux进程间通信—共享内存