[Linux] IPC共享內存

一、共享內存原理

在這裏插入圖片描述

二、共享內存生命週期

共享內存的生命週期是隨內核的。
匿名管道、命名管道、消息隊列、信號量、共享內存這五種進程間通信方式。兩種管道的生命週期是隨進程,剩下的都是隨內核的。

三、共享內存爲什麼是最快的通信方式?

  因爲共享內存的原理是:在物理內存中直接開闢一片空間,並將空間映射到各個進程的虛擬地址空間的共享區;這時候進程就可以通過虛擬地址來直接對共享內存進行操作。
  其他通信的方式是將數據拷貝到內核態,用的時候再從內核態拷貝到用戶態進行操作
  共享內存少了兩步從用戶態<–>內核態之間的數據拷貝過程,所以共享內存最快。


注意共享內存並沒有進行同步與互斥,所以最好搭配信號量或互斥鎖來使用。


四、共享內存的操作流程

第一步:創建共享內存

這一步需要指定標識符、大小、權限。成功會返回一個句柄。
使用函數:int shmget(key_t key, size_t size, int shmflg);
參數:
key:共享內存標識符 這個key可以自己去指定成任意的數字也可以通過系統提供的接口 ftok 去生成一個System V 標準的 IPC key 值
size:共享內存大小,最小也是一個內存頁的大小,4096個字節
shmflg:選項信息(權限信息以及如何操作共享內存)

ftok解釋:
ftok - convert a pathname and a project identifier to a System V IPC key(通過一個文件名和proj id生成一個System V 標準的IPC key 值)
   key_t ftok(const char *pathname, int proj_id);
    具體過程:proj_id是一個數字,在ftok這個函數內部通過文件名獲取到文件的inode結點號,然後將inode結點號這個數字取出一部分,再取出proj_id的一部分,合併起來組成 key 值。
    ftok的缺陷:由於ftok是通過文件名和proj_id生成一個key值,會有多個進程連接到共享內存,萬一文件被刪除了,那麼其他進程也就無法訪問到這段共享內存了。就算重新創建一個文件,文件的inode結點號也是不一樣所以還是無法訪問到這段共享內存。

第二步:將共享內存映射到虛擬地址空間

需要句柄、虛擬地址空間的首地址(以便知道映射到哪裏去)
void *shmat(int shmid, const void *shmaddr, int shmflg);
參數:
shmid: 共享內存創建返回的句柄
shmaddr:映射首地址-通常置NULL
shmflg:
0 可讀可寫
SHM_RDONLY 只讀
返回值:成功:返回映射首地址 失敗:返回 (void*)-1

第三步:進行內存操作
第四步:解除映射關係

int shmdt(const void *shmaddr);
參數:
shmaddr: 映射首地址
返回值:成功返回:0 失敗返回:-1

第五步:刪除共享內存

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
參數:
shmid: 句柄
cmd: 操作
IPC_RMID 刪除共享內存
buf: 設置/獲取屬性信息
想獲取傳入首地址,不想獲取置NULL
共享內存並不會被立即刪除,而是判斷當前連接數,若不爲0,則拒絕後續連接,直到連接數爲0的時候才刪除這塊共享內存。


寫一個共享內存讀寫的例子:
shm_read.c

/*共享內存的基本使用*/

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

#define IPC_KEY 0x12345678
#define PROJ_ID 0x12345678
#define SHM_SIZE    4096
int main()
{
	//1、創建/打開共享內存
    int shmid = shmget(IPC_KEY, SHM_SIZE, IPC_CREAT|0664);
    if(shmid < 0){
		perror("shmget error");
		return -1;
    }

	//2、將共享內存映射到虛擬地址空間
    char *shm_start = (char*)shmat(shmid, NULL, 0);
    if(shm_start == (void*)-1){
		perror("shmat error");
		return -1;
    }

    //3、進行內存操作
    int i = 0;
    while(1){
		printf("%s\n", shm_start);
		sleep(1);
    }

    //4、解除映射關係
    shmdt(shm_start);

    //5、刪除共享內存
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}

shm_write.c

/*共享內存的基本使用*/

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

#define IPC_KEY 0x12345678
#define PROJ_ID 0x12345678
#define SHM_SIZE    4096
int main()
{
    //1、創建/打開共享內存
    int shmid = shmget(IPC_KEY, SHM_SIZE, IPC_CREAT|0664);
    if(shmid < 0){
		perror("shmget error");
		return -1;
    }
    //2、將共享內存映射到虛擬地址空間
    char *shm_start = (char*)shmat(shmid, NULL, 0);
    if(shm_start == (void*)-1){
		perror("shmat error");
		return -1;
    }
    //3、進行內存操作
    int i = 0;
    while(1){
		sprintf(shm_start, "開始輸出!!! +%d\n", i++);
		sleep(1);
    }
    //4、解除映射關係
    shmdt(shm_start);
    //5、刪除共享內存
    shmctl(shmid, IPC_RMID, NULL);

    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章