IPC之—共享内存

什么是共享内存

共享内存容许两个或更多进程访问同一块内存,就如同 malloc() 函数向不一样进程返回了指向同一个物理内存区域的指针。当一个进程改变了这块地址中的内容的时候,其它进程都会察觉到这个更改。在Linux中,每一个进程都有本身的P C B 和地址空间,而且都有一个对应的页表,负责将进程的地址和物理地址进行映射,经过MMU来管理。建立一段共享存储区(临界资源),被映射到不一样进程的地址空间内,从而实现了高效率的资源共享。linux

共享内存的优缺点

优势:
共享内存在全部用于进程间通讯的IPC资源中实现进程间通讯的速度是 最快的,由于它少了两次资源的拷贝。
以下图
这里写图片描述函数

缺点:
操做系统对共享内存并无提供同步与互斥机制,因此须要用户来实现同步与互斥机制,咱们知道,信号量就能够实现。spa

共享内存的使用

进程间通讯的本质是使不一样的进程能够看到同一份资源,这份资源称之为临界资源,那么这里的共享内存便是这份临界资源。操作系统

因此,首先应该建立一段共享存储区。而后使其要访问这个共享存储区的每个进程都要将该共享存储区链接至每一个进程的地址空间。
当通讯完成后,全部的进程须要对其去关联,通俗点讲,也就是说将这些进程和这块共享存储区的链接断开,并由建立共享存储区的进程释放该共享存储区。3d

注意:建议用户申请的共享存储块的大小最好是系统页面大小的整数倍。由于,若是用户申请的共享存储段的大小不是页面大小的整数倍的话,那么系统会采用向上对齐至页面大小的整数倍,可是用户可使用的仍是自定义的存储段。在linux系统下,页面大小默认为4kb。指针

指令

  1. 查看系统中的共享存储段
    ipcs -m
    1. 删除系统中的共享存储段
      ipcrm -m shmid

共享内存实现进程间通讯

下面一幅图来解释一下,实现共享内存间的通讯code

这里写图片描述

实现通讯的接口函数

  1. shmght:建立一个新的共享存储段或引用一个现有的共享存储段。
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg)

参数说明:server

*key:由ftok函数获得的IPC键值,标识系统中惟一的IPC资源。blog

size:该共享存储段的长度,以字节为单位。一般须要将其向上取为系统页长的整数倍。(一页为4k)。
若是在建立一个新段,那么必须指定size值,若是引用一个现存的段,则将size指定为0。当建立一个新段时,段内的内容初始化为0.
shmflg:
PC_CREAT||IPC_EXCL|0666 保证返回一个新的共享内存标识符,而且指定该共享内存的劝降为0666.
PC_CREAT 若是共享内存不存在,则建立并返回,若是存在,则打开并返回其标识符。
返回值:
成功时返回一个新建立的共享内存标识符或者一个已存在的共享内存标识符。取决于shmglg参数。
失败时返回-1并设置错误码。接口

  1. shmat:挂接
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void shmaddr, int shmflg)

功能:进程可调用该函数将共享存储段链接到它的地址空间
若成功,返回指向共享存储段的指针(该段所链接的虚拟地址),而且内核将使其与该共享存储段相关的shmid_ds结构中的shm_nattch计数器加1(相似于引用计数);若出错,返回-1.

参数:
shmid:共享存储段标识符。
shmaddr:
shmaddr = 0,则存储段链接到由内核选择的第一个可用地址上。通常推荐使用该方法,由于内核最清楚进程的地址空间。
shmflg:
若指定了SHM_RDONLY位,则以只读方式链接此段,不然以读写方式链接此段。

  1. shmdt(去关联)
    函数说明:
    当对共享存储的操做已经结束时,则需调用shmdt与该段分离。与shmat函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存。
    须要注意的是,该函数并不删除所指定的共享内存区,而是将以前用shmat函数链接好的共享内存区脱离目前的进程。
#include <sys/types.h>
#include <sys/shm.h>
void *shmdt(const void* shmaddr);

返回值:
若成功,返回0,并将shmid_ds结构中的shm_nattch计数器值减一;若出错,返回-1.

  1. shmctl:删除共享内存段。
#include <sys/types.h>
#Include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds* buf);

返回值:若删除成功,返回0;
若出错,返回-1。

参数:
shmid:共享存储段标识符;
cmd:指定的操做。
执行删除:设置为IPC_RMID。
buf:删除时忽略该参数,设置为NULL

key值与shmid有什么区别?

key值与shmid值都是标识惟一的一份IPC资源,那么它们之间又有什么区别呢?

key值:在操做系统层面,用来标识惟一的一份IPC资源。
shmid:在用户层面,即就是在代码层面,用来标识一份IPC资源。 —–>这个是用户所关注的

代码实现共享内存间通讯

//comm.h
#ifndef __COMM_H__
 #define __COMM_H__

 #include<stdio.h>
 #include<sys/shm.h>
 #include<sys/types.h>
 #include<sys/ipc.h>
 #define PATHNAME "."
 #define ID 65

 int CreateShm(int size);
 int GetShm(int size);
 int DestoryShm(int shmid);

 #endif
//comm.c
 #include"comm.h"
 static int CommShm(int size,int flags)
 {   
     key_t _key = ftok(PATHNAME,ID);
     if(_key<0)
     {
         perror("ftok");
         return -1;
     }
     int shmid = shmget(_key,size,flags);
     if(shmid<0)
     {
         perror("shmid");
         return -2;
     }
     return shmid;
 }
 int CreateShm(int size)
 {
     return CommShm(size,IPC_CREAT|IPC_EXCL|0666);
 }


 int GetShm(int size)
 {
     return CommShm(size,IPC_CREAT);
 }
 int DestoryShm(int shmid)
 {
     if(shmctl(shmid,IPC_RMID,NULL)<0)
     {
         perror("shmctl");
         return -1;
     }
     return 0;
 }
//server.c
#include"comm.h"
 int main()
 {
     //chuangjiangongxiangcunchuduan
     int shmid = CreateShm(4095);
     char* buf;
     buf = shmat(shmid,NULL,0);//guanlian
     sleep(4);
     int count = 0;
     while(count<20)
     {
         buf[count++] = 'a'+count;
         buf[count] = '\0';
         sleep(1);
     }
     shmdt(buf);//quguanlian
     DestoryShm(shmid);
     return 0;
 }
//client.c
#include"comm.h"
int main()
{
    int shmid = GetShm(0);
    char *buf;
    buf = shmat(shmid,NULL,0);
    int count  = 0;
    while(count++<15)
    {
        printf("client# %s\n",buf);
        sleep(1);
    }
    shmdt(buf);
    return 0;
}

运行结果 以下图,而且由监视窗口能够看到,关联的进程数由2变为1,而后删除
这里写图片描述
这里写图片描述

相关文章
相关标签/搜索