Ⅰ C內存共享進程通信範例
mmap()範例
下面將給出使用mmap()的兩個範例:範例1給出兩個進程通過映射普通文件實現共享內存通信;範例2給出父子進程通過匿名映射實現共享內存。系統調用mmap()有許多有趣的地方,下面是通過mmap()映射普通文件實現進程間的通信的範例,我們通過該範例來說明mmap()實現共享內存的特點及注意事項。
範例1:兩個進程通過映射普通文件實現共享內存通信
範例1包含兩個子程序:map_normalfile1.c及map_normalfile2.c。編譯兩個程序,可執行文件分別為map_normalfile1及map_normalfile2。兩個程序通過命令行參數指定同一個文件來實現共享內存方式的進程間通信。map_normalfile2試圖打開命令行參數指定的一個普通文件,把該文件映射到進程的地址空間,並對映射後的地址空間進行寫操作。map_normalfile1把命令行參數指定的文件映射到進程地址空間,然後對映射後的地址空間執行讀操作。這樣,兩個進程通過命令行參數指定同一個文件來實現共享內存方式的進程間通信。
下面是兩個程序代碼:
/*-------------map_normalfile1.c-----------*/
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
typedef struct{
char name[4];
int age;
}people;
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,00777);
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++)
{
temp += 1;
memcpy( ( *(p_map+i) ).name, &temp,2 );
( *(p_map+i) ).age = 20+i;
}
printf(" initialize over \n ");
sleep(10);
munmap( p_map, sizeof(people)*10 );
printf( "umap ok \n" );
}
/*-------------map_normalfile2.c-----------*/
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
typedef struct{
char name[4];
int age;
}people;
main(int argc, char** argv) // map a normal file as shared mem:
{
int fd,i;
people *p_map;
fd=open( argv[1],O_CREAT|O_RDWR,00777 );
p_map = (people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
for(i = 0;i<10;i++)
{
printf( "name: %s age %d;\n",(*(p_map+i)).name, (*(p_map+i)).age );
}
munmap( p_map,sizeof(people)*10 );
}
map_normalfile1.c首先定義了一個people數據結構,(在這里採用數據結構的方式是因為,共享內存區的數據往往是有固定格式的,這由通信的各個進程決定,採用結構的方式有普遍代表性)。map_normfile1首先打開或創建一個文件,並把文件的長度設置為5個people結構大小。然後從mmap()的返回地址開始,設置了10個people結構。然後,進程睡眠10秒鍾,等待其他進程映射同一個文件,最後解除映射。
map_normfile2.c只是簡單的映射一個文件,並以people數據結構的格式從mmap()返回的地址處讀取10個people結構,並輸出讀取的值,然後解除映射。
分別把兩個程序編譯成可執行文件map_normalfile1和map_normalfile2後,在一個終端上先運行./map_normalfile2 /tmp/test_shm,程序輸出結果如下:
initialize over
umap ok
在map_normalfile1輸出initialize over 之後,輸出umap ok之前,在另一個終端上運行map_normalfile2 /tmp/test_shm,將會產生如下輸出(為了節省空間,輸出結果為稍作整理後的結果):
name: b age 20; name: c age 21; name: d age 22; name: e age 23; name: f age 24;
name: g age 25; name: h age 26; name: I age 27; name: j age 28; name: k age 29;
在map_normalfile1 輸出umap ok後,運行map_normalfile2則輸出如下結果:
name: b age 20; name: c age 21; name: d age 22; name: e age 23; name: f age 24;
name: age 0; name: age 0; name: age 0; name: age 0; name: age 0;
從程序的運行結果中可以得出的結論
1、 最終被映射文件的內容的長度不會超過文件本身的初始大小,即映射不能改變文件的大小;
2、 可以用於進程通信的有效地址空間大小大體上受限於被映射文件的大小,但不完全受限於文件大小。打開文件被截短為5個people結構大小,而在map_normalfile1中初始化了10個people數據結構,在恰當時候(map_normalfile1輸出initialize over 之後,輸出umap ok之前)調用map_normalfile2會發現map_normalfile2將輸出全部10個people結構的值,後面將給出詳細討論。
註:在linux中,內存的保護是以頁為基本單位的,即使被映射文件只有一個位元組大小,內核也會為映射分配一個頁面大小的內存。當被映射文件小於一個頁面大小時,進程可以對從mmap()返回地址開始的一個頁面大小進行訪問,而不會出錯;但是,如果對一個頁面以外的地址空間進行訪問,則導致錯誤發生,後面將進一步描述。因此,可用於進程間通信的有效地址空間大小不會超過文件大小及一個頁面大小的和。
3、 文件一旦被映射後,調用mmap()的進程對返回地址的訪問是對某一內存區域的訪問,暫時脫離了磁碟上文件的影響。所有對mmap()返回地址空間的操作只在內存中有意義,只有在調用了munmap()後或者msync()時,才把內存中的相應內容寫回磁碟文件,所寫內容仍然不能超過文件的大小。
範例2:父子進程通過匿名映射實現共享內存
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
typedef struct{
char name[4];
int age;
}people;
main(int argc, char** argv)
{
int i;
people *p_map;
char temp;
p_map=(people*)mmap(NULL,sizeof(people)*10,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
if(fork() == 0)
{
sleep(2);
for(i = 0;i<5;i++)
printf("child read: the %d people's age is %d\n",i+1,(*(p_map+i)).age);
(*p_map).age = 100;
munmap(p_map,sizeof(people)*10); //實際上,進程終止時,會自動解除映射。
exit();
}
temp = 'a';
for(i = 0;i<5;i++)
{
temp += 1;
memcpy((*(p_map+i)).name, &temp,2);
(*(p_map+i)).age=20+i;
}
sleep(5);
printf( "parent read: the first people,s age is %d\n",(*p_map).age );
printf("umap\n");
munmap( p_map,sizeof(people)*10 );
printf( "umap ok\n" );
}
考察程序的輸出結果,體會父子進程匿名共享內存:
child read: the 1 people's age is 20
child read: the 2 people's age is 21
child read: the 3 people's age is 22
child read: the 4 people's age is 23
child read: the 5 people's age is 24
parent read: the first people,s age is 100
umap
umap ok
回頁首
四、對mmap()返回地址的訪問
前面對範例運行結構的討論中已經提到,linux採用的是頁式管理機制。對於用mmap()映射普通文件來說,進程會在自己的地址空間新增一塊空間,空間大小由mmap()的len參數指定,注意,進程並不一定能夠對全部新增空間都能進行有效訪問。進程能夠訪問的有效地址大小取決於文件被映射部分的大小。簡單的說,能夠容納文件被映射部分大小的最少頁面個數決定了進程從mmap()返回的地址開始,能夠有效訪問的地址空間大小。超過這個空間大小,內核會根據超過的嚴重程度返回發送不同的信號給進程。可用如下圖示說明:
注意:文件被映射部分而不是整個文件決定了進程能夠訪問的空間大小,另外,如果指定文件的偏移部分,一定要注意為頁面大小的整數倍。下面是對進程映射地址空間的訪問範例:
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
typedef struct{
char name[4];
int age;
}people;
main(int argc, char** argv)
{
int fd,i;
int pagesize,offset;
people *p_map;
pagesize = sysconf(_SC_PAGESIZE);
printf("pagesize is %d\n",pagesize);
fd = open(argv[1],O_CREAT|O_RDWR|O_TRUNC,00777);
lseek(fd,pagesize*2-100,SEEK_SET);
write(fd,"",1);
offset = 0; //此處offset = 0編譯成版本1;offset = pagesize編譯成版本2
p_map = (people*)mmap(NULL,pagesize*3,PROT_READ|PROT_WRITE,MAP_SHARED,fd,offset);
close(fd);
for(i = 1; i<10; i++)
{
(*(p_map+pagesize/sizeof(people)*i-2)).age = 100;
printf("access page %d over\n",i);
(*(p_map+pagesize/sizeof(people)*i-1)).age = 100;
printf("access page %d edge over, now begin to access page %d\n",i, i+1);
(*(p_map+pagesize/sizeof(people)*i)).age = 100;
printf("access page %d over\n",i+1);
}
munmap(p_map,sizeof(people)*10);
}
如程序中所注釋的那樣,把程序編譯成兩個版本,兩個版本主要體現在文件被映射部分的大小不同。文件的大小介於一個頁面與兩個頁面之間(大小為:pagesize*2-99),版本1的被映射部分是整個文件,版本2的文件被映射部分是文件大小減去一個頁面後的剩餘部分,不到一個頁面大小(大小為:pagesize-99)。程序中試圖訪問每一個頁面邊界,兩個版本都試圖在進程空間中映射pagesize*3的位元組數。
版本1的輸出結果如下:
pagesize is 4096
access page 1 over
access page 1 edge over, now begin to access page 2
access page 2 over
access page 2 over
access page 2 edge over, now begin to access page 3
Bus error //被映射文件在進程空間中覆蓋了兩個頁面,此時,進程試圖訪問第三個頁面
版本2的輸出結果如下:
pagesize is 4096
access page 1 over
access page 1 edge over, now begin to access page 2
Bus error //被映射文件在進程空間中覆蓋了一個頁面,此時,進程試圖訪問第二個頁面
結論:採用系統調用mmap()實現進程間通信是很方便的,在應用層上介面非常簡潔。內部實現機制區涉及到了linux存儲管理以及文件系統等方面的內容,可以參考一下相關重要數據結構來加深理解。在本專題的後面部分,將介紹系統v共享內存的實現。
參考資料
[1] Understanding the Linux Kernel, 2nd Edition, By Daniel P. Bovet, Marco Cesati , 對各主題闡述得重點突出,脈絡清晰。
[2] UNIX網路編程第二卷:進程間通信,作者:W.Richard Stevens,譯者:楊繼張,清華大學出版社。對mmap()有詳細闡述。
[3] Linux內核源代碼情景分析(上),毛德操、胡希明著,浙江大學出版社,給出了mmap()相關的源代碼分析。
[4]mmap()手冊
Ⅱ UE4對象系統_序列化和uasset文件格式
虛幻的序列化這塊是個人比較喜歡的技術點,個人在工作中也山寨了個簡化版,UE4延續了UE3的序列化方案。它使用了訪問者模式(Vistor Pattern),將序列化的存檔介面抽象化,其中FArchive為訪問者, 其它實現了void Serialize( FArchive& Ar )介面的類為被訪問者。FArchive可以是磁碟文件訪問, 內存統計,對象統計等功能。
FArchive的類繼承體系如下:
定義介面如下:
通過重載operater <<來實現對數據的訪問。
下面為調試時的幾張堆棧圖:
UE中使用統一的格式存儲資源(uasset, umap),每個uasset對應一個包(package),存儲一個UPackage對象時,會將該包下的所有對象都存到uasset中。UE的uasset文件格式很像Windows下的DLL文件格式(PE格式),並且使用起來神似(下一節分析Linker)。
導入表條目FObjectImport
導出表的條目FObjectExport
Ⅲ umap後啜是什麼文件需要用什麼打開
第一,UMAP是游戲自己定義的私有文件類型,並不存在公開的打開方式,如果你懂得解包技術,又知道這個游戲是用什麼方式
封包
的,或者可以試試。
第二,一般的網游都有文件保護的,你的想法基本是不可能實現,要
黑白色
也簡單,在
顯卡驅動
里設置一下就行了,但網游不載入地圖的話,肯定進不去。
Ⅳ UE4基礎知識總結
一、游戲架構
1.Pawn 是作為世界中的一個 「代理」的Actor。Pawn可以由控制器處理,它們可以輕松地接受輸入,並且可以執行各種各樣的類似於玩家的動作。
2.Character是類人的Pawn。它默認包含一個用於碰撞的CapsuleComponent(膠囊體組件)和CharacterMovementComponent (角色運動組件)它可以進行基本的擬人運動,
它可以平滑地在網格上復制運動,並且它具有一些動畫相關的功能。
3.Controller是負責管理Pawn的Actor,分為AIController和PlayerController。其中PlayerController(玩家控制器)是Pawn和控制它的人類玩家間的介面。PlayerController本質上代表了人類玩家的意願。
AIController正如其名,是控制Pawn的一個模擬「意願」。
4.HUD 是一種「平頭顯示信息」,或者說是二維的屏幕顯示信息,在很多游戲中都很常見。PlayerCameraManager是玩家的眼睛,管理玩家如何表現。每個PlayerController一般也具有一個這樣的類。
5.游戲這個概念劃分為兩個類。Game Mode 和 Game State 是游戲的定義,包括像游戲規則及獲勝條件這樣的內容。
其中Game Mode僅存在於伺服器上。它一般在游戲過程中不會有太多數據改變,並且它一定不應該具有客戶端需要的臨時數據。
GameState 包含了游戲狀態,它存在於伺服器和所有客戶端上,可以自由地進行復制來保持同步。
PlayerState是游戲中的一個參與者的狀態,比如人類玩家或者模擬人類玩家的機器人。所有玩家的PlayerStates在所有機器上都存在,並且可以自由地進行復制來保持同步。
6.總結:一個游戲由GameMode和GameState構成。加入游戲的人類玩家同PlayerController相關聯。
這些PlayerController允許玩家在游戲中佔有pawn,以便它們在游戲中有物理表示。
PlayerController也為玩家提供了輸入控制、平頭顯示信息或HUD、 及處理相機視圖的PlayerCameraManager。
二、虛幻項目
1.項目的所有內容均包含在項目目錄中。可創建任意數量的項目,但每個均為自含式。使用虛幻引擎的 項目瀏覽器(Project Browser)進行創建新項目將設置必要的項目框架,
如目錄結構和可在編輯器中打開的虛幻項目文件([ProjectName].uproject)。
2.項目所包含的資源作為 .uasset 文件存儲在 Content 文件夾中。這些資源包括材質、靜態和骨骼網格體、藍圖、聲音提示,以及紋理。它們是可重復使用的參考物質和模板,可被項目中的對象調用。
3.項目中還包括關卡。關卡通常被稱作地圖,作為 .umap 文件存儲在 Content 文件夾中。在虛幻編輯器中,每次可針對一個關卡進行操作,關卡將顯示在視口中。
4.關卡中可放置Actor。Actor 是一個游戲性實體,(通常)包含一個或多個組件。