進程間通訊(3)——共享內存和信號量

【4】共享內存

(1)概述

  • 兩個不一樣進程A、B共享內存的意思是,同一塊物理內存被映射到進程A、B各自的進程地址空間,進程A能夠即時看到進程B對共享內存中數據的更新,反之,進程B也能夠即時看到進程A對共享內存中數據的更新。c++

  • 共享內存是存在於內核級別的一種資源shell

  • 在系統內核爲一個進程分配內存地址時,經過分頁機制可讓一個進程的物理地址不連續,同時也可讓一段內存同時分配給不一樣的進程。共享內存機制就是經過該原理來實現的,共享內存機制只是提供數據的傳送,如何控制服務器端和客戶端的讀和寫操做互斥,這就須要一些其餘的輔助工具,例如信號量的概念。數組

  • 共享內存能夠說是Linux下最快速、最有效的進程間通訊方式服務器

  • 由於進程能夠直接讀寫內存,而不須要任何數據的拷貝
  • 管道和消息隊列等通訊方式,須要在內核和用戶空間進行四次的數據拷貝,而共享內存則只拷貝兩次數據:一次從輸入文件到共享內存區,另外一次從共享內存區到輸出文件
  • 進程之間在共享內存時,並不老是讀寫少許數據後就解除映射,有新的通訊時,再從新創建共享內存區域。而是保持共享區域,直到通訊完畢爲止,這樣,數據內容一直保存在共享內存中,並無寫回文件。共享內存中的內容每每是在解除映射時才寫回文件的
  • 共享內存的不足之處:因爲多個進程對同一塊內存區域具備訪問的權限,各個進程間的同步問題尤其重要。必須控制同一時刻只有一個進程對共享內存區域寫入數據,不然將形成數據的混亂。同步控制問題能夠經過下一節介紹的信號量來解決。

(2)共享內存相關操做

用於Linux進程通訊共享內存。共享內存函數由shmget、shmat、shmdt、shmctl四個函數組成。ide

shmat函數原型函數

  • shmat(把共享內存區對象映射到調用進程的地址空間)工具

  • 所需頭文件命令行

    #include <sys/types.h>
      #include <sys/shm.h>
  • 函數說明線程

    鏈接共享內存標識符爲shmid的共享內存,鏈接成功後把共享內存區對象映射到調用進程的地址空間,隨後可像本地空間同樣訪問
  • 函數原型code

    void *shmat(int shmid, const void *shmaddr, int shmflg)
  • 函數傳入值

    shmid   共享內存標識符
      shmaddr  指定共享內存出如今進程內存地址的什麼位置,直接指定爲NULL讓內核本身決定一個合適的地址位置
      shmflg   SHM_RDONLY:爲只讀模式,其餘爲讀寫模式
  • 函數返回值

    成功:附加好的共享內存地址
      出錯:-1,錯誤緣由存於errno中

shmget()

  • 建立或打開共享內存的函數

    #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/shm.h>
      int shmget(key_t key,int size,int flag);

返回值:成功返回共享內存ID,出錯返回-1

key:建立或打開的共享內存的鍵值

size:共享內存區域大小,只在建立一個新的共享內存時生效

flag:調用函數的操做類型,也可用於設置共享內存的訪問權限,二者經過邏輯或表示

shmdt函數原型

  • shmdt(斷開共享內存鏈接)

  • 所需頭文件

    #include <sys/types.h>
      #include <sys/shm.h>
  • 函數說明

    與shmat函數相反,是用來斷開與共享內存附加點的地址,禁止本進程訪問此片共享內存
  • 函數原型

    int shmdt(const void *shmaddr)
  • 函數傳入值

    shmaddr:鏈接的共享內存的起始地址
  • 函數返回值

    成功:0
      出錯:-1,錯誤緣由存於error中

(3)使用shmget函數建立共享內存的例子,create_shm.c:

程序中在調用shmget函數時指定key參數值爲IPC_PRIVATE,這個參數的意義是建立一個新的共享內存區,當建立成功後使用shell命令ipcs來顯示目前系統下共享內存的狀態。命令參數-m爲只顯示共享內存的狀態。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#define BUFSZ 4096
int main(void)
{
        int shm_id;/*共享內存標識符*/
        shm_id = shmget(IPC_PRIVATE,BUFSZ,0666);
        /*建立共享內存*/
        if(shm_id < 0)
        {
                printf("shmget failed!\n");
                exit(1);/*shmget出錯退出*/
        }
        printf("create a shared memory segment successfully:%d\n",shm_id);
        system("ipcs -m");/*調用ipcs命令查看IPC*/
        exit(0);
}

