PHP 원리의 변수 분리/참조(Variables Separation) 분석

우선 zval의 구조를 되돌아봅시다.

struct _zval_struct {
        /* Variable information */
        zvalue_value value; /* value */
        zend_uint refcount;
        zend_uchar type; /* active type */
        zend_uchar is_ref;
};
그중의 refcount와 is_ref 필드는 우리가 줄곧 소개한 적이 없습니다. 우리는 PHP가 오랫동안 실행된 서버 측의 스크립트 해석기라는 것을 알고 있습니다.그렇다면 효율과 자원 사용률은 매우 중요한 평가 기준이다. 즉, PHP는 가능한 한 메모리 사용률을 소개해야 한다. 아래의 코드를 고려해야 한다.

<?php
   $var = "laruence";
   $var_dup = $var;
   unset($var);
?>
첫 번째 줄 코드는 문자열 변수를 만들고 9자 크기의 메모리를 신청했으며 문자열'laruence'와 NULL(/0)의 끝을 저장했다.두 번째 줄은 새로운 문자열 변수를 정의하고 변수 var의 값을 복사합니다.세 번째 줄 unset 변수 var 같은 코드는 우리가 평소에 스크립트에서 흔히 볼 수 있는 것이다. 만약에 PHP가 모든 변수에 대해 메모리를 재분배하고copy 데이터를 분배한다면 위의 코드는 모두 18바이트의 메모리 공간을 신청해야 한다. 그리고 우리도 쉽게 알 수 있듯이 위의 코드는 사실 두 개의 공간을 신청할 필요가 없다. 허허, PHP 개발자도 알아차렸다. 우리가 전에 말했듯이,PHP의 변수는symbol_에 저장됩니다.테이블의 기호 이름은 zval에 대응하여 이루어집니다. 예를 들어 위의 첫 줄 코드는symbol_테이블에 하나의 값'var'을 저장합니다. 대응하는 바늘은 하나의 zval 구조를 가리키고 변수값'laruence'는 이 zval에 저장되어 있기 때문에 상상하기 어렵지 않습니다. 위의 코드에 대해 말하자면 우리는'var'와'var_dup'에 대응하는 바늘은 모두 같은 zval을 가리키면 됩니다.PHP도 이렇게 했습니다. 이럴 때 우리가 이전에 소개하지 않았던 zval 구조의refcount 필드를 소개해야 합니다.refcount는 말 그대로 현재의 zval이 인용된 계수를 기록합니다.예를 들어 코드:

<?php
   $var = 1;
   $var_dup = $var;
?>
첫 번째 줄에 성형 변수를 만들었는데 변수 값은 1이다.이때 성형 1을 저장하는 이 zval의refcount는 1입니다.두 번째 줄은 새로운 성형 변수를 만들었고 변수도 방금 만든 zval을 가리키며 이 zval의refcount에 1을 추가했습니다. 이때 이 zval의refcount는 2입니다.PHP는 우리가 이 과정을 이해하는 데 도움을 줄 수 있는 함수를 제공했다 debug_zval_dump:

<?php
 $var = 1;
 debug_zval_dump($var);
 $var_dup = $var;
 debug_zval_dump($var);
?>


long(1) refcount(2)
long(1) refcount(3

이상하면 var의refcount는 1이겠지?우리는 간단한 변수에 대해 PHP가 매개 변수를 전달하는 형식으로 뚫는다는 것을 안다.즉, debug_를 실행할 때zval_dump ($var) 시 $var는 debug_zval_dump, 즉 var의refcount 더하기 1을 초래할 수 있기 때문에 우리는 변수가 변수에 값을 부여한 후에 zval의refcount 더하기 1을 초래할 수 있다는 사실을 볼 수 있다.이제 우리는 문장의 시작 코드를 뒤돌아보았습니다. 마지막 줄unset ($var) 를 실행한 후에 무슨 일이 일어날까요?네, refcount 마이너스 1, 위 코드:

<?php
   $var = "laruence";
   $var_dup = $var;
   unset($var);
   debug_zval_dump($var_dup);
?>


string(8) "laruence" refcount(2

그런데 아래 코드는요?

<?php
   $var = "laruence";
   $var_dup = $var;
   $var = 1;
?>
이 코드가 실행된 후에 $var_dup의 값은 아직도'laruence'일 것이다. 그러면 이것은 어떻게 실현된 것입니까?이것이 바로 PHP의copy on write 메커니즘입니다. PHP는 변수를 수정하기 전에 이 변수의refcount를 먼저 볼 것입니다. 만약refcount가 1보다 크면 PHP는 분리된 절차를 실행합니다. 위의 코드에 대해 세 번째 줄까지 실행할 때 PHP가 $var가 가리키는 zval의refcount가 1보다 크다는 것을 발견하면 PHP는 새로운 zval을 복사하여 원래의 zval의refcount를 1을 줄이고 symbol_를 수정합니다.table, $var 및 $var_dup 분리(Separation).이 메커니즘은 소위copy on write이다.코드 테스트:

<?php
   $var = "laruence";
   $var_dup = $var;
   $var = 1;
   debug_zval_dump($var);
   debug_zval_dump($var_dup);
?>


long(1) refcount(2)
string(8) "laruence" refcount(2

현재 우리는 변수 복제를 사용할 때 PHP 내부는 진정한 복제가 아니라 같은 구조를 가리키며 비용을 최대한 절약하는 것을 알고 있다.그러면 PHP의 인용은 어떻게 이루어질까요?

<?php
   $var = "laruence";
   $var_ref = &$var;
   $var_ref = 1;
?>
이 코드가 끝난 후에 $var도 간접적으로 1로 수정됩니다. 이 과정을 (change on write: 쓸 때 변경) 라고 합니다.그렇다면 ZE는 이번 복제에 Separation이 필요 없다는 것을 어떻게 알았을까?이럴 때 zval에 써야 되는 is_ref 필드: 위의 코드에 대해 두 번째 줄이 실행되면 $var가 대표하는 zval의 refcount가 2로 바뀌고 is_ref는 1입니다.세 번째 줄까지 PHP에서 var_ 확인ref가 대표하는 zval의 is_ff 필드가 1이면 분리되지 않고 대체적인 논리는 다음과 같다.

 if((*val)->is_ref || (*val)->refcount<2){
          // Separation
        ... ;//process
  }
그러나 문제가 또 생겼다. 다음과 같은 코드에 대해서는 어떻게 될까?

<?php
   $var = "laruence";
   $var_dup = $var;
   $var_ref = &$var;
?>
위의 코드에 대해copy on write의 변수 $var와 $var_가 존재합니다dup, 또 한 쌍의change on write 메커니즘의 변수가 $var와 $var_ref, 이 상황은 또 어떻게 작동합니까?두 번째 줄이 실행될 때, 앞에서 말한 바와 같이, $var_dup와 $var는 같은 zval을 가리키며,refcount는 2.세 번째 줄을 실행할 때 PHP가 조작할 zval의 refcount가 1보다 큰 것을 발견하면 PHP는 Separation을 실행하고 $var_dup를 분리하고 $var와 $var_ref는 change on write 연결을 합니다.즉,refcount=2,is_ref=1;이러한 분석을 바탕으로 debug_zval_dump는 refcount가 1이라는 결과를 내놓았다.

<?php
     $var = "laruence";
    $var_dup = &$var;
     debug_zval_dump($var);
?>


string(8) "laruence" refcount(1

상세한 원인은 독자가 조금만 분석하면 알 수 있다. 나는 주제넘게 나서지 않겠다.이번에 우리는 PHP의 변수 분리 메커니즘을 소개했는데, 다음에 확장에서 PHP 스크립트의 매개 변수를 수신하고 전달하면 계속 소개하겠습니다.

좋은 웹페이지 즐겨찾기