Heim  >  Artikel  >  Backend-Entwicklung  >  Eine kurze Diskussion des PHP-Quellcodes 30: Speicherschicht im PHP-Speicherpool

Eine kurze Diskussion des PHP-Quellcodes 30: Speicherschicht im PHP-Speicherpool

不言
不言Original
2018-06-29 09:41:411461Durchsuche

Dieser Artikel stellt hauptsächlich den PHP-Quellcode 30 vor: Die Speicherschicht im PHP-Speicherpool hat einen bestimmten Referenzwert. Jetzt können Freunde in Not darauf verweisen

Apropos PHP Quellcode 30: Die Speicherschicht im PHP-Speicherpool

[Übersicht]
Der Speichermanager von PHP ist hierarchisch. Dieser Manager besteht aus drei Schichten: Speicherschicht, Heap-Schicht und Emalloc/Efree-Schicht. Die Speicherschicht beantragt tatsächlich Speicher vom System über Funktionen wie malloc() und mmap() und gibt den angeforderten Speicher über die Funktion free() frei. Die Speicherschicht gilt normalerweise für größere Speicherblöcke. Der hier angewendete große Speicher bedeutet nicht, dass die Speicherschichtstruktur den Speicher benötigt, wenn sie die Zuweisungsmethode aufruft Das Segmentformat ist relativ groß. Die Rolle der Speicherschicht besteht darin, die Speicherzuweisungsmethode für die Heap-Schicht transparent zu machen.

Sehen Sie sich zunächst die Struktur der Speicherschicht an:
[Struktur]

  /* Heaps with user defined storage */
  typedef struct _zend_mm_storage zend_mm_storage; typedef struct _zend_mm_segment {
    size_t    size;
    struct _zend_mm_segment *next_segment;
    } 
    zend_mm_segment; 
    typedef struct _zend_mm_mem_handlers {
    const char *name;
    zend_mm_storage* (*init)(void *params);    //    初始化函数
    void (*dtor)(zend_mm_storage *storage);    //    析构函数
    void (*compact)(zend_mm_storage *storage);
    zend_mm_segment* (*_alloc)(zend_mm_storage *storage, size_t size);    //    内存分配函数
    zend_mm_segment* (*_realloc)(zend_mm_storage *storage, zend_mm_segment *ptr, size_t size);    //    重新分配内存函数
    void (*_free)(zend_mm_storage *storage, zend_mm_segment *ptr);    //    释放内存函数
    } 
    zend_mm_mem_handlers; struct _zend_mm_storage {
    const zend_mm_mem_handlers *handlers;    //    处理函数集
    void *data;};

Die Speicherzuweisungsmethode, die aufgerufene Funktion ist die in der _zend_mm_storage-Struktur festgelegte Verarbeitungsfunktion und der Speicher ist segmentiert. In Form ausgedrückt.
[4 Speicherschemata]
PHP verfügt über 4 Speicherzuweisungsschemata in der Speicherebene: malloc, win32, mmap_anon, mmap_zero. Standardmäßig wird malloc zum Zuweisen von Speicher verwendet Windows-Version und ruft HeapAlloc auf, um Speicher zuzuweisen, die verbleibenden zwei Speicherschemata sind anonyme Speicherzuordnungen und das Speicherschema von PHP kann durch Festlegen von Variablen geändert werden.
Die offizielle Beschreibung lautet wie folgt:
Das Zend MM kann mit den Umgebungsvariablen ZEND_MM_MEM_TYPE und ZEND_MM_SEG_SIZE optimiert werden.
Die Standardwerte sind „malloc“ und „256K“. kann auch die Speichermanager „mmap_anon“, „mmap_zero“ und „win32“ verwenden.

Im Code ist für diese vier Speicherzuweisungsschemata jede Verarbeitungsfunktion in zend_mm_mem_handlers jeweils implementiert. Eine einfache Beschreibung des Codes lautet wie folgt:

