Swoole源碼學習記錄(一)——進程間共享數據ShareMemory 和 MemoryPool

我接觸PHP的時間不長,最開始只認爲PHP是用來做網站開發,是一個比JSP要簡單的語言。後來,因爲工作需要,一位學長建議我使用Ngnix + PHP 搭建應用服務器,並建議使用現有的框架。一番搜索之下,我意外發現了Swoole http://www.swoole.com/ 

接下來的半年裏,我一直使用Swoole擴展作爲我的服務器核心。Swoole穩定而高效的性能以及優秀的架構設計使我的開發變得簡單、高效。因此,我希望能夠更加深入的瞭解Swoole的核心,學習它的架構設計,也能鍛鍊自己的C語言能力。

因此,我將不定期更細這一系列,將我在閱讀、理解Swoole源碼過程中的心得體會記錄下來,也希望我的記錄能幫助那些同樣希望理解Swoole源碼的人。本人能力有限,C語言能力也只能說剛剛入門,難免會有誤解的地方,希望大家能及時指正。

 ———————————— 正文分割線 —————————————

swoole版本:1.7.4-stable

Github地址:https://github.com/LinkedDestiny/swoole-src-analysis

Swoole沒有采用多線程模型而是使用的多進程模型,在一定程度上減少了訪問數據時加鎖解鎖的開銷,但同時也引入了新的需求:共享內存。

直接上源碼吧,在Swoole中,對於ShareMemory的聲明在swoole.h464行-478行,如下:

#define SW_SHM_MMAP_FILE_LEN  64

typedef struct  _swShareMemory_mmap

{

         int size;

         char mapfile[SW_SHM_MMAP_FILE_LEN];

         int tmpfd;

         int key;

         int shmid;

         void *mem;

}swShareMemory;

void* swShareMemory_mmap_create(swShareMemory *object, int size, char *mapfile);

int swShareMemory_mmap_free(swShareMemory *object);

void* swShareMemory_sysv_create(swShareMemory *object, int size, int key);

int swShareMemory_sysv_free(swShareMemory *object, int rm);


    這裏,Rango聲明瞭一個結構體swShareMemory表示共享內存的結構。在內存中,一個完整的swShareMemory存儲形式如下:


其中,首部swShareMemory存放了共享內存的相關信息,尾部的void *mem指針指向共享內存的起始地址。int size代表共享內存的大小(不包括swShareMemory結構體大小),char mapfile[]代表共享內存使用的內存映射文件的文件名,int tmpfd爲內存映射文件的描述符,int key代表使用Linux自帶的shm系列函數創建的共享內存的key值,int shmid爲shm系列函數創建的共享內存的id(類似於fd)。

 

         接下來看對應的四個處理函數,從名字上可以區分,mmap代表使用內存映射文件的共享內存,sysv代表使用shm系列函數的共享內存。這四個函數的實現寫在ShareMemory.c文件中。這裏重點解析swShareMemory_mmap_create 方法和swShareMemory_sysv_create方法。

下面貼上swShareMemory_mmap_create的核心代碼:

#ifdef MAP_ANONYMOUS

         flag |= MAP_ANONYMOUS;

#else

         if(mapfile == NULL)

         {

                   mapfile ="/dev/zero";

         }

         if((tmpfd = open(mapfile, O_RDWR)) <0)

         {

                   return NULL;

         }

         strncpy(object->mapfile, mapfile,SW_SHM_MMAP_FILE_LEN);

         object->tmpfd = tmpfd;

#endif

 

         mem = mmap(NULL, size, PROT_READ |PROT_WRITE, flag, tmpfd, 0);


 

先解釋一下MAP_ANONYMOUS宏,該宏定義在 mman-linux.h 內,定義如下:

#  define MAP_ANONYMOUS        0x20          /* Don'tuse a file.  */


使用這個宏代表使用mmap生成的映射區不與任何文件關聯。我的理解是:此處創建一個僅存在與內存中虛擬文件,用來存放需要共享的內容。

其次解釋 /dev/zero 。熟悉Linux的應該都知道 /dev/zero 和/de/null , 這是Linux系統中的兩個特殊文件,/dev/zero的特性是,所有寫入該文件的數據都會消失,如果從該文件中讀取內容,只能讀取到連續的‘\0’。

源碼解釋:

         如果定義了MAP_ANONYMOUS 宏,則添加進flag中。否則,判定mapfile是否爲空,如果爲空,則指定mapfile爲/dev/zero ,然後打開文件,將描述符賦值給tmpfd。最後,通過mmap打開大小爲size的映射內存,指定內存可讀可寫,並將內存地址賦值給mem。

 

swShareMemory_sysv_create的核心源碼:

         

<span style="white-space:pre">	</span>if (key == 0)

         {

                   key = IPC_PRIVATE;

         }

         if ((shmid = shmget(key, size, SHM_R |SHM_W | IPC_CREAT)) < 0)

         {

                   swWarn("shmget() failed.Error: %s[%d]", strerror(errno), errno);

                   return NULL;

         }

         if ((mem = shmat(shmid, NULL, 0)) <0)

         {

                   swWarn("shmat() failed.Error: %s[%d]", strerror(errno), errno);

                   return NULL;

         }

         else

         {

                   object->key = key;

                   object->shmid = shmid;

                   object->size = size;

                   object->mem = mem;

                   return mem;

         }

源碼解釋:

如果key 爲0,則表示需要創建新的共享內存,則將key賦值爲IPC_PRIVATE 宏。然後,調用shmget方法,如果key爲IPC_PRIVATE,則創建一個大小爲size的可讀可寫的共享內存,並獲取到共享內存標識符shmid,否則獲取到IPC值爲key的共享內存標識符shmid。接着,調用shmat方法獲取到shmid對應的共享內存首地址,並賦值給mem。

 

兩個free方法則是分別調用munmap方法和shmctl方法釋放內存,不再貼上源碼。

 

         時間緣故,這次分析就到這裏,下一篇將介紹Swoole的三種類型的MemoryPool的具體實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章