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,而後刪除
這裏寫圖片描述
這裏寫圖片描述

相關文章
相關標籤/搜索