一.POSIX共享內存的實現
共享內存是在進程間共享某一塊內存。是最快一種ipc通信機構。其中posix共享內存機制 它主要是通過內存映射(mmap)機制來實現的。
在進程間共享內存使用如下固定步驟:
1.創建一個共享內存
int shm_open(const char *name, int oflag, mode_t mode);
name是共享內存名字,各個進程通過名字來找到同一塊內存.
oflag,是這個內存屬性。類似於文件屬性。使用O_RDWR/O_RDONLY/O_CREAT,第一次創建共享內存必須帶O_CREAT標志位。
mode,是權限代碼。
當其打開成功是會在建立一個虛擬的文件 /dev/shm/shm.XXXX,其中XXXX是name的名字
例: int fd = shm_open("test",O_RDWR,666);
將會創建 /dev/shm/shm.test文件。
shm_open成功後,將返回一個文件描述符fd.你可以理解是在內核的中分配一段空間,並分配一個fd號給應用程序使用。
這裡要注意,如果一個進程已經創建一個共享內存,後面其它的進程打開這個共享內存只需要用
shm_open(name,flag,0); //而且
2.設置內存大小。
int ftruncate(int fd, off_t length);
ftruncate操作的fd即可是一個文件open後的fd,也可是shm_open打開的fd .
普通文件將會被ftruncate強行設為length大小(不夠加0空間,超過則被截斷
如果共享內存,將表示把共享內存設為length大小.
如果設置 ftruncate返回0
3.用mmap 眏射到進程空間當中某一個地址上
void *mmap(void *start, size_t length, int prot, int flags,
int fd, off_t offset);
start是表示開始映射的物理地址,如果為NULL表示由內核自行選擇合適空間來分配。
length是內存的大小,一般是和第二步的同一大小。
prot 是共享內存屬性。它有如下值
PROT_EXEC 分配空間可執行
PROT_READ 分配空間可讀
PROT_WRITE 可寫
PROT_NONE 禁止訪問,一般為省事,都設為 PROT_READ|PROT_WRITE
flags 是共享內存的標志位,它有如下取值
MAP_FIXED ,內存固定大小,不能超過一頁。如果超過將mmap失敗.
MAP_SHARED ,在多個進程間共享這一內存
MAP_PRIVATE, 只供本進程使用。
fd 是shm_open或open創建文件的描述符.
offset 是在共享內存或文件中的偏移量。一般是0
如果映射成功,將會返回一個進程內部地址。對這個地址訪問即是對內核共享內存的訪問。這個地址位於堆和棧的空閒區。
如果失敗,將返回MAP_FAILED (它等於 (void *)-1)
到mmap後,對共享內存的操作就跟與普通內存沒有什麼區別了。如使用memcpy/memset等操作.
如果結束的對共享內存使用,即可采用接下兩步.
4.munmap解除當前進程對這塊共享映射。
int munmap(void *start, size_t length);
start是映射的進程內地址,lenght是內存的長度
如果映射是文件,它還有存盤功能。
5.從內核清除共享內存
int shm_unlink(const char *name);
name是共享內存名字,如果有多個進程打開這個內存,只有最後使用進程調用shm_unlink,這個共享內存才會真正清除掉。
二.POSIX共享內存實現代碼
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/mman.h> /* for mmap,shm_open */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#define PRINT_INTX(e) printf("%s=0x%X\n",#e,e)
typedef struct mmap_fd{
int fd; /* 打開後的文件描述符*/
void * base; /* 在進程內部的地址*/
char * name; /* 共享內存名字 */
int len ; /* 共享內存長度 */
int offset; /* fd中的偏移量*/
}MMAP_FD;
#define MMAP_ADDR(p) (p)->base
MMAP_FD * shm_map(char * name,int len,int offset)
{
MMAP_FD * p_fd;
void * base;
int fd;
//第一步:創建一個共享內存
fd = shm_open(name,O_CREAT|O_EXCL|O_RDWR,666);
if(fd == -1)
{
if(errno == EEXIST)
{
fd = shm_open(name,O_RDWR,666);
}
}
if(fd == -1)
{
perror("shm_open");
return NULL;
}
//第二步:設置內存大小
if(ftruncate(fd,len) == -1)
{
perror("ftruncate");
shm_unlink(name);
return NULL;
}
//第三步,將內核中共享內存映射到進程空間之上
//void *mmap(void *start, size_t length, int prot, int flags,
// int fd, off_t offset);
base = mmap(NULL,len,PROT_READ | PROT_WRITE, MAP_SHARED ,fd,offset);
if(base == MAP_FAILED)
{
perror("mmap");
shm_unlink(name);
return NULL;
}
p_fd = malloc(sizeof(MMAP_FD));
p_fd->len = len;
p_fd->name = strdup(name);
p_fd->fd = fd;
p_fd->base = base;
p_fd->offset = offset;
return p_fd;
}
int shm_destroy(MMAP_FD * p_fd)
{
if(p_fd == NULL)
return -1;
//取消映射
if(munmap(p_fd->base,p_fd->len) == -1)
{
perror("munmap");
return -1;
}
//刪除共享內存
if(shm_unlink(p_fd->name) == -1)
{
perror("shm_unlink");
return -2;
}
free(p_fd->name);
free(p_fd);
return 0;
}
#define SHM_NAME "test_mmap"
MMAP_FD * p_fd = NULL;
void exit_handler(int sig)
{
printf("EXIT HANDLER\n");
shm_destroy(p_fd);
exit(0);
}
//創建進程,用於寫數據
void write_proc()
{
char ary[10];
char * p ;
static int count = 1;
printf("WRITE SHM\n");
signal(SIGINT,exit_handler);
signal(SIGTERM,exit_handler);
p_fd = shm_map(SHM_NAME,100,0);
if(p_fd == NULL)
return ;
p= malloc(10);
//注意各種變量地址,ary空間是棧,p是堆的,p_fd->base是內存映射地址
PRINT_INTX(ary);
PRINT_INTX(MMAP_ADDR(p_fd));
PRINT_INTX(p);
free(p);
while(1)
{
snprintf(MMAP_ADDR(p_fd),p_fd->len,"mmap write %d\n",count++);
sleep(1);
}
}
void read_proc()
{
signal(SIGINT,exit_handler);
signal(SIGTERM,exit_handler);
printf("READ SHM\n");
p_fd = shm_map(SHM_NAME,100,0);
if(p_fd == NULL)
return ;
while(1)
{
printf(MMAP_ADDR(p_fd));
sleep(1);
}
}
int main(int argc,char * argv[])
{
if((argc>1) && (argv[1][0] == 'r'))
read_proc();
else
write_proc();
}
本文出自 “驿落黃昏” 博客,請務必保留此出處http://yiluohuanghun.blog.51cto.com/3407300/1081585