摘要:前言我們知道,由于沒有多線程模型,所以更多的使用多進(jìn)程模型,因此代碼相對來說更加簡潔,減少了各種線程鎖的阻塞與同步,但是也帶來了新的問題數(shù)據(jù)同步。相比多線程之前可以直接共享進(jìn)程的內(nèi)存,進(jìn)程之間數(shù)據(jù)的相互同步依賴于共享內(nèi)存。
前言
我們知道,由于 PHP 沒有多線程模型,所以 swoole 更多的使用多進(jìn)程模型,因此代碼相對來說更加簡潔,減少了各種線程鎖的阻塞與同步,但是也帶來了新的問題:數(shù)據(jù)同步。相比多線程之前可以直接共享進(jìn)程的內(nèi)存,進(jìn)程之間數(shù)據(jù)的相互同步依賴于共享內(nèi)存。本文將會講解 swoole 中共享內(nèi)存的源碼。
前置知識:
mmap 函數(shù)的使用: APUE 學(xué)習(xí)筆記——高級 IO
共享內(nèi)存: APUE 學(xué)習(xí)筆記——進(jìn)程間通信
共享內(nèi)存數(shù)據(jù)結(jié)構(gòu)typedef struct _swShareMemory_mmap { size_t size; char mapfile[SW_SHM_MMAP_FILE_LEN]; int tmpfd; int key; int shmid; void *mem; } swShareMemory;
注意 mem 是一個 void 類型的指針,用于存放共享內(nèi)存的首地址。這個成員變量相當(dāng)于面向?qū)ο笾械?this 指針,通過它就可以訪問到 swShareMemory 的各個成員。
size 代表共享內(nèi)存的大小(不包括 swShareMemory 結(jié)構(gòu)體大小), mapfile[] 代表共享內(nèi)存使用的內(nèi)存映射文件的文件名, tmpfd 為內(nèi)存映射文件的描述符。key 代表使用 System V 的 shm 系列函數(shù)創(chuàng)建的共享內(nèi)存的 key 值, shmid 為 shm 系列函數(shù)創(chuàng)建的共享內(nèi)存的 id(類似于fd),這兩個由于不是 POSIX 標(biāo)準(zhǔn)定義的 api,用途有限。
共享內(nèi)存的申請與創(chuàng)建swoole 在申請共享內(nèi)存時常常調(diào)用的函數(shù)是 sw_shm_malloc,這個函數(shù)可以為進(jìn)程匿名申請一大塊連續(xù)的共享內(nèi)存:
void* sw_shm_malloc(size_t size) { swShareMemory object; void *mem; size += sizeof(swShareMemory); mem = swShareMemory_mmap_create(&object, size, NULL); if (mem == NULL) { return NULL; } else { memcpy(mem, &object, sizeof(swShareMemory)); return mem + sizeof(swShareMemory); } }
從 sw_shm_malloc 函數(shù)可以看出,雖然我們申請的是 size,但是實際申請的內(nèi)存是要略大的,因為還要加上 swShareMemory 這個結(jié)構(gòu)體。當(dāng)函數(shù)返回時,也不會直接返回申請的內(nèi)存首地址,而是復(fù)制了 object 各個成員變量的值后,在申請的首地址上加上 swShareMemory 的大小。
void *swShareMemory_mmap_create(swShareMemory *object, size_t size, char *mapfile) { void *mem; int tmpfd = -1; int flag = MAP_SHARED; bzero(object, sizeof(swShareMemory)); #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 #if defined(SW_USE_HUGEPAGE) && defined(MAP_HUGETLB) if (size > 2 * 1024 * 1024) { flag |= MAP_HUGETLB; } #endif mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flag, tmpfd, 0); #ifdef MAP_FAILED if (mem == MAP_FAILED) #else if (!mem) #endif { swWarn("mmap(%ld) failed. Error: %s[%d]", size, strerror(errno), errno); return NULL; } else { object->size = size; object->mem = mem; return mem; } }
由于 swoole 的各個進(jìn)程都是由 master 進(jìn)程所建立,也就是各個進(jìn)程之間存在親戚關(guān)系, 因此swShareMemory_mmap_create 函數(shù)直接以 匿名映射 、(/dev/zero 設(shè)備) 的方式利用 mmap 建立共享內(nèi)存,并沒有 open 具體的共享內(nèi)存文件,或者調(diào)用 shm_open 打開 POSIX IPC 名字。
值得注意的是 MAP_HUGETLB,這個是 linux 內(nèi)核 2.6.32 引入的一個 flags,用于使用大頁面分配共享內(nèi)存。大頁是相對傳統(tǒng) 4K 小頁而言的,一般來說常見的體系架構(gòu)都會提供2種大頁大小,比如常見的 2M 大頁和 1G 大頁。使用大頁可以減少頁表項數(shù)量,從而減少 TLB Miss 的概率,提升系統(tǒng)訪存性能。當(dāng)然有利必有弊,使用大頁降低了內(nèi)存管理的粒度和靈活性,如果程序并不是對內(nèi)存的使用量特別大,使用大頁還可能造成內(nèi)存的浪費(fèi)。
共享內(nèi)存的 calloccalloc 與 malloc 大同小異,無非多了一個 num 參數(shù)
void* sw_shm_calloc(size_t num, size_t _size) { swShareMemory object; void *mem; void *ret_mem; int size = sizeof(swShareMemory) + (num * _size); mem = swShareMemory_mmap_create(&object, size, NULL); if (mem == NULL) { return NULL; } else { memcpy(mem, &object, sizeof(swShareMemory)); ret_mem = mem + sizeof(swShareMemory); bzero(ret_mem, size - sizeof(swShareMemory)); return ret_mem; } }共享內(nèi)存的 realloc
realloc 函數(shù)用于修改已申請的內(nèi)存大小,邏輯非常簡單,先申請新的內(nèi)存,進(jìn)行復(fù)制后,再釋放舊的內(nèi)存:
void* sw_shm_realloc(void *ptr, size_t new_size) { swShareMemory *object = ptr - sizeof(swShareMemory); void *new_ptr; new_ptr = sw_shm_malloc(new_size); if (new_ptr == NULL) { return NULL; } else { memcpy(new_ptr, ptr, object->size); sw_shm_free(ptr); return new_ptr; } }修改共享內(nèi)存的權(quán)限
在內(nèi)存映射完成后,由標(biāo)記讀、寫、執(zhí)行權(quán)限的 PROT_READ、PROT_WRITE 和 PROT_EXEC 等權(quán)限仍可以被 mprotect 系統(tǒng)調(diào)用所修改。
int sw_shm_protect(void *addr, int flags) { swShareMemory *object = (swShareMemory *) (addr - sizeof(swShareMemory)); return mprotect(object, object->size, flags); }共享內(nèi)存的釋放
void sw_shm_free(void *ptr) { swShareMemory *object = ptr - sizeof(swShareMemory); swShareMemory_mmap_free(object); } int swShareMemory_mmap_free(swShareMemory *object) { return munmap(object->mem, object->size); }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/29198.html
摘要:前言內(nèi)存數(shù)據(jù)結(jié)構(gòu),類似于的通道,底層基于共享內(nèi)存互斥鎖實現(xiàn),可實現(xiàn)用戶態(tài)的高性能內(nèi)存隊列。是當(dāng)前隊列占用的內(nèi)存大小,用來指定是否使用共享內(nèi)存是否使用鎖是否使用通知。 前言 內(nèi)存數(shù)據(jù)結(jié)構(gòu) Channel,類似于 Go 的 chan 通道,底層基于 共享內(nèi)存 + Mutex 互斥鎖實現(xiàn),可實現(xiàn)用戶態(tài)的高性能內(nèi)存隊列。Channel 可用于多進(jìn)程環(huán)境下,底層在讀取寫入時會自動加鎖,應(yīng)用層不需...
摘要:如果互斥鎖的持有者死亡了,或者持有這樣的互斥鎖的進(jìn)程了互斥鎖所在的共享內(nèi)存或者持有這樣的互斥鎖的進(jìn)程執(zhí)行了調(diào)用,則會解除鎖定該互斥鎖?;コ怄i的下一個持有者將獲取該互斥鎖并返回錯誤。 前言 swoole_table 一個基于共享內(nèi)存和鎖實現(xiàn)的超高性能,并發(fā)數(shù)據(jù)結(jié)構(gòu)。用于解決多進(jìn)程/多線程數(shù)據(jù)共享和同步加鎖問題。 swoole_table 的數(shù)據(jù)結(jié)構(gòu) swoole_table 實際上...
摘要:前言中為了更好的進(jìn)行內(nèi)存管理,減少頻繁分配釋放內(nèi)存空間造成的損耗和內(nèi)存碎片,程序設(shè)計并實現(xiàn)了三種不同功能的內(nèi)存池,和。比較特殊的是單鏈表內(nèi)存池的內(nèi)存只能增加不會減少。 前言 Swoole 中為了更好的進(jìn)行內(nèi)存管理,減少頻繁分配釋放內(nèi)存空間造成的損耗和內(nèi)存碎片,程序設(shè)計并實現(xiàn)了三種不同功能的內(nèi)存池:FixedPool,RingBuffer 和 MemoryGlobal。 其中 Memor...
摘要:的數(shù)據(jù)結(jié)構(gòu)數(shù)據(jù)結(jié)構(gòu)中是鏈表元素的個數(shù),是緩沖區(qū)創(chuàng)建時,鏈表元素約定的大小實際大小不一定是這個值,是實際上緩沖區(qū)占用的內(nèi)存總大小。中的有三種,分別應(yīng)用于緩存數(shù)據(jù)發(fā)送文件提醒連接關(guān)閉三種情景。指的是元素的內(nèi)存大小。 前言 swoole 中數(shù)據(jù)的接受與發(fā)送(例如 reactor 線程接受客戶端消息、發(fā)送給客戶端的消息、接受到的來自 worker 的消息、要發(fā)送給 worker 的消息等等)都...
摘要:如果在調(diào)用之前我們設(shè)置了,但是不在第二個進(jìn)程啟動前這個套接字,那么第二個進(jìn)程仍然會在調(diào)用函數(shù)的時候出錯。 前言 本節(jié)主要介紹 server 模塊進(jìn)行初始化的代碼,關(guān)于初始化過程中,各個屬性的意義,可以參考官方文檔: SERVER 配置選項 關(guān)于初始化過程中,用于監(jiān)聽的 socket 綁定問題,可以參考: UNP 學(xué)習(xí)筆記——基本 TCP 套接字編程 UNP 學(xué)習(xí)筆記——套接字選項 構(gòu)造...
閱讀 2035·2021-11-15 18:09
閱讀 927·2021-09-06 15:13
閱讀 2659·2021-08-23 09:43
閱讀 2043·2019-08-30 15:54
閱讀 2238·2019-08-30 13:56
閱讀 2504·2019-08-26 11:31
閱讀 3100·2019-08-26 10:56
閱讀 726·2019-08-26 10:28