運行結果:

hyx@hyx-virtual-machine:~/test$ ./create_shm
create a shared memory segment successfully:2949133

------------ 共享內存段 --------------
鍵        shmid      擁有者  權限     字節     鏈接數  狀態      
0x00000000 65536      hyx        600        524288     2          目標       
0x00000000 1114113    hyx        600        524288     2          目標       
0x00000000 196610     hyx        600        524288     2          目標       
0x00000000 393219     hyx        600        524288     2          目標       
0x00000000 950276     hyx        600        524288     2          目標       
0x00000000 589829     hyx        600        524288     2          目標       
0x00000000 622598     hyx        600        16777216   2                       
0x00000000 655367     hyx        600        16777216   2          目標       
0x00000000 753672     hyx        600        524288     2          目標       
0x00000000 819209     hyx        600        2097152    2          目標       
0x00000000 1146890    hyx        600        1048576    2          目標       
0x00000000 1245195    hyx        600        524288     2          目標       
0x00000000 1933324    hyx        600        67108864   2          目標       
0x00000000 2949133    hyx        666        4096       0

執行ipcs,打印共享內存,信號量和消息隊列的信息:

hyx@hyx-virtual-machine:~/test$ ipcs

------------ 共享內存段 --------------
鍵        shmid      擁有者  權限     字節     鏈接數  狀態      
0x00000000 65536      hyx        600        524288     2          目標       
0x00000000 1114113    hyx        600        524288     2          目標       
0x00000000 196610     hyx        600        524288     2          目標       
0x00000000 393219     hyx        600        524288     2          目標       
0x00000000 950276     hyx        600        524288     2          目標       
0x00000000 589829     hyx        600        524288     2          目標       
0x00000000 622598     hyx        600        16777216   2                       
0x00000000 655367     hyx        600        16777216   2          目標       
0x00000000 753672     hyx        600        524288     2          目標       
0x00000000 819209     hyx        600        2097152    2          目標       
0x00000000 1146890    hyx        600        1048576    2          目標       
0x00000000 1245195    hyx        600        524288     2          目標       
0x00000000 1933324    hyx        600        67108864   2          目標       
0x00000000 2949133    hyx        666        4096       0                       

--------- 信號量數組 -----------
鍵        semid      擁有者  權限     nsems     

--------- 消息隊列 -----------
鍵        msqid      擁有者  權限     已用字節數 消息

執行ipcs -m,只打印共享內存段信息:

hyx@hyx-virtual-machine:~/test$ ipcs -m

------------ 共享內存段 --------------
鍵        shmid      擁有者  權限     字節     鏈接數  狀態      
0x00000000 65536      hyx        600        524288     2          目標       
0x00000000 1114113    hyx        600        524288     2          目標       
0x00000000 196610     hyx        600        524288     2          目標       
0x00000000 393219     hyx        600        524288     2          目標       
0x00000000 950276     hyx        600        524288     2          目標       
0x00000000 589829     hyx        600        524288     2          目標       
0x00000000 622598     hyx        600        16777216   2                       
0x00000000 655367     hyx        600        16777216   2          目標       
0x00000000 753672     hyx        600        524288     2          目標       
0x00000000 819209     hyx        600        2097152    2          目標       
0x00000000 1146890    hyx        600        1048576    2          目標       
0x00000000 1245195    hyx        600        524288     2          目標       
0x00000000 1933324    hyx        600        67108864   2          目標       
0x00000000 2949133    hyx        666        4096       0                       

hyx@hyx-virtual-machine:~/test$

(4)使用共享內存進行通訊的實例

共享內存ID以命令行參數的形式傳遞給進程。

write_shm.c是寫共享內存,即分10次向共享內存中寫入people結構體的成員數據,(姓名和年齡)數據的值由for循環自動生成。程序的最後使用shmdt將共享內存段從當前的進程空間中脫離掉。

