我接觸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的具體實現。