> 백엔드 개발 > PHP 튜토리얼 > 기계 설계, 제조 및 자동화 전공 소개 PHP 커널 소개 및 확장 개발 가이드 - 기본 지식

기계 설계, 제조 및 자동화 전공 소개 PHP 커널 소개 및 확장 개발 가이드 - 기본 지식

WBOY
풀어 주다: 2016-07-29 08:46:41
원래의
969명이 탐색했습니다.

1. 기본 지식
이 장에서는 Zend 엔진의 내부 메커니즘 중 일부를 간략하게 소개합니다. 이 지식은 Extensions와 밀접하게 관련되어 있으며 보다 효율적인 PHP 코드를 작성하는 데도 도움이 됩니다.
 1.1 PHP 변수 저장
 1.1.1 zval 구조
Zend는 zval 구조를 사용하여 PHP 변수의 값을 저장합니다. 구조는 다음과 같습니다.

코드 복사 코드는 다음과 같습니다.


typedef Union _zvalue_value {
long lval; /* long value */
double dval; /
struct {
char *val;
int len;
} str;
HashTable *ht /* 해시 테이블 값 */
zend_object_value obj; ;
struct _zval_struct {
/* 변수 정보 */
zvalue_value value; /* value */
zend_uint refcount; /* 활성 유형 */
zend_uchar is_ref ;
} ;
typedef struct _zval_struct zval;
Zend는 유형 값에 따라 액세스할 값의 멤버를 결정합니다. 🎜>

IS_NULLN/A
IS_LONG은 value.lval에 해당

IS_DOUBLE은 value.dval에 해당
IS_STRING은 value.str에 해당
IS_ARRAY는 value.ht에 해당
IS_OBJECT는 value.obj에 해당합니다.
IS_BOOL은 value.lval에 해당합니다.
IS_RESOURCE는 value.lval에 해당합니다.
이 테이블을 기반으로 두 가지 흥미로운 점을 찾을 수 있습니다. 첫째, PHP 배열은 실제로 HashTable입니다. 이는 PHP가 연관 배열을 지원할 수 있는 이유를 설명합니다. 둘째, Resource가 저장하는 것은 일반적으로 포인터, 내부 배열의 인덱스 또는 생성자만 아는 다른 것입니다.
 1.1.1 참조 카운팅
참조 카운팅은 가비지 수집 및 메모리에 사용됩니다. 풀과 문자열이 널리 사용되며 Zend는 일반적인 참조 카운팅을 구현합니다. 여러 PHP 변수는 참조 계산 메커니즘을 통해 동일한 zval을 공유할 수 있습니다. zval의 나머지 두 멤버인 is_ref 및 refcount는 이 공유를 지원하는 데 사용됩니다.
분명히 refcount는 참조가 증가하거나 감소할 때 이 값도 그에 따라 증가하고 감소합니다. 일단 0으로 감소하면 Zend는 zval을 재활용합니다.
is_ref는 어떻습니까?
1.1.2 zval status
PHP에는 참조와 비참조라는 두 가지 유형의 변수가 있으며 모두 참조 카운팅을 사용하여 Zend에 저장됩니다. 비참조 변수의 경우 변수는 서로 독립적이어야 합니다. 하나의 변수를 수정하면 다른 변수에 영향을 미칠 수 없습니다. 이 충돌은 변수를 쓰려고 할 때 Copy-On-Write 메커니즘을 사용하여 해결할 수 있습니다. , Zend는 이 변수가 가리키는 zval이 여러 변수에 의해 공유되는 경우 refcount가 1인 zval이 여기에 복사되고 원래 zval의 refcount가 감소되는 과정을 "zval 분리"라고 합니다. . 그러나 참조 변수의 경우에는 비참조 유형의 요구 사항과 반대입니다. 참조로 할당된 변수는 하나의 변수를 수정하면 묶인 모든 변수가 수정됩니다.
이 두 가지 상황을 각각 처리하려면 현재 zval의 상태를 지적해야 함을 알 수 있습니다. is_ref는 현재 zval을 가리키는 모든 변수가 참조로 할당되었는지 여부를 지적하기 위한 것입니다. - 모든 참조 또는 전혀 참조 없음. 이때 다른 변수는 zval의 is_ref가 0, 즉 참조가 아닌 경우에만 Copy-On-Write를 실행합니다.
 1.1.3 zval 상태 전환
zval에 수행된 모든 할당 작업이 참조 또는 비참조인 경우 is_ref 하나로 충분합니다. 그러나 세상이 항상 그렇게 아름다운 것은 아닙니다. PHP는 참조 할당과 비참조 할당을 혼합할 때 특별한 처리를 수행해야 합니다.
상황 I, 다음 PHP 코드를 살펴보세요.

전체 프로세스는 다음과 같습니다.
이 코드의 처음 세 문장은 is_ref=1인 zval을 가리키는 a, b 및 c입니다. refcount=3 ; 네 번째 문장은 참조 카운트만 늘리면 됩니다. 그러나 대상 zval은 참조 카운트를 늘리는 것만으로도 명백히 잘못된 것입니다. d에 대한 별도의 zval 복사본입니다.
전체적인 과정은 다음과 같습니다.



1.1.1 매개변수 전달  PHP内核介绍及扩展开发指南—基础知识 PHP 함수 매개변수 전달은 변수 할당과 동일합니다. 참조 할당이 아닌 참조 할당과 동일하며 참조 전송은 참조 할당과 동일하며 zval 상태 전환이 수행될 수도 있습니다. 이에 대해서는 나중에 언급하겠습니다.
 1.2 HashTable 구조
 HashTable은 Zend 엔진에서 가장 중요하고 널리 사용되는 데이터 구조로 거의 모든 것을 저장하는 데 사용됩니다.
 1.1.1 데이터 구조
 HashTable 데이터 구조는 다음과 같이 정의됩니다.



코드 복사

코드는 다음과 같습니다.


typedef struct bucket {
ulong h; // 해시 저장
uint nKeyLength;
void *pData; // 사용자 데이터의 복사본인 값
void *pDataPtr;
struct bucket *pListNext; // pListNext 및 pListLast 형식
struct bucket *pListLast; // 전체 HashTable
struct bucket의 이중 연결 목록 *pNext; 해당 해시 형성
struct bucket *pLast; // 이중 연결 목록
char arKey[1] // 키
} Bucket
typedef struct _hashtable {
uint nTableSize; >uint nTableMask;
uint nNumOfElements;
Bucket *pInternalPointer /* 요소 탐색에 사용됨 */
Bucket *pListTail; *arBuckets; // 해시 배열
dtor_func_t pDestructor; // HashTable 초기화 시 지정, Bucket 소멸 시 호출
zend_bool persist; // C 메모리 할당 루틴 사용 여부
zend_bool; bApplyProtection;
#if ZEND_DEBUG
int 불일치
#endif
} HashTable


일반적으로 Zend의 HashTable은 아래와 같이 선형 순회에 최적화된 연결 목록 해시입니다.

 PHP内核介绍及扩展开发指南—基础知识
HashTable에는 연결 목록 해시와 이중 연결 목록의 두 가지 데이터 구조가 포함되어 있습니다. 전자는 빠른 키-값 쿼리에 사용되고 후자는 선형 순회에 편리합니다. 버킷은 두 데이터 구조 모두에 존재합니다.
이 데이터 구조에 대한 몇 가지 설명:
l 연결 목록 해싱에 이중 연결 목록이 사용되는 이유는 무엇입니까?
일반적으로 연결 목록 해싱은 키로만 작동하면 되며 단일 연결 목록이면 충분합니다. 그러나 Zend는 연결 목록 해시에서 특정 버킷을 삭제해야 하는 경우가 있는데, 이는 이중 연결 목록을 사용하여 매우 효율적으로 달성할 수 있습니다.
l nTableMask의 기능은 무엇인가요?
이 값은 해시 값을 arBuckets 배열 인덱스로 변환하는 데 사용됩니다. HashTable을 초기화할 때 Zend는 먼저 arBuckets 배열에 대해 nTableSize 크기의 메모리를 할당합니다. nTableSize는 사용자가 지정한 크기 중 가장 작은 2^n(이진수로 10*) 이상입니다. nTableMask = nTableSize – 1, 이는 바이너리 01*입니다. 이때 h & nTableMask는 우연히 [0, nTableSize – 1]에 속하며 Zend는 이를 인덱스로 사용하여 arBuckets 배열에 액세스합니다.
l pDataPtr은 무엇을 합니까?
일반적으로 사용자가 키-값 쌍을 삽입하면 Zend는 값을 복사하고 pData를 값 복사 위치로 지정합니다. 복사 작업을 수행하려면 Zend의 내부 루틴인 emalloc을 호출하여 메모리를 할당해야 합니다. 이는 시간이 많이 소요되는 작업이며 값보다 큰 메모리를 소비하게 됩니다(추가 메모리는 쿠키를 저장하는 데 사용됩니다). 큰 낭비. HashTable은 포인터 값을 저장하는 데 주로 사용된다는 점을 고려하여 Zend는 값이 포인터만큼 작을 때 이를 pDataPtr에 직접 복사하고 pData를 pDataPtr로 지정합니다. 이렇게 하면 emalloc 작업이 방지되고 캐시 적중률도 향상됩니다.
arKey의 크기는 왜 1인가요?
arKey는 키를 저장하는 배열이지만 크기가 1에 불과해 키를 보관할 수 없습니다. HashTable의 초기화 함수에서 다음 코드를 찾을 수 있습니다.
 1p = (Bucket *) pemalloc(sizeof(Bucket) - 1 nKeyLength, ht->percious)
 Zend가 충분히 할당하는 것을 볼 수 있습니다. Bucket을 위한 공간 자신의 기억과 키를 내려놓으세요,
 l 위쪽 절반이 Bucket이고, 아래쪽 절반이 Key이고, arKey는 Bucket의 마지막 요소가 "우연"하므로 사용할 수 있습니다. arKey를 사용하여 키에 액세스합니다. 이 기술은 메모리 관리 루틴에서 가장 일반적으로 사용됩니다. 메모리가 할당되면 지정된 크기보다 큰 메모리가 실제로 할당됩니다. 추가 상위 절반은 일반적으로 블록 크기, 이전 블록과 같은 이 메모리에 대한 정보를 저장합니다. 포인터, 다음 블록 포인터 등 Baidu의 Transmit 프로그램은 이 방법을 사용합니다.
키 관리에 포인터를 사용하지 않는 목적은 하나의 emalloc 작업을 줄이고 캐시 적중률을 높이기 위한 것입니다. 또 필요한 이유는 대부분의 경우 키가 고정되어 있고, 키가 길어지기 때문에 전체 Bucket을 재할당하지 않는다는 점입니다. 이는 값이 변경 가능하기 때문에 값이 배열로 할당되지 않는 이유도 설명합니다.
1.2.2 PHP Array
HashTable에 대해 아직도 풀리지 않는 질문이 있습니다. 즉, nNextFreeElement는 무엇을 하는 걸까요?
Zend의 HashTable은 일반 해싱과 달리 사용자가 직접 해시 값을 지정하고 키를 무시할 수 있습니다. 키를 지정할 필요도 없습니다(현재 nKeyLength는 0입니다). 동시에 HashTable은 추가 작업도 지원합니다. 사용자는 해시 값을 지정할 필요도 없고 값만 제공하면 됩니다. 이때 Zend는 nNextFreeElement를 해시로 사용하고 nNextFreeElement를 증가시킵니다.
HashTable의 이러한 동작은 이상해 보입니다. 키로 값에 액세스할 수 없고 전혀 해시가 아니기 때문입니다. 문제를 이해하는 열쇠는 PHP 배열이 HashTable을 사용하여 구현된다는 것입니다. 연관 배열은 일반적인 k-v 매핑을 사용하여 HashTable에 요소를 추가하고 해당 키는 사용자가 지정한 문자열입니다. 비연관 배열은 배열 첨자를 해시 값으로 직접 사용합니다. , 없이 키가 있으며 배열에 연관 요소와 비연관 요소를 혼합하거나 array_push 작업을 사용할 때 nNextFreeElement를 사용해야 합니다.
값을 다시 살펴보겠습니다. PHP 배열의 값은 zval*이라는 일반 구조를 직접 사용합니다. 이전 섹션의 소개에 따르면 이 zval*은 pDataPtr에 직접 저장됩니다. zval을 직접 사용하므로 배열 요소는 모든 PHP 유형이 될 수 있습니다.
배열 순회 연산, 즉 foreach, Each 등은 HashTable의 이중 연결 리스트를 통해 수행되며 pInternalPointer는 현재 위치를 기록하는 커서로 사용됩니다.
 1.2.3 변수 기호 테이블
 HashTable은 배열 외에도 PHP 함수, ​​변수 기호, 로드된 모듈, 클래스 멤버 등과 같은 많은 다른 데이터를 저장하는 데 사용됩니다.
변수 기호 테이블은 연관 배열과 동일하며 키는 변수 이름(긴 변수 이름을 사용하는 것은 좋지 않음을 알 수 있음)이고 값은 zval*입니다.
언제든지 PHP 코드는 두 개의 변수 기호 테이블(symbol_table 및 active_symbol_table)을 볼 수 있습니다. 전자는 전역 기호 테이블이라고 하는 전역 변수를 저장하는 데 사용되며 후자는 현재 활성 변수 기호 테이블을 가리키는 포인터입니다. 일반적으로 이 경우 전역 기호 테이블입니다. 그러나 PHP 함수(여기서는 사용자가 PHP 코드를 사용하여 생성한 함수를 나타냄)를 입력할 때마다 Zend는 함수에 로컬인 변수 기호 테이블을 생성하고 active_symbol_table을 로컬 기호 테이블로 가리킵니다. Zend는 항상 active_symbol_table을 사용하여 변수에 액세스함으로써 지역 변수의 범위 제어를 달성합니다.
그러나 전역으로 표시된 변수가 함수에서 로컬로 액세스되는 경우 Zend는 특수 처리를 수행합니다. active_symbol_table에 같은 이름의 변수에 대한 참조를 생성합니다. 먼저 생성될 것입니다.
 1.3 메모리 및 파일
프로그램이 소유하는 리소스에는 일반적으로 메모리와 파일이 포함됩니다. 일반 프로그램의 경우 이러한 리소스는 프로세스가 끝나면 운영 체제나 C 라이브러리가 자동으로 해당 리소스를 재활용합니다. 우리는 리소스를 명시적으로 공개하지 않았습니다.
하지만 PHP 프로그램에는 페이지 기반이라는 특성이 있습니다. 페이지가 실행되면 메모리나 파일과 같은 리소스도 적용됩니다. C 라이브러리는 자원 재활용의 필요성을 알지 못할 수도 있습니다. 예를 들어, php를 모듈로 apache로 컴파일하고 prefork 또는 작업자 모드에서 apache를 실행합니다. 이 경우 Apache 프로세스나 스레드가 재사용되며, PHP 페이지에서 할당한 메모리는 코어가 해제될 때까지 메모리에 유지됩니다.
이 문제를 해결하기 위해 Zend는 메모리 할당 API 세트를 제공합니다. 해당 기능은 C의 해당 기능과 동일합니다. 차이점은 이러한 함수는 Zend 자체 메모리 풀에서 메모리를 할당하고 페이지를 구현할 수 있다는 것입니다. 기반 자동 재활용. 우리 모듈에서 페이지에 할당된 메모리는 C 루틴 대신 이러한 API를 사용해야 합니다. 그렇지 않으면 Zend는 페이지 끝에서 메모리를 해제하려고 시도하며 그 결과는 일반적으로 크러쉬입니다.
emalloc()
efree()
estrdup()
estrndup()
ecalloc()
erealloc()
또한 Zend는 다음과 같은 셰이프 그룹도 제공합니다. as VCWD_xxx 매크로는 C 라이브러리와 운영 체제의 해당 파일 API를 대체하는 데 사용되며 PHP의 가상 작업 디렉터리를 지원할 수 있으며 항상 모듈 코드에서 사용해야 합니다. 매크로의 구체적인 정의는 PHP 소스코드 "TSRM/tsrm_virtual_cwd.h"를 참고하세요. 모든 매크로에서 닫기 작업이 제공되지 않는다는 것을 알 수 있습니다. 이는 닫기 개체가 열린 리소스이고 파일 경로를 포함하지 않기 때문에 C 또는 운영 체제 루틴을 유사하게 사용할 수 있기 때문입니다. 쓰기와 같은 작업은 C 또는 운영 체제 루틴을 직접 사용합니다.

위 내용은 기계설계, 제조, 자동화 전공 소개, PHP 커널 소개 및 확장 개발 가이드 - 기계설계, 제조, 자동화 전공 소개를 포함한 기초지식을 소개하고 있는데, PHP에 관심이 있는 친구들에게 도움이 되었으면 좋겠습니다. 튜토리얼.

원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