Skip to content

Commit

Permalink
第三章校正完成
Browse files Browse the repository at this point in the history
  • Loading branch information
[email protected] committed Dec 23, 2012
1 parent c52a507 commit 772bd29
Show file tree
Hide file tree
Showing 2 changed files with 6 additions and 6 deletions.
8 changes: 4 additions & 4 deletions 3.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ void call_function(const char *fname, int fname_len TSRMLS_DC)
<div class="tip-common">
其实php_error_docref()函数就相当与php语言里的trigger_error()函数.它的第一个参数是一个将被添加到docref的可选的文档引用第三个参数可以是任何我们熟悉的E_*家族常量,用于指示错误的严重程度。后面的两个参数就像printf()风格的格式化和变量参数列表式样。</div>
### Zend内存管理器
在上面的"跳出"请求期间解决内存泄漏的方案之一是:使用Zend内存管理(Zend Memory Manager,简称ZendMM、ZMM)层。内核的这一部分非常类似于操作系统的内存管理功能——分配内存给调用程序。区别在于,它处于进程空间中非常低的位置而且是"请求感知"的;这样以来,当一个请求结束时,它能够执行与OS在一个进程终止时相同的行为。也就是说,它会隐式地释放所有的为该请求所占用的内存。图1展示了ZendMM与OS以及PHP进程之间的关系。
<p style="text-align:center;"><img src="image/03fig01.jpg" />
在上面的"跳出"请求期间解决内存泄漏的方案之一是:使用Zend内存管理(Zend Memory Manager,简称ZendMM、ZMM)层。内核的这一部分非常类似于操作系统的内存管理功能——分配内存给调用程序。区别在于,它处于进程空间中非常低的位置而且是"请求感知"的;这样一来,当一个请求结束时,它能够执行与OS在一个进程终止时相同的行为。也就是说,它会隐式地释放所有的为该请求所占用的内存。图1展示了ZendMM与OS以及PHP进程之间的关系。
<p style="text-align:center;"><img src="http://www.walu.cc/phpbook/image/03fig01.jpg" />
除了提供隐式的内存清除功能之外,ZendMM还能够根据php.ini中memory_limit设置来控制每一次内存请求行为,如果一个脚本试图请求比系统中可用内存更多的内存,或大于它每次应该请求的最大量,那么,ZendMM将自动地发出一个E_ERROR消息并且启动相应的终止进程。这种方法的一个额外优点在于,大多数内存分配调用的返回值并不需要检查,因为如果失败的话将会导致立即跳转到引擎的退出部分。
把PHP内核代码和OS的实际的内存管理层"钩"在一起的原理并不复杂:所有内部分配的内存都要使用一组特定的可选函数实现。例如,PHP内核代码不是使用malloc(16)来分配一个16字节内存块而是使用了emalloc(16)。除了实现实际的内存分配任务外,ZendMM还会使用相应的绑定请求类型来标志该内存块;这样以来,当一个请求"跳出"时,ZendMM可以隐式地释放它。
有些事后,某次申请的内存需要在一个请求结束后仍然存活一段时间,也就是持续性存在于各个请求之间。这种类型的分配(因其在一次请求结束之后仍然存在而被称为"永久性分配"),可以使用传统型内存分配器来实现,因为这些分配并不会添加ZendMM使用的那些额外的相应于每种请求的信息。然而有时,我们必须在程序运行时根据某个数据的具体值或者状态才能确定是否需要进行永久性分配,因此ZendMM定义了一组帮助宏,其行为类似于其它的内存分配函数,但是使用最后一个额外参数来指示是否为永久性分配。
有些时候,某次申请的内存需要在一个请求结束后仍然存活一段时间,也就是持续性存在于各个请求之间。这种类型的分配(因其在一次请求结束之后仍然存在而被称为"永久性分配"),可以使用传统型内存分配器来实现,因为这些分配并不会添加ZendMM使用的那些额外的相应于每种请求的信息。然而有时,我们必须在程序运行时根据某个数据的具体值或者状态才能确定是否需要进行永久性分配,因此ZendMM定义了一组帮助宏,其行为类似于其它的内存分配函数,但是使用最后一个额外参数来指示是否为永久性分配。
如果你确实想实现一个永久性分配,那么这个参数应该被设置为1;在这种情况下,请求是通过传统型malloc()分配器家族进行传递的。然而,如果运行时刻逻辑认为这个块不需要永久性分配;那么,这个参数可以被设置为零,并且调用将会被调整到针对每种请求的内存分配器函数。
例如,pemalloc(buffer_len,1)将映射到malloc(buffer_len),而pemalloc(buffer_len,0)将被使用下列语句映射到emalloc(buffer_len):
````c
Expand Down Expand Up @@ -115,7 +115,7 @@ void *safe_emalloc(size_t size, size_t count, size_t addtl);
void *safe_pemalloc(size_t size, size_t count, size_t addtl, char persistent);

````
这些函数分配的内存空间最终大小都是((size*count)+addtl)。你可以会问:"为什么还要提供额外函数呢?为什么不使用一个emalloc/pemalloc呢?"原因很简单:为了安全,以防万一。尽管有时候可能性相当小,但是,正是这一"可能性相当小"的结果导致宿主平台的内存溢出。这可能会导致分配负数个数的字节空间,或更有甚者,会导致分配一个小于调用程序要求大小的字节空间。而safe_emalloc()能够避免这种类型的陷井-通过检查整数溢出并且在发生这样的溢出时显式地预以结束。
这些函数分配的内存空间最终大小都是((size*count)+addtl)。你可能会问:"为什么还要提供额外函数呢?为什么不使用一个emalloc/pemalloc呢?"原因很简单:为了安全,以防万一。尽管有时候可能性相当小,但是,正是这一"可能性相当小"的结果导致宿主平台的内存溢出。这可能会导致分配负数个数的字节空间,或更有甚者,会导致分配一个小于调用程序要求大小的字节空间。而safe_emalloc()能够避免这种类型的陷井-通过检查整数溢出并且在发生这样的溢出时显式地预以结束。
<p class="note">注意,并不是所有的内存分配例程都有一个相应的p*对等实现。例如,不存在pestrndup(),并且在PHP 5.1版本前也不存在safe_pemalloc()。
<hr />
Expand Down
4 changes: 2 additions & 2 deletions 3.2.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ if ((*varval)->is_ref || (*varval)->refcount < 2) {
````
这里我们可以看到,$a,$b,$c这三个变量现在共用一个zval结构,有两个属于change-on-write组合($a,$c),有两个属于copy-on-write组合($a,$b),我们的is_ref__gc和refcount__gc该怎样工作,才能正确的处理好这段复杂的关系呢?
The answer is: 不可能!在这种情况下,变量的值必须分离成两份完全独立的存在!$a与$c共用一个zval,$b自己用一个zval,尽管他们拥有同样的值,但是必须至少通过两个zval来实现。见图3.2【在引用时强制复制!】
<p style="text-align:center"><img src="image/03fig02.jpg" />
<p style="text-align:center"><img src="http://www.walu.cc/phpbook/image/03fig02.jpg" />
同样,下面的这段代码同样会在内核中产生歧义,所以需要强制复制!
<p style="text-align:center"><img src="image/03fig03.jpg" />
<p style="text-align:center"><img src="http://www.walu.cc/phpbook/image/03fig03.jpg" />
````php
//上图对应的代码
$a = 1;
Expand Down

0 comments on commit 772bd29

Please sign in to comment.