#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
typedef struct
{
        char name[4];
        int age;
}people;
int main(int argc,char** argv)
{
        int shm_id,i;
        char temp;
        people *p_map;
        if(argc != 2)/*命令行參數錯誤*/
        {
                printf("USAGE:atshm < d=identifier>");/*打印幫助消息*/
                exit(1);
        }
        shm_id = atoi(argv[1]);/*獲得要引入的共享內存段,atoi將字符轉換成整型*/
        p_map = (people *)shmat(shm_id,NULL,0);/*shmat:把共享內存區對象映射到調用進程的地址空間*/
        temp = 'a';
        for(i = 0;i < 10;i++)
        {
                temp+=1;
                memcpy((*(p_map+i)).name,&temp,1);
                (*(p_map+i)).age = 20+i;
                /*memcpy:c和c++使用的內存拷貝函數,memcpy函數的功能是從源src所指的內存地址的起始位置開始拷貝n個字節到目標des所指的內存地址的起始位置中,void *memcpy(void *dest, const void *src, size_t n);*/
        }
        if(shmdt(p_map)==-1)
        {
                perror("detach error!\n");
        }
        return 0;
}

read_shm.c是讀共享內存,即分10次從共享內存中讀出people結構體的成員數據。程序的最後一樣使用shmdt將共享內存段從當前的進程空間中脫離掉。

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
typedef struct
{
        char name[4];
        int age;
}people;
int main(int argc,char** argv)
{
        int shm_id,i;
        people *p_map;
        if(argc != 2)
        {
                printf("USAGE:atshm <idenfifier>");
                exit(1);
        }
        shm_id = atoi(argv[1]);/*獲得要引入的共享內存段,atoi將字符轉換成整型*/
        p_map = (people*)shmat(shm_id,NULL,0);/*shmat:把共享內存區對象映射到調用進程的地址空間*/
        for(i=0;i<10;i++)
        {
                printf("name:%s  ",(*(p_map+i)).name);
                printf("age %d\n",(*(p_map+i)).age);
        }
        if(shmdt(p_map)==-1)
        {
                perror("detach error!\n");

        }
       return 0;
}

運行結果:

hyx@hyx-virtual-machine:~/test$ ./write_shm 2949133
hyx@hyx-virtual-machine:~/test$ ./read_shm 2949133
name:b  age 20
name:c  age 21
name:d  age 22
name:e  age 23
name:f  age 24
name:g  age 25
name:h  age 26
name:i  age 27
name:j  age 28
name:k  age 29
hyx@hyx-virtual-machine:~/test$

【5】信號量

  • 信號量的原理是一種數據操做鎖的概念,它自己不具有數據交換的功能,而是經過控制其餘的通訊資源(如文件、外部設備等)來實現進程間通訊。信號量自己不具有數據傳輸的功能,其只是一種外部資源的標識。

  • 信號量,有時也被稱爲信號燈,是在多進程環境下使用的一種設施,它負責協調各個進程,以保證它們可以正確、合理的使用公共資源。信號量分爲單值和多值兩種,前者只能被一個進程得到,後者能夠被若干個進程得到。

  • 以停車場爲例:假設停車場只有三個車位,一開始三個車位都爲空。這時若是同時來了第五輛車,看門人容許其中三輛直接進入,而後放下車欄,剩下的車則必須在入口等待,在此後來的車也都不得不在入口處等待。這時,有一輛車離開停車場,看門人得知後,打開車欄,放入外面的一輛進去,若是又離開兩輛,則又能夠放入兩輛,如此往復。

  • 車位:公共資源
  • 每輛車:一個進程
  • 看門人:信號量的做用
  • 抽象來說,信號量的特性以下:信號量是一個非負整數(車位數),全部經過它的進程/線程(車輛)都會將該整數減1(經過它是爲了使用公共資源),當該整數值爲時,全部試圖經過它的進程都處於等待狀態。在信號量上咱們定義兩種操做:wait(等待)和releas(釋放)。當一個進程調用wait操做時,它要麼獲得資源探後將信號量減1,要麼一直等下去(指放入阻塞隊列),直到信號量大於等於1時。release其實是在信號量上執行加1操做,對應於車輛離開停車場,該操做之因此叫作「釋放」是由於釋放了信號量守護的資源。
相關文章
相關標籤/搜索