>  기사  >  백엔드 개발  >  PHP 소스 코드 30에 대한 간략한 토론: PHP 메모리 풀의 스토리지 계층

PHP 소스 코드 30에 대한 간략한 토론: PHP 메모리 풀의 스토리지 계층

不言
不言원래의
2018-06-29 09:41:411463검색

이 기사에서는 주로 PHP 소스 코드 30에 대한 간략한 설명을 소개합니다. PHP 메모리 풀의 저장 계층에는 특정 참조 값이 있습니다. 이제 필요한 친구들이 참조할 수 있습니다.

PHP에 대한 간략한 설명입니다. 소스 코드 30: PHP 메모리 풀의 저장 계층

[개요]
PHP의 메모리 관리자는 계층적입니다. 이 관리자에는 저장 계층, 힙 계층 및 emalloc/efree 계층의 세 가지 계층이 있습니다. 저장 계층은 실제로 malloc(), mmap() 등의 함수를 통해 시스템에서 메모리를 적용하고, free() 함수를 통해 요청된 메모리를 해제한다. 스토리지 레이어는 일반적으로 더 큰 메모리 블록에 적용됩니다. 여기에 적용되는 대용량 메모리는 스토리지 레이어 구조에 필요한 메모리를 의미하지 않습니다. 단지 힙 레이어가 스토리지 레이어의 할당 방법을 호출할 때 적용되는 메모리입니다. 세그먼트 형식이 상대적으로 크기 때문에 스토리지 계층의 역할은 메모리 할당 방법을 힙 계층에 투명하게 만드는 것입니다.

먼저 저장 계층의 구조를 살펴보세요:
[구조]

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

메모리 할당 방법, 호출되는 함수는 _zend_mm_storage 구조에 설정된 처리 함수이며, 메모리는 세그먼트 형태로 표현됩니다.
【4가지 메모리 체계】
PHP에는 저장소 계층에 malloc, win32, mmap_anon, mmap_zero의 4가지 메모리 할당 체계가 있습니다. 기본적으로 ZEND_WIN32 매크로가 설정된 경우 Windows 버전과 HeapAlloc이 사용됩니다. 나머지 두 가지 메모리 체계는 익명 메모리 매핑이며, 변수 설정을 통해 PHP의 메모리 체계를 수정할 수 있습니다.
공식 설명은 다음과 같습니다.
Zend_MM_MEM_TYPE 및 ZEND_MM_SEG_SIZE 환경
변수를 사용하여 Zend MM을 조정할 수 있습니다. 기본값은 "malloc" 및 "256K"입니다. 대상 시스템에 따라
"mmap_anon"을 사용할 수도 있습니다. mmap_zero” 및 “win32 ″ 저장소 관리자입니다.

코드에서 이러한 네 가지 메모리 할당 방식에 대해 zend_mm_mem_handlers의 각 처리 함수가 각각 구현됩니다. 코드에 대한 간단한 설명은 다음과 같습니다.

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

[익명 메모리 매핑의 장점에 대해]
mmem_zero 솔루션:
(SVR 4) /dev/zero 메모리 매핑
1 의사 장치 "/dev/를 전달할 수 있습니다. "0"을 mmap의 매개변수로 매핑 영역을 생성합니다. /dev/zero의 특별한 점은 이 장치 파일에 대한 모든 읽기 작업이 지정된 길이의 바이트 스트림을 값 0으로 반환하고 작성된 내용을 모두 삭제한다는 것입니다. 우리의 관심은 이를 사용하여 매핑 영역을 만드는 데 있습니다. /dev/zero로 생성된 매핑 영역의 내용은 0으로 초기화됩니다.
2. /dev/zero를 사용하면 mmap이 매핑 영역을 생성할 때 항상 존재하는 파일이 필요하지 않다는 장점이 있습니다. /dev/zero이면 충분합니다. 단점은 관련 프로세스 사이에서만 사용할 수 있다는 점이다. 관련 프로세스 간의 통신에 비해 스레드 간 통신을 사용하는 것이 더 효율적입니다. 사용되는 기술에 관계없이 공유 데이터에 대한 액세스는 동기화되어야 합니다.

mmem_anon 솔루션:
(4.4 BSD) 익명 메모리 매핑
1. 익명 메모리 매핑 및 /dev/zero 유형 사용에는 실제 파일이 필요하지 않습니다. 익명 매핑을 사용하려면 MAP_ANON 플래그를 mmap에 전달하고 fd 매개변수를 -1로 설정해야 합니다.
2. 소위 익명이란 매핑 영역이 fd를 통해 파일 경로 이름과 연결되지 않음을 의미합니다. 익명의 메모리 매핑은 혈액과 관련된 프로세스 간에 사용됩니다.

[win32 방식의 힙 메모리 할당 선언]
windows API
HeapAlloc 함수는 다음과 같이 선언됩니다.

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은 프로세스 힙 메모리의 시작 위치입니다.
dwFlags는 힙 메모리 할당을 위한 플래그입니다.
dwBytes는 할당된 힙 메모리의 크기입니다.

【초기화】
zend_mm_startup이 시작되면 프로그램은 다음 코드에 표시된 대로 구성에 따라 메모리 할당 계획과 세그먼트 할당 크기를 설정합니다.

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 전체 mem_handlers 배열을 탐색하여 메모리를 확인합니다. 할당 계획이 설정되지 않은 경우 ZEND_MM_MEM_TYPE 변수는 기본적으로 malloc 체계를 사용합니다. Windows(예: ZEND_WIN32)인 경우 기본적으로 win32 체계가 사용됩니다. ZEND_MM_MEM_TYPE 변수가 설정된 경우 설정된 체계가 사용됩니다.

1140~1152번째 줄은 세그먼트 할당 크기를 확인합니다. ZEND_MM_SEG_SIZE 변수가 설정된 경우 설정된 크기가 사용되며 여기서는 설정된 크기가 2의 배수를 만족하고 ZEND_MM_ALIGNED_SEGMENT_SIZE + 이상인지 판단합니다. ZEND_MM_ALIGNED_HEADER_SIZE; 설정되지 않으면 기본 ZEND_MM_SEG_SIZE

를 사용하지 않습니다.[부록]
기능 설명:
mmap은 파일이나 다른 개체를 메모리에 매핑합니다. 파일은 여러 페이지에 매핑되며, 파일 크기가 모든 페이지 크기의 합이 아닌 경우 마지막 페이지의 사용되지 않은 공간이 지워집니다. munmap은 반대 작업을 수행하여 특정 주소 범위에 대한 개체 매핑을 삭제합니다.
파일 기반 매핑, mmap 및 munmap 실행 중 언제든지 매핑된 파일의 st_atime이 업데이트될 수 있습니다. 위의 상황에서 st_atime 필드가 업데이트되지 않으면 매핑된 영역의 첫 번째 페이지가 처음으로 인덱싱될 때 이 필드의 값이 업데이트됩니다. PROT_WRITE 및 MAP_SHARED 플래그를 사용하여 설정된 파일 매핑의 경우 st_ctime 및 st_mtime은 매핑 영역에 쓴 후 업데이트되지만 MS_SYNC 및 MS_ASYNC 플래그를 사용하여 msync()가 호출되기 전에 업데이트됩니다.

사용법:


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

위 내용은 PHP 소스 코드 30에 대한 간략한 토론: PHP 메모리 풀의 스토리지 계층의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.