system V共享內存

system V共享內存和posix共享內存類似,system V共享內存是調用shmget函數和shamat函數。
shmget函數創建共享內存區,或者訪問一個存在的內存區,類似系統調用共享內存的open和posix共享內存shm_open函數。shmget函數原型為:

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

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

key: 函數ftok返回值,或者IPC_PRIVATE ,當使用IPC_PRIVATE時,最好兩個進程空間是共享的,比如父子進程,否則當前進程產生的共享內存標識(返回值),在另一個進程裡面不易得到;

ftok函數原型為:key_t ftok(const char *pathname, int proj_id); 參數pathname為文件絕對路徑名,proj_id為一個整型標識符,該函數將一個已存在的的路徑名和一個整型標識符轉化成一個key_t值(返回值),稱為IPC鍵。

size:創建新的共享內存大小,當創建一片新的共享內存時,該值為不為0的參數。如果是讀取一片共享內存,該值可以為0。

shmflg:讀寫權限值組合。IPC_CREAT(創建新的共享內存)或IPC_CREAT|IPC_EXCL(當將要創建的共享內存已經存在時,再試圖創建將返回EEXIST)。其實IPC_CREAT和IPC_EXCL的組合和open函數的O_CREAT和O_EXCL組合類似。

函數返回共享內存區的標識。shmxxx函數操作共享內存將使用該函數返回值。該函數類似posix共享內存shm_open函數功能。

當shmget創建或打開一個共享內存區後,需要使用函數shmat來將該片共享內存連接到當前進程空間中來,當某一進程使用完共享內存後,使用函數shmdt斷開和共享內存的鏈接。

#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);

shmid:是函數shmget函數返回的共享內存標識符。

shmaddr: 連接到調用進程地址空間的地址,如果該參數為NULL,系統選擇一個合適地址;如果shmaddr非空並且shmflg指定了選項SHM_RND,那麼相應的共享內存鏈接到由shmaddr參數指定的地址向下舍入一個SHMLAB常值。如果shmaddr非空並且shmflg未指定SHM_RND,共享內存地址鏈接到shmaddr參數指定的地址。

shmflg:可以指定SHM_RND和SHM_RDONLY(只讀),如果指定SHM_RDONLY選項,那麼調用進程對該片共享內存只有讀權限,否則,進程對該片內存將有讀寫權限。

#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

shmid:共享內存區標識;

cmd:對共享內存的操作命令,命令IPC_RMID銷燬(destroy)一片共享內存,銷燬之後所有shmat,shmdt,shmctl對該片內存操作都將失效,銷燬該共享內存要等到該共享內存引用計數變為0才進行;IPC_SET命令設置shmid_ds結構成員;IPC_STAT返回當前共享內存結構;其餘命令查看man手冊。

buf:為指向shmid_ds數據結構;

system V 共享內存示例:

server process:

int sln_shm_get(char* shm_file, void** mem, int mem_len)
{
    int shmid;
    key_t key;


    if (NULL == fopen(shm_file, "w+")) {
        printf("fopen: %s\n", strerror(errno));
        return -1;
    }

    key = ftok(shm_file, 0);

    if (key < 0) {
        printf("ftok: %s\n", strerror(errno));
        return -1;
    }

    shmid = shmget(key, mem_len, IPC_CREAT);

    if (shmid < 0) {
        printf("shmget: %s\n", strerror(errno));
        return -1;
    }

    *mem = (void*)shmat(shmid, NULL, 0);

    if ((void*) - 1 == *mem) {
        printf("shmat: %s\n", strerror(errno));
        return -1;
    }

    return shmid;
}

int main(int argc, const char* argv[])
{
    char* shm_file = NULL;
    char* shm_buf = NULL;
    int shmid;


    shmid = sln_shm_get(SHM_IPC_FILENAME, (void**)&shm_buf, SHM_IPC_MAX_LEN);

    if (shmid < 0) {
        return -1;
    }

    snprintf(shm_buf, SHM_IPC_MAX_LEN,
             "Hello system V shaare memory IPC! this is write by server.");

    sleep(15);

    printf("System V server delete share memory segment!\n");
    //shmdt(shm_buf);

    shmctl(shmid, IPC_RMID,
           NULL); //server在15秒之後destroy該片共享內存,此時客戶進程將獲取不到共享內存的內容

    return 0;
}

client process:

int sln_shm_get(char* shm_file, void** mem, int mem_len)
{
    int shmid;
    key_t key;


    key = ftok(shm_file, 0);

    if (key < 0) {
        printf("ftok: %s\n", strerror(errno));
        return -1;
    }

    shmid = shmget(key, mem_len, IPC_CREAT);

    if (shmid < 0) {
        printf("shmget: %s\n", strerror(errno));
        return -1;
    }

    *mem = (void*)shmat(shmid, NULL, 0);

    if ((void*) - 1 == *mem) {
        printf("shmat: %s\n", strerror(errno));
        return -1;
    }

    return shmid;
}

int main(int argc, const char* argv[])
{
    char* shm_buf = NULL;
    int i;

    if (sln_shm_get(SHM_IPC_FILENAME, (void**)&shm_buf, SHM_IPC_MAX_LEN) < 0) {
        return -1;
    }


    printf("ipc client get: %s\n", shm_buf);


    return 0;
}

運行時,首先執行server process,使用命令ipcs可以查看當前系統共享內存:

# ipcs 
------ Message Queues -------- 
key msqid owner perms used-bytes messages 


------ Shared Memory Segments -------- 
key shmid owner perms bytes nattch status 
0x0010a797 131072 root 0 4096 1 


------ Semaphore Arrays -------- 
key semid owner perms nsems

可以看到存在一個共享內存區,其中key為:0x0010a797 ,共享內存ID為:131072

# ./client 
ipc client get: Hello system V shaare memory IPC! this is write by server. 
#

當server進程destroy共享內存之後,再重複上面步驟,

# ipcs 

------ Message Queues -------- 
key msqid owner perms used-bytes messages 


------ Shared Memory Segments -------- 
key shmid owner perms bytes nattch status 


------ Semaphore Arrays -------- 
key semid owner perms nsems

此時共享內存已經不在了,但文件依然存在。

# ./client
ipc client get:

此時客戶端已經不能獲取之前共享內存內容了。 另外,ipcrm命令可以在命令行上刪除指定共享內存區。 通過讀取文件/proc/sys/kernel/shmmax可以獲取系統所支持共享內存最大值,

# cat /proc/sys/kernel/shmmax
33554432

可以看到我目前系統支持最大個共享內存值為:32M。

通過上示例可以看到system V共享內存和posix共享內存類似,不過posix共享內存的大小可以隨時通過ftruncate改變,而system V 的共享內存大小在shmget時就已經確定下來了。 同樣的,system V共享內存大多數時候也需要在多進程之間同步,system V 可以使用自己的信號量來實現,具體細節將在後面同步相關專欄詳細講解。

本節源碼下載:

http://download.csdn.net/detail/gentleliu/8140887