/* 使用mmap内存映射函数分配内存 写入时拷贝的私有映射,并且匿名映射,映射区不与任何文件关联。*/
# define ZEND_MM_MEM_MMAP_ANON_DSC {"mmap_anon", zend_mm_mem_dummy_init, zend_mm_mem_dummy_dtor, zend_mm_mem_dummy_compact, zend_mm_mem_mmap_anon_alloc, zend_mm_mem_mmap_realloc, zend_mm_mem_mmap_free}
/* 使用mmap内存映射函数分配内存 写入时拷贝的私有映射,并且映射到/dev/zero。*/
# define ZEND_MM_MEM_MMAP_ZERO_DSC {"mmap_zero", zend_mm_mem_mmap_zero_init, zend_mm_mem_mmap_zero_dtor, zend_mm_mem_dummy_compact, zend_mm_mem_mmap_zero_alloc, zend_mm_mem_mmap_realloc, zend_mm_mem_mmap_free}
/* 使用HeapAlloc分配内存 windows版本 关于这点,注释中写的是VirtualAlloc() to allocate memory,实际在程序中使用的是HeapAlloc*/
# define ZEND_MM_MEM_WIN32_DSC {"win32", zend_mm_mem_win32_init, zend_mm_mem_win32_dtor, zend_mm_mem_win32_compact, zend_mm_mem_win32_alloc, zend_mm_mem_win32_realloc, zend_mm_mem_win32_free}
/* 使用malloc分配内存 默认为此种分配 如果有加ZEND_WIN32宏,则使用win32的分配方案*/
# define ZEND_MM_MEM_MALLOC_DSC {"malloc", zend_mm_mem_dummy_init, zend_mm_mem_dummy_dtor, zend_mm_mem_dummy_compact, zend_mm_mem_malloc_alloc, zend_mm_mem_malloc_realloc, zend_mm_mem_malloc_free}static const zend_mm_mem_handlers mem_handlers[] = {#ifdef HAVE_MEM_WIN32    
ZEND_MM_MEM_WIN32_DSC,#endif#ifdef HAVE_MEM_MALLOC    ZEND_MM_MEM_MALLOC_DSC,#endif#ifdef HAVE_MEM_MMAP_ANON    
ZEND_MM_MEM_MMAP_ANON_DSC,#endif#ifdef HAVE_MEM_MMAP_ZERO    
ZEND_MM_MEM_MMAP_ZERO_DSC,#endif    
{NULL, NULL, NULL, NULL, NULL, NULL}};

[Über die Vorteile der anonymen Speicherzuordnung]

mmem_zero-Lösung:
(SVR 4) /dev/zero Memory Mapping
1. Sie können Pseudogeräte verwenden. „/dev/zero“ wird als Parameter an mmap übergeben, um einen Zuordnungsbereich zu erstellen. Das Besondere an /dev/zero ist, dass alle Lesevorgänge auf dieser Gerätedatei einen Bytestrom der angegebenen Länge mit dem Wert 0 zurückgeben und jeglicher geschriebener Inhalt verworfen wird. Unser Interesse besteht darin, damit einen Mapping-Bereich zu erstellen. Der Inhalt des mit /dev/zero erstellten Mapping-Bereichs wird auf 0 initialisiert.
2. Der Vorteil der Verwendung von /dev/zero besteht darin, dass beim Erstellen des Mapping-Bereichs keine Datei erforderlich ist, die jederzeit vorhanden ist. Die Pseudodatei /dev/zero ist ausreichend. Der Nachteil besteht darin, dass es nur zwischen verwandten Prozessen verwendet werden kann. Im Vergleich zur Kommunikation zwischen verwandten Prozessen ist die Verwendung der Inter-Thread-Kommunikation effizienter. Unabhängig von der verwendeten Technologie muss der Zugriff auf gemeinsam genutzte Daten synchronisiert werden.

mmem_anon-Lösung:

(4.4 BSD) Anonyme Speicherzuordnung
1. Für die anonyme Speicherzuordnung und die Verwendung des Typs /dev/zero sind keine echten Dateien erforderlich. Um die anonyme Zuordnung zu verwenden, müssen Sie das Flag MAP_ANON an mmap übergeben und den Parameter fd auf -1 setzen.
2. Das sogenannte Anonym bedeutet, dass der Zuordnungsbereich nicht über fd mit dem Dateipfadnamen verknüpft ist. Die anonyme Speicherzuordnung wird zwischen Prozessen verwendet, die durch Blut miteinander verbunden sind.

[Deklaration der Heap-Speicherzuweisung im Win32-Schema]

Windows-API
Die Funktion HeapAlloc wird wie folgt deklariert:

WINBASEAPI
__out_opt
HANDLE
WINAPI
HeapCreate(
__in DWORD flOptions,
__in SIZE_T dwInitialSize,
__in SIZE_T dwMaximumSize); 
WINBASEAPI
BOOL
WINAPI
HeapDestroy(
__in HANDLE hHeap); 
WINBASEAPI
__bcount(dwBytes)LPVOID
WINAPI
HeapAlloc(
__in HANDLE hHeap,
__in DWORD dwFlags,
__in SIZE_T dwBytes); 
 
WINBASEAPI
BOOL
WINAPI
HeapFree(
__inout HANDLE hHeap,
__inDWORD dwFlags,
__deref LPVOID lpMem); 
WINBASEAPI
SIZE_T
WINAPI
HeapSize(
__in HANDLE hHeap,
__in DWORD dwFlags,
__in LPCVOID lpMem);

hHeap ist der Startort des Prozess-Heap-Speichers.

dwFlags ist das Flag für die Zuweisung von Heap-Speicher.
dwBytes ist die Größe des zugewiesenen Heap-Speichers.

[Initialisierung]

Wenn zend_mm_startup gestartet wird, legt das Programm den Speicherzuweisungsplan und die Segmentzuweisungsgröße entsprechend der Konfiguration fest, wie im folgenden Code gezeigt:

ZEND_API zend_mm_heap *zend_mm_startup(void){
    int i;
    size_t seg_size;
    char *mem_type = getenv("ZEND_MM_MEM_TYPE");
    char *tmp;
    const zend_mm_mem_handlers *handlers;
    zend_mm_heap *heap;     if (mem_type == NULL) {
    i = 0;
    } else {
    for (i = 0; mem_handlers[i].name; i++) {
    if (strcmp(mem_handlers[i].name, mem_type) == 0) {
    break;
    }
    }
    if (!mem_handlers[i].name) {
    fprintf(stderr, "Wrong or unsupported zend_mm storage type '%s'\n", mem_type);
    fprintf(stderr, "  supported types:\n");
    for (i = 0; mem_handlers[i].name; i++) {
    fprintf(stderr, "'%s'\n", mem_handlers[i].name);
    }
    exit(255);
    }
    }
    handlers = &mem_handlers[i]; 
    tmp = getenv("ZEND_MM_SEG_SIZE");
    if (tmp) {
    seg_size = zend_atoi(tmp, 0);
    if (zend_mm_low_bit(seg_size) != zend_mm_high_bit(seg_size)) {
    fprintf(stderr, "ZEND_MM_SEG_SIZE must be a power of two\n");
    exit(255);
    } else if (seg_size < ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE) {
    fprintf(stderr, "ZEND_MM_SEG_SIZE is too small\n");
    exit(255);
    }
    } else {
    seg_size = ZEND_MM_SEG_SIZE;
    }     //....代码省略}

Zeilen 1121 ~1138 Durchlaufen Sie das gesamte mem_handlers-Array. Wenn die Variable ZEND_MM_MEM_TYPE nicht festgelegt ist, wird standardmäßig das Malloc-Schema (z. B. ZEND_WIN32) verwendet Wird die Variable gesetzt, wird das Set-Schema verwendet.

Die Zeilen 1140 bis 1152 bestätigen die Segmentzuordnungsgröße. Wenn die Variable ZEND_MM_SEG_SIZE festgelegt ist, wird die festgelegte Größe verwendet. Hier wird beurteilt, ob die festgelegte Größe einem Vielfachen von 2 entspricht und größer als oder ist gleich ZEND_MM_ALIGNED_SEGMENT_SIZE + ZEND_MM_ALIGNED_HEADER_SIZE; Wenn keine Einstellung vorhanden ist, wird die Standardgröße ZEND_MM_SEG_SIZE nicht verwendet

[Anhang]

Funktionsbeschreibung:
mmap ordnet eine Datei oder ein anderes Objekt dem Speicher zu. Die Datei ist mehreren Seiten zugeordnet. Wenn die Dateigröße nicht der Summe der Größen aller Seiten entspricht, wird der ungenutzte Speicherplatz der letzten Seite gelöscht. munmap führt den umgekehrten Vorgang aus und löscht die Objektzuordnung für einen bestimmten Adressbereich.
Dateibasierte Zuordnung: Die st_atime der zugeordneten Datei kann jederzeit während der Ausführung von mmap und munmap aktualisiert werden. Wenn das Feld st_atime in der oben genannten Situation nicht aktualisiert wird, wird der Wert dieses Felds aktualisiert, wenn die erste Seite des zugeordneten Bereichs zum ersten Mal indiziert wird. Für eine Dateizuordnung, die mit den Flags PROT_WRITE und MAP_SHARED erstellt wurde, werden ihre st_ctime und st_mtime
nach dem Schreiben in den Zuordnungsbereich aktualisiert, aber bevor msync() mit den Flags MS_SYNC und MS_ASYNC aufgerufen wird.

Verwendung:


#include 
void *mmap(void *start, size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(void *start, size_t length);

参数:
start:映射区的开始地址。
length:映射区的长度。
prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起
PROT_EXEC //页内容可以被执行
PROT_READ //页内容可以被读取
PROT_WRITE //页可以被写入
PROT_NONE //页不可访问
flags:指定映射对象的类型,映射选项和映射页是否可以共享。它的值可以是一个或者多个以下位的组合体
MAP_FIXED //使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。
MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。
MAP_PRIVATE //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。
MAP_DENYWRITE //这个标志被忽略。
MAP_EXECUTABLE //同上
MAP_NORESERVE //不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。
MAP_LOCKED //锁定映射区的页面,从而防止页面被交换出内存。
MAP_GROWSDOWN //用于堆栈,告诉内核VM系统,映射区可以向下扩展。
MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联。
MAP_ANON //MAP_ANONYMOUS的别称,不再被使用。
MAP_FILE //兼容标志,被忽略。
MAP_32BIT //将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到支持。
MAP_POPULATE //为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。
MAP_NONBLOCK //仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。
fd:有效的文件描述词。如果MAP_ANONYMOUS被设定,为了兼容问题,其值应为-1。
offset:被映射对象内容的起点。

返回说明:
成功执行时,mmap()返回被映射区的指针,munmap()返回0。失败时,mmap()返回MAP_FAILED[其值为(void *)-1],munmap返回-1。errno被设为以下的某个值
EACCES:访问出错
EAGAIN:文件已被锁定,或者太多的内存已被锁定
EBADF:fd不是有效的文件描述词
EINVAL:一个或者多个参数无效
ENFILE:已达到系统对打开文件的限制
ENODEV:指定文件所在的文件系统不支持内存映射
ENOMEM:内存不足,或者进程已超出最大内存映射数量
EPERM:权能不足,操作不允许
ETXTBSY:已写的方式打开文件,同时指定MAP_DENYWRITE标志
SIGSEGV:试着向只读区写入
SIGBUS:试着访问不属于进程的内存区

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

浅谈PHP源码二十九:关于接口的继承

浅谈PHP源码二十八:关于类结构和继承

浅谈PHP源码二十七:PHP对构造方法的识别

Das obige ist der detaillierte Inhalt vonEine kurze Diskussion des PHP-Quellcodes 30: Speicherschicht im PHP-Speicherpool. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn