歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> Linux的共享內存及內存映射

Linux的共享內存及內存映射

日期:2017/3/3 16:27:07   编辑:關於Linux

一.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

Copyright © Linux教程網 All Rights Reserved