進程間通信——共享內存

前言


進程間IPC通信的三大主題,消息隊列,信號量,共享內存,我們接下來說一說共享內存,共享內存可以提供給服務器進程和客戶進程之間進行通信,不需要進行數據的複製,所以速度最快,只需要讓兩個進程通過頁表映射到同一塊物理內存即可,這樣,這塊物理內存是兩個進程都能看到的,這樣當一個進程進行寫操作,另外的一個進程也就可以做讀操作。所以問題關鍵也就是給出一個特定的存儲區。通常情況下,我們需要確保一個進程在寫的時候,另外一個進程不能去讀,所以我們可以使用信號量進行共享內存訪問。

這裏寫圖片描述

這裏寫圖片描述

創建共享內存


首先我們利用ftok函數生成key標識。

  key_t ftok(const char *pathname, int proj_id);

然後我們使用shmget函數,這個函數可以進行創建共享內存。

int shmget(key_t key, size_t size, int shmflg);

在這你要看好,這個size是需要進行申請的共享內存的大小,需要注意的是,操作系統爲你提供的時候是按照頁來提供的,因爲是按照頁提供的,所以size爲4k的整數倍,然後shmflg和信號量以及消息隊列的類似,如果要創建新的共享內存,那麼就是使用IPC_CREATE,IPC_EXCL。如果是已存在的,那麼只需要使用IPC_CREATE或者傳0。

掛接共享內存


創建好共享的內存以後,這個時候我們就應該考慮的是讓進程去掛接共享內存。

 void *shmat(int shmid, const void *shmaddr, int shmflg);

掛接共享內存,shmflg一般表示什麼方式進行掛接,一般都是取0,shmid是掛接的進程號。這個函數返回的是一個地址,我們可以想一下使用malloc的時候如何使用的,這個和那個類似。

去關聯共享內存


當一個進程不需要共享內存的時候,這個時候就需要去關聯,

int shmdt(const void *shmaddr);

這個參數就是我們掛接以後返回的地址。

銷燬共享內存


我們可以使用函數銷燬共享內存,

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

上面的這個函數用來控制共享內存的操作,我們可以用它來刪除共享內存,當cmd取IPC_RMID的時候就可以刪除共享內存,另外我們也可以使用命令來進行操作ipcrm -m xxx
這樣也可以進行銷燬共享內存。

注意點


共享內存並沒有提供互斥和同步機制,所以一般都是我們自己來做這個,可以使用信號量保證互斥,共享內存一般來提供對大塊內存區域的訪問,因爲共享內存不想IPC另外兩個一樣需要拷貝和搬遷,所以共享內存是最快的,所以使用也比較多。

對於一個共享內存,實現也應用了引用計數的原理,當進程脫離了共享內存區之後,計數器會減一,只有當計數器變爲0的時候,就是沒有任何進程來使用這個共享內存區,這個時候共享內存纔會被刪除,當一個進程終止的時候,它所附加的共享內存區都會自動脫離。

實例


下面的例子是服務器進程進行創建共享共享內存,然後掛接,客戶進程是得到關聯共享內存,然後客戶進程一秒發一個a,這個時候服務器進程接受到,打印,從而實現共享內存。

comm.h

#ifndef __COMM_H_
#define __COMM_H_

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

#define PATHNAME "."
#define PROJID 0x6666
#define SIZE 4096*1

int Comm_Shm(int flags);
int Create_Shm();
int Get_Shm();
int Destory_Shm(int shmid);
char *At_Shm(int shmid);
int Dt_Shm(char *mem_addr);



#endif //!__COMM_H_

comm.c

#include"comm.h"


int Comm_Shm(int flags)
{
    key_t shmkey=ftok(PATHNAME,PROJID);
    if(shmkey<0)
    {
        perror("ftok");
        return -1;
    }
    printf("sda");

    int shmid=shmget(shmkey,SIZE,flags);
    if(shmget<0)
    {
        perror("shmget");
        return -2;
    }
    printf("create");
    return shmid;
}
int Create_Shm()
{
    return Comm_Shm(IPC_CREAT|IPC_EXCL|0666);
}
int Get_Shm()
{
    return Comm_Shm(0);
}
int Destory_Shm(int shmid)
{
    int ret=shmctl(shmid,IPC_RMID,NULL);
    if(ret<0)
    {
        perror("shmctl");
        return -1;
    }
    return 0;
}

char* At_Shm(int shmid)
{
    char *shm=(char *)shmat(shmid,NULL,0);
    return shm;
}
int Dt_Shm(char *mem_addr)
{
    int ret =shmdt(mem_addr);
    return 0;
}

client.c

#include"comm.h"
int main()
{
    int shmid=Get_Shm();

    char *add=At_Shm(shmid);

    printf("physic address:%s",add);
    int i=0;
    while(1)
    {
        sleep(1);
        add[i++] = 'a';
        i %= (SIZE-1);
        add[i]='\0';

    }
    Dt_Shm(add);
    return 0;
}

server.c

#include"comm.h"
int main()
{
    int shmid=Create_Shm();
    sleep(5);
    char *add=At_Shm(shmid);
    while(1)
    {
        sleep(1);
        printf("%s\n",add);
    }
    sleep(5);
    Dt_Shm(add);
    Destory_Shm(shmid);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章