Home  >  Article  >  Backend Development  >  A brief discussion of PHP source code 30: Storage layer in PHP memory pool

A brief discussion of PHP source code 30: Storage layer in PHP memory pool

不言
不言Original
2018-06-29 09:41:411463browse

This article mainly introduces about the PHP source code thirty: the storage layer in the PHP memory pool, which has a certain reference value. Now I share it with you. Friends in need can refer to it

Talking about PHP source code 30: Storage layer in PHP memory pool

[Overview]
PHP’s memory manager is hierarchical. This manager has three layers: storage layer, heap layer and emalloc/efree layer. The storage layer actually applies for memory from the system through functions such as malloc() and mmap(), and releases the requested memory through the free() function. The storage layer usually applies for relatively large memory blocks. The large memory applied here does not mean the memory required by the storage layer structure. It is just that when the heap layer calls the allocation method of the storage layer, the memory it applies for in the segment format is relatively large. , the role of the storage layer is to make the memory allocation method transparent to the heap layer.

First look at the structure of the storage layer:
[Structure]

  /* 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;};

The memory allocation method, the function called is the processing function set in the _zend_mm_storage structure, and the memory is segmented Expressed in form.
[4 memory schemes]
PHP has 4 memory allocation schemes in the storage layer: malloc, win32, mmap_anon, mmap_zero. By default, malloc is used to allocate memory. If the ZEND_WIN32 macro is set, it is the windows version and calls HeapAlloc to allocate it. Memory, the remaining two memory schemes are anonymous memory mapping, and PHP's memory scheme can be modified by setting variables.
The official description is as follows:
The Zend MM can be tweaked using ZEND_MM_MEM_TYPE and ZEND_MM_SEG_SIZE environment
variables. Default values ​​are “malloc” and “256K”. Dependent on target system you
can also use “mmap_anon ”, “mmap_zero” and “win32″ storage managers.

In the code, for these four memory allocation schemes, each processing function in zend_mm_mem_handlers is implemented respectively. A brief description of the code is as follows:

/* 使用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}};

[About the advantages of anonymous memory mapping]
mmem_zero solution:
(SVR 4) /dev/zero Memory Mapping
1. You can use pseudo devices "/dev/zero" is passed as a parameter to mmap to create a mapping area. The special thing about /dev/zero is that all read operations on this device file return a byte stream of the specified length with a value of 0, and any written content is discarded. Our interest lies in using it to create a mapping area. The contents of the mapping area created with /dev/zero are initialized to 0.
2. The advantage of using /dev/zero is that when mmap creates a mapping area, it does not need a file that exists at all times. The pseudo file /dev/zero is enough. The disadvantage is that it can only be used between related processes. Compared with communication between related processes, using inter-thread communication is more efficient. Regardless of the technology used, access to shared data needs to be synchronized.

mmem_anon solution:
(4.4 BSD) Anonymous Memory Mapping
1. Anonymous memory mapping and the use of /dev/zero type do not require real files. To use anonymous mapping, you need to pass the MAP_ANON flag to mmap and set the fd parameter to -1.
2. The so-called anonymous means that the mapping area is not associated with the file path name through fd. Anonymous memory mapping is used between processes that are related by blood.

[Declaration of heap memory allocation in win32 scheme]
windows API
The function HeapAlloc is declared as follows:

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 is the starting position of the process heap memory.
dwFlags is the flag for allocating heap memory.
dwBytes is the size of the allocated heap memory.

[Initialization]
When zend_mm_startup is started, the program will set the memory allocation plan and segment allocation size according to the configuration, as shown in the following code:

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;
    }     //....代码省略}

Lines 1121~1138 traverse the entire mem_handlers Array, confirm the memory allocation scheme. If the ZEND_MM_MEM_TYPE variable is not set, the malloc scheme is used by default. If it is windows (i.e. ZEND_WIN32), the win32 scheme is used by default. If the ZEND_MM_MEM_TYPE variable is set, the set scheme is used.

Lines 1140~1152 confirm the segment allocation size. If the ZEND_MM_SEG_SIZE variable is set, the set size will be used. Here, it will be judged whether the set size satisfies a multiple of 2 and is greater than or equal to ZEND_MM_ALIGNED_SEGMENT_SIZE ZEND_MM_ALIGNED_HEADER_SIZE; if If there is no setting, the default ZEND_MM_SEG_SIZE is not used

[Appendix]
Function description:
mmap maps a file or other object into memory. The file is mapped to multiple pages. If the file size is not the sum of the sizes of all pages, the unused space of the last page will be cleared. munmap performs the opposite operation, deleting the object mapping for a specific address range.
Based on file mapping, at any time during the execution of mmap and munmap, the st_atime of the mapped file may be updated. If the st_atime field is not updated in the above situation, the value of this field will be updated when the first page of the mapped area is indexed for the first time. For a file mapping established with the PROT_WRITE and MAP_SHARED flags, its st_ctime and st_mtime
will be updated after writing to the mapping area, but before msync() is called with the MS_SYNC and MS_ASYNC flags.

usage:

#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对构造方法的识别

The above is the detailed content of A brief discussion of PHP source code 30: Storage layer in PHP memory pool. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn