進程間通訊---共享內存(mmap)

已經很晚了,但是今天對代碼的理解碰到點問題,就擱到如今,並且問題還沒解決,在吹牛以前先把問題擺出來吧
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char** argv)
{
  int fd;
  
  fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,0777);
  lseek(fd,100,SEEK_SET);
  write(fd,"",1);
  close( fd );
  
  return 0;
}
這個函數的做用是建立一個大小爲100的文件
執行以下:
./test /home/nsl/myprogram/shmfile
運行此函數,結果獲得的文件還真的是大小爲100,但是不知道該如何理解,從程序上看不過是往99的位置處寫了個'\0'而已,但是我看有人說文件大小和文件內容徹底無關,在linux系統中由inode記錄文件長度和文件中的塊數,若是讀到最後一塊,說明文件結束
還請高手指點
我看了一下apue,對mmap的介紹不是很好理解,因此從網上找了點資料
本人下面所寫的東西也是在理解這篇文章的東西上寫的,可是畢竟本身重寫一遍會好理解一些,因此不辭辛勞
爲何要用共享內存呢?爲何不直接把數據寫到一個文件裏,再從一個文件讀呢?
由於mmap和memcpy快啊,cpu時間大概是普通的read/write的一半(在linux中)
當要進行重複的數據傳輸操做時,用這個好
mmap
void* mmap(void* addr,size_t len,int prot,int flags,int fd,off_t offset)
addr:指定文件應被映射到進程空間的起始地址,通常被指定一個空指針,此時選擇起始地址的任務留給內核來完成
len:映射到調用進程地址空間的字節數,它從被映射文件開頭offset個字節開始算起
prot:指定共享內存的訪問權限,可取以下值: PROT_READ,PROT_WRITE,PROT_EXEC,PROT_NONE
flags:包括MAP_SHARED,MAP_PRIVATE,MAP_FIXED,其中MAP_SHARED,MAP_PRIVATE必選其一,而MAP_FIXED則不推薦使用
fd:即將映射到進程空間的文字描述子,通常由open()返回,同時fd能夠爲-1,表示匿名映射,用於親緣關係的進程之間
offset:通常設置爲0,表示從文件的開頭處開始映射
返回值:爲最後文件映射到進程空間的地址,進程可直接操做起始地址爲該值的有效地址
如需參考資料,請輸命令:man mmap
能夠看出來,文件mmap之後,磁盤文件就再也不是普通的文件,其實這個普通的文件描述字已經能夠關掉,對這個文件的訪問已經能夠像訪問普通內存同樣去訪問了,其實數據在沒有解除映射以前,這些數據也是不寫到磁盤文件中去的。
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>

typedef struct
{
  char name;
  int    age;
}people;

int main(int argc, char** argv) // map a normal file as shared mem:
{
  int fd,i;
  people *p_map;
  char temp;
  
  fd=open(argv[1],O_CREAT|O_RDWR|O_TRUNC,0777);
  //O_CREAT:沒有就建立的形式打開
  //O_RDWR:讀寫的方式打開
  //O_TRUNC:若是文件存在且以寫方式打開,則清空文件內容和文件長度
  printf( "sizeof(people) = %d\n",sizeof(people));
  lseek(fd,sizeof(people)*5-1,SEEK_SET);
  write(fd,"",1);
  p_map = (people*) mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0 );
  close( fd );//映射完了以後磁盤文件就能夠關掉了
  temp = 'a';
   for(i=0; i<10; i++)
  {
        (*(p_map+i)).name = temp ;
    temp += 1;
    ( *(p_map+i) ).age = 20+i;
  }
  printf( " initialize over \n ");
  sleep(10);
  munmap( p_map, sizeof(people)*10 );
  printf( "umap ok \n" );
  
  return 0;
}
注意,mmap那一句其實獲得的實際的地址,而後對地址進行(people *)強制類型轉換,使得這個的p_map指針就像malloc過同樣,指向了一塊內存的起始地址
把這個編譯爲test1
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>

typedef struct
{
  char name;
  int    age;
}people;

int main(int argc, char** argv)  
{
  int fd,i;
  people *p_map;

  fd=open( argv[1],O_CREAT|O_RDWR,0777 );
  p_map = (people*)mmap( NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
   for(i = 0;i<10;i++)
  {
                printf( "name: %c age %d;\n",(*(p_map+i)).name, (*(p_map+i)).age );
  }
  munmap( p_map,sizeof(people)*10 );
  
  return 0;
}
把這個程序編譯爲test2
執行狀況:
./test1 /home/nsl/myprogram/shm_file & (轉入後臺運行)
./test2 /home/nsl/myprogram/shm_file
在test1還沒munmap以前:
name: a age 20;
name: b age 21;
name: c age 22;
name: d age 23;
name: e age 24;
name: f age 25;
name: g age 26;
name: h age 27;
name: i age 28;
name: j age 29;
在test1被munmap以後:
name: a age 20;
name: b age 21;
name: c age 22;
name: d age 23;
name: e age 24;
name:  age 0;
name:  age 0;
name:  age 0;
name:  age 0;
name:  age 0;
可見,在共享文件只有5個people那麼大的時候映射10個people沒有返回錯誤,應爲,映射文件時按頁的,也就說要映射的剩餘大小小於一頁的時候,通通分配一晚上,因此映射10個也沒有錯,而且進行的數據的操做也沒有返回錯誤,由於對地址的操做還在那一頁當中,若是超出頁,那就會出錯
當munmap時,那就把數據寫入到磁盤文件中去了,由於磁盤文件的大小隻有5個people,因此對共享內存數據進行了截斷,因此test2再去讀,就只有5個people的大小了
因此,映射的大小最好不要超過文件的大小
另外
頁的大小是多少,怎麼查看?不會,故寫了個程序
#include <unistd.h>
int main()
{
  int a = getpagesize();
  printf ( "%d\n",a); }
獲得的返回時4096,個人頁大小是4096字節
相關文章
相關標籤/搜索