進程間通信:共享內存

系統V的IPC通信機制包括了消息隊列、共享內存和信號量。每一種IPC結構結構都有一個非負整數標誌,當創建一個IPC結構時,調用進程都必須提供一個類型爲key_t的鍵(key)。操作系統把這個鍵轉換爲一個IPC的唯一標識符。

可以使用以下方式來指定一個鍵,調用函數如下:

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char* path, int projectid);

創建好以後就可以使用這個鍵去創建一個新的IPC結構了。服務器只需要調用合適的get函數去創建就好了。這裏我們主要談論共享內存。

一. 共享內存

顧名思義,通過共享一塊內存空間,其他進程不需要進行讀取操作就可以看到一份公共資源,從而進行相互交換信息,但是共享內存之中不提供同步與互斥機制,這就需要我們用戶自己去設計,簡單的就是執行PV操作了。

要使用一個共享內存段,進程就必須獲得該共享內存段的標誌符,這裏可以使用shmget函數,源碼如下:

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

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

參數size指定了共享內存段的大小,建議制定爲頁的整數倍,參數key制定了該共享內存段使用的key,可以使用ftok得到,參數shmflg可以設置爲IPC_CREAT和IPC_EXCL,兩個同時設置代表
如果共享內存段不存在則創建,如果存在則返回一個標識符,只使用IPC_CREAT時代表共享內存段存在則打開,不存在則返回-1,同時將失敗原因保存在errno中。

創建了一個共享內存段後就要對他進行一系列的操作,比如,刪除。這裏就要使用shmctl函數:

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

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

參數shmid包括了共享內存段的標識符,參數buf指向一個類型爲struct_ds的結構體,定義了內存段的性質:

struct shmid_ds {
    struct ipc_perm     shm_perm;   /* operation perms */
    int         shm_segsz;  /* size of segment (bytes) */
    __kernel_time_t     shm_atime;  /* last attach time */
    __kernel_time_t     shm_dtime;  /* last detach time */
    __kernel_time_t     shm_ctime;  /* last change time */
    __kernel_ipc_pid_t  shm_cpid;   /* pid of creator */
    __kernel_ipc_pid_t  shm_lpid;   /* pid of last operator */
    unsigned short      shm_nattch; /* no. of current attaches */
    unsigned short      shm_unused; /* compatibility */
    void            *shm_unused2;   /* ditto - used by DIPC */
    void            *shm_unused3;   /* unused */
};

參數cmd可以設置爲IPC_RMID,表示從系統中刪除shmid指定共享內存段的標識符,並刪除相應的數據結構。

如果一個進程要使用一個共享內存段,他必須連接該內存段,也就是說必須將內存段映射到進程的地址空間。可以使用shmat函數:

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

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

參數shmid指定了要連接的共享內存段的標識符;

參數shmaddr指定了共享內存的地址,一般設置爲0,表示由系統選擇,因爲系統是最瞭解地址的;

參數shmflg包括了常量SHM_RDONLY,那麼內存段就是隻讀的,否則就是讀寫的,如果成功連接,返回開始地址,否則返回(void*)-1,同時將失敗原因保存在errno中。

在使用完共享內存段以後,進程可以使用shmdt來斷開連接;

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

void *shmdt(void* shmaddr);

參數是shmat的返回值。

二.共享內存代碼

//頭文件comm.h
#ifndef _COMM_H_
#define _COMM_H_

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

#define PATHNAME "."
#define PROJ_ID 0x6666

int creatShm(int size);   //共享內存的創建
int destroyShm(int shmid);//共享內存的銷燬
int getShm(int size);  //共享內存獲取


#endif //_COMM_H_H
//comm.c
#include "comm.h"

static int commShm(int size,int flags)
{
    key_t key = ftok(PATHNAME, PROJ_ID);  //key值獲取
    if(key<0){
        perror("ftok");
        return -1;
    }
    int shmid = shmget(key, size, flags); //創建共享內存
    if(shmid<0){
        perror("shmget");
        return -2;
    }
    return shmid;
}
int creatShm(int size)
{
    return commShm(size, IPC_CREAT|IPC_EXCL|0666);
}
int destroyShm(int shmid)
{
    if(shmctl(shmid, IPC_RMID, 0)<0){   //共享內存銷燬
        perror("shmctl");
        return -2;
    }
    return 0;
}
int getShm(int size)
{
    return commShm(size,IPC_CREAT);

}
///server.c
#include "comm.h"
int main()
{
    int shmid = creatShm(4096);   //創建
    sleep(4);
    char * addr = (char*)shmat(shmid, NULL, 0); //連接內存段
    sleep(3);
    while(1){
        printf("%s\n", addr);
        sleep(1);
    }
    shmdt(addr);         //斷開連接
    destroyShm(shmid);  //銷燬共享內存
    return 0;
}
#include "comm.h"


int main()
{
    int shmid = getShm(4096);   //打開共享內存
    sleep(4);
    char * addr = (char*)shmat(shmid, NULL, 0); //連接

    sleep(3);
    int i = 0;
    while(1){   //在內存中寫入數據
        addr[i] = 'A' + i;   
        i++;
        addr[i] = 0;
        sleep(1);
    }
    shmdt(addr); //斷開連接
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章