系統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;
}