Perl 内部结构详解

PerlGuts Illustrated


直接命令行的方法查看:perl -MDevel::Peek -e "$a = 123; Dump $a"

简单的例子用来查看变量内部结构:

  1. use Devel::Peek;  
  2.   
  3. $a  = 123;  
  4. @a = 1..10;  
  5. %a = 1..10;  
  6.           
  7. Dump $a;  
  8. Dump \@a ;  
  9. Dump \%a;  

标量:

  1. SV = IV(0x2410960) at 0x2410964  
  2.   REFCNT = 1  
  3.   FLAGS = (IOK,pIOK)  
  4.   IV = 123  

数组:

  1. SV = RV(0x3a7190) at 0x3a7184  
  2.   REFCNT = 1  
  3.   FLAGS = (TEMP,ROK)  
  4.   RV = 0x24109c4  
  5.   SV = PVAV(0x3a80ac) at 0x24109c4  
  6.     REFCNT = 2  
  7.     FLAGS = ()  
  8.     ARRAY = 0x24c1aec  
  9.     FILL = 9  
  10.     MAX = 9  
  11.     ARYLEN = 0x0  
  12.     FLAGS = (REAL)  
  13.     Elt No. 0  
  14.     SV = IV(0x24c0450) at 0x24c0454  
  15.       REFCNT = 1  
  16.       FLAGS = (IOK,pIOK)  
  17.       IV = 1  
  18.     Elt No. 1  
  19.     SV = IV(0x24c0420) at 0x24c0424  
  20.       REFCNT = 1  
  21.       FLAGS = (IOK,pIOK)  
  22.       IV = 2  
  23.     Elt No. 2  
  24.     SV = IV(0x24b7e40) at 0x24b7e44  
  25.       REFCNT = 1  
  26.       FLAGS = (IOK,pIOK)  
  27.       IV = 3  
  28.     Elt No. 3  
  29.     SV = IV(0x24b7e50) at 0x24b7e54  
  30.       REFCNT = 1  
  31.       FLAGS = (IOK,pIOK)  
  32.       IV = 4  

Hash:

  1. SV = RV(0xe7190) at 0xe7184  
  2.   REFCNT = 1  
  3.   FLAGS = (TEMP,ROK)  
  4.   RV = 0x24aaa0c  
  5.   SV = PVHV(0x2401c94) at 0x24aaa0c  
  6.     REFCNT = 2  
  7.     FLAGS = (SHAREKEYS)  
  8.     ARRAY = 0x24bd47c  (0:3, 1:5)  
  9.     hash quality = 150.0%  
  10.     KEYS = 5  
  11.     FILL = 5  
  12.     MAX = 7  
  13.     RITER = -1  
  14.     EITER = 0x0  
  15.     Elt "1" HASH = 0x806b80c9  
  16.     SV = IV(0x24b7ca0) at 0x24b7ca4  
  17.       REFCNT = 1  
  18.       FLAGS = (IOK,pIOK)  
  19.       IV = 2  
  20.     Elt "3" HASH = 0xa400c7f3  
  21.     SV = IV(0x24b7c80) at 0x24b7c84  
  22.       REFCNT = 1  
  23.       FLAGS = (IOK,pIOK)  
  24.       IV = 4  
  25.     Elt "7" HASH = 0xecc9d984  
  26.     SV = IV(0x24b7c60) at 0x24b7c64  
  27.       REFCNT = 1  
  28.       FLAGS = (IOK,pIOK)  
  29.       IV = 8  

首先看一下Perl data structure。Perl内部称为SV(scalar value), AV(array value), HV(hash value), 此外IV代表integer, NV代表double, PV代表string, RV代表指针,指向另外的任意数据结构。


Perl内部的数据结构之间的关系很像OO,用C structure内存裁减的方式模拟C++的继承。各种结构之间的继承关系如下图所示,它们之间是IS-A(是一个)的关系。

Perl 内部结构详解


正如图所见,Perl使用以SvNULL为虚拟基类的多重继承,所有的类型都是根据type(small number)来确定并标记,因此你可以获取一个object的数据类型并且执行相应的操作。


下边为类型type 的定义,相应的解释分析见随后的blog:

  1. typedef enum {  
  2.     SVt_NULL,   /* 0 */  
  3.     SVt_IV,     /* 1 */  
  4.     SVt_NV,     /* 2 */  
  5.     SVt_RV,     /* 3 */  
  6.     SVt_PV,     /* 4 */  
  7.     SVt_PVIV,   /* 5 */  
  8.     SVt_PVNV,   /* 6 */  
  9.     SVt_PVMG,   /* 7 */  
  10.     SVt_PVBM,   /* 8 */  
  11.     SVt_PVLV,   /* 9 */  
  12.     SVt_PVAV,   /* 10 */  
  13.     SVt_PVHV,   /* 11 */  
  14.     SVt_PVCV,   /* 12 */  
  15.     SVt_PVGV,   /* 13 */  
  16.     SVt_PVFM,   /* 14 */  
  17.     SVt_PVIO,   /* 15 */  
  18.     SVt_LAST    /* keep last in enum. used to size arrays */  
  19. } svtype;  

_SV_HEAD and struct sv

下面来看一下最简单的类型struct sv,代表了SV,GV,CV,AV,HV,IO的通用结构。如下图所示:

Perl 内部结构详解

第一个字段Any可以指向任意结构,除了RV,所有的其他类型都是由Any指向的附加数据来实现。
第二个字段REFCNT表明了有多少pointers引用了这个object。初始置为1,当有pointers指向它或者被销毁的时候,这个值需要相应的加1或者减1,当值为0的时候,内存释放。
第三个字段包含了FLAGS & TYPE,是一个32 bit unsigned int。如下图:

Perl 内部结构详解

常见的flags见下边具体示例。


  1. C:\>perl -MDevel::Peek -e "Dump $a"  
  2. SV = NULL(0x0) at 0x182a9fc  
  3.   REFCNT = 1  
  4.   FLAGS = ()  
  5.   
  6. C:\>perl -MDevel::Peek -e "Dump \$a"  
  7. SV = RV(0x299158) at 0x29914c  
  8.   REFCNT = 1  
  9.   FLAGS = (TEMP,ROK)  
  10.   RV = 0x182a9fc  
  11.   SV = NULL(0x0) at 0x182a9fc  
  12.     REFCNT = 2  
  13.     FLAGS = ()  


SvIV and SvNV

整数和小树,结构如图所示:

Perl 内部结构详解

Perl 内部结构详解

  1. C:\>perl -MDevel::Peek -e "$a = 123; Dump $a"  
  2. SV = IV(0x182aa20) at 0x182aa24  
  3.   REFCNT = 1  
  4.   FLAGS = (IOK,pIOK)  
  5.   IV = 123  
  6.   
  7. C:\>perl -MDevel::Peek -e "$a = 123.9; Dump $a"  
  8. SV = NV(0x184882c) at 0x182aa2c  
  9.   REFCNT = 1  
  10.   FLAGS = (NOK,pNOK)  
  11.   NV = 123.9  

SvPV

字符串,结构如图所示:


除了SV以外,额外的结构xpv被分配,它包含三部分:

PVX 指向实际的字符串。
CUR 标记字符串的长度,PVX+CUR 处的字符应该为'\0',标记字符串的结束。
LEN 标记内存分配给char * 的长度,以4为增量。

POK标记表示PVX所指向的内存包含有效字符串,否则所包含的字符串无效。

  1. C:\>perl -MDevel::Peek -e "$a = 't'; Dump $a"  
  2. SV = PV(0x296fec) at 0x182aa24  
  3.   REFCNT = 1  
  4.   FLAGS = (POK,pPOK)  
  5.   PV = 0x182472c "t"\0  
  6.   CUR = 1  
  7.   LEN = 4  
  8.   
  9. C:\>perl -MDevel::Peek -e "$a = 'test'; Dump $a"  
  10. SV = PV(0x296fec) at 0x182aa2c  
  11.   REFCNT = 1  
  12.   FLAGS = (POK,pPOK)  
  13.   PV = 0x182466c "test"\0  
  14.   CUR = 4  
  15.   LEN = 8 (随着长度增加,以4为增量)  
  16.   
  17. C:\>perl -MDevel::Peek -e "$a = 'test'; $a = undef; Dump $a"  
  18. SV = PV(0x296ffc) at 0x182aa34  
  19.   REFCNT = 1  
  20.   FLAGS = ()  (包含字符串,但是无效)  
  21.   PV = 0x1824674 "test"\0  
  22.   CUR = 4  
  23.   LEN = 8  

SvOOK

为了提高移除字符串开头字符的速度,使用了OOK标记,IVX存储着偏移量,结构如图所示:

Perl 内部结构详解
  1. C:\>perl -MDevel::Peek -e "$a = 'xtesting'; $a=~s/.//; Dump $a"  
  2. SV = PVIV(0x182005c) at 0x182aa4c  
  3.   REFCNT = 1  
  4.   FLAGS = (POK,OOK,pPOK)  
  5.   IV = 1  (OFFSET)  
  6.   PV = 0x182467d ( "x" . ) "testing"\0  
  7.   CUR = 7  
  8.   LEN = 11  

SvPVIV and SvPVNV

类似字符串,但是PVIV储存了额外的整数或者小数信息,可以根据flag直接进行数学运算,结构如图所示:

Perl 内部结构详解

Perl 内部结构详解

  1. C:\>perl -MDevel::Peek -e "$a = '123.0'; 0+$a; Dump $a"  
  2. SV = PVNV(0x29838c) at 0x182aa34  
  3.   REFCNT = 1  
  4.   FLAGS = (IOK,NOK,POK,pIOK,pNOK,pPOK)  
  5.   IV = 123  
  6.   NV = 123  
  7.   PV = 0x1824674 "123.0"\0  
  8.   CUR = 5  
  9.   LEN = 8  
  10.   
  11. C:\>perl -MDevel::Peek -e "$a = '123testing'; 0+$a; Dump $a"  
  12. SV = PVNV(0x298394) at 0x182aa34  
  13.   REFCNT = 1  
  14.   FLAGS = (POK,pIOK,pNOK,pPOK)  
  15.   IV = 123  
  16.   NV = 123  
  17.   PV = 0x1824674 "123testing"\0  
  18.   CUR = 10  
  19.   LEN = 12  

SvRV

类似指针,指向任意其他结构,结构如图所示:

  1. C:\>perl -MDevel::Peek -e "$a = '123testing'; $b = \$a ; Dump $b"  
  2. SV = RV(0x182ab68) at 0x182ab5c  
  3.   REFCNT = 1  
  4.   FLAGS = (ROK)  
  5.   RV = 0x182aa4c  
  6.   SV = PV(0x296ffc) at 0x182aa4c  
  7.     REFCNT = 2  
  8.     FLAGS = (POK,pPOK)  
  9.     PV = 0x182467c "123testing"\0  
  10.     CUR = 10  
  11.     LEN = 12  

AV

数组,结构如图所示:

Perl 内部结构详解

字段ALLOC 指向实际分配的SV 数组的开头地址。

字段ARRAY 指向数组本身的开头地址,由于数组的改变,这个地址比起实际分配的地址,可能会产生一定的偏移。

字段FILL 指向数组本身最后一个位置的偏移。

字段MAX 指向分配的SV 数组最后一个位置的偏移。

  1. C:\>perl -MDevel::Peek -e "@a = 1..2; Dump \@a"  
  2. SV = RV(0x299120) at 0x299114  
  3.   REFCNT = 1  
  4.   FLAGS = (TEMP,ROK)  
  5.   RV = 0x182a9cc  
  6.   SV = PVAV(0x29a03c) at 0x182a9cc  
  7.     REFCNT = 2  
  8.     FLAGS = ()  
  9.     ARRAY = 0x1852984  
  10.     FILL = 1  (数组最后一个元素index为1)  
  11.     MAX = 3   (分配的数组空间最大index为3,说明还有剩余空间来分配)  
  12.     ARYLEN = 0x0  
  13.     FLAGS = (REAL)  
  14.     Elt No. 0  
  15.     SV = IV(0x299220) at 0x299224  
  16.       REFCNT = 1  
  17.       FLAGS = (IOK,pIOK)  
  18.       IV = 1  
  19.     Elt No. 1  
  20.     SV = IV(0x299230) at 0x299234  
  21.       REFCNT = 1  
  22.       FLAGS = (IOK,pIOK)  
  23.       IV = 2  

shift pop 操作可以通过调节ARRAY FILL MAX来完成。

  1. C:\>perl -MDevel::Peek -e "@a = 0..3; Dump \@a ; shift @a ; pop @a ; Dump \@a"  
  2. SV = RV(0x299130) at 0x299124  
  3.   REFCNT = 1  
  4.   FLAGS = (TEMP,ROK)  
  5.   RV = 0x182aa14  
  6.   SV = PVAV(0x29a04c) at 0x182aa14  
  7.     REFCNT = 2  
  8.     FLAGS = ()  
  9.     ARRAY = 0x1824afc  
  10.     FILL = 3  
  11.     MAX = 3  
  12.     ARYLEN = 0x0  
  13.     FLAGS = (REAL)  
  14.     Elt No. 0  
  15.     SV = IV(0x299280) at 0x299284  
  16.       REFCNT = 1  
  17.       FLAGS = (IOK,pIOK)  
  18.       IV = 0  
  19. ...  
  20. SV = RV(0x2991e0) at 0x2991d4  
  21.   REFCNT = 1  
  22.   FLAGS = (TEMP,ROK)  
  23.   RV = 0x182aa14  
  24.   SV = PVAV(0x29a04c) at 0x182aa14  
  25.     REFCNT = 2  
  26.     FLAGS = ()  
  27.     ARRAY = 0x1824b00 (offset=1)  
  28.     ALLOC = 0x1824afc  
  29.     FILL = 1  
  30.     MAX = 2  
  31.     ARYLEN = 0x0  
  32.     FLAGS = (REAL)  
  33.     Elt No. 0  
  34.     SV = IV(0x299200) at 0x299204  
  35.       REFCNT = 1  
  36.       FLAGS = (IOK,pIOK)  
  37.       IV = 1  
  38.     Elt No. 1  
  39.     SV = IV(0x182aa40) at 0x182aa44  
  40.       REFCNT = 1  
  41.       FLAGS = (IOK,pIOK)  
  42.       IV = 2  
FLAGS REAL 表示所有内部的SV都需要有自己的引用计数,一般的AV 都是使用这个标记。

FLAGS REIFY 表示这个数组不是一个REAL 类型的,当这个数组发生改变的时候需要置为REAL,默认参数@_ 都是使用REIFY 标记。

HV

hash table是最复杂的数据结构,HV使用HE struct表示key/value结构,使用HEK表示key。

Perl 内部结构详解

GvSTASH 当这个hash表示一个命名空间(模块),STASH指向Perl 语法树的一个节点,用来实现reset。(略过,未搞明白)

ARRAY 数组用于分配储存hash值,它的大小必须是2的n次方,当hash为空情况下,ARRAY为NULL。定位hash值在ARRAY中的位置只使用hash code的最后几位,ARRAY[HASH & MAX],稍后用一个例子说明。

FILL 表示ARRAY数组中有多少个不为NULL的节点。多个hash code可能共享ARRAY数组的一个节点,如上图所示。

MAX 表示ARRAY数组分配的空间减1,最小值为7,即使hash为空。

HE 包含三个指针,分别指向下一个节点,key和value。

HEK 包含hash code,key长度和key值。

RITER, EITER 这两个字段用来实现遍历hash 元素,RITER指向ARRAY的index,EITER指向HE的指针。当循环的时候,查找EITER->next值,为空的情况下RITER增1,直到ARRAY[RITER]不为空。初始情况RITER为-1,EITER 为空。

  1. C:\>perl -MDevel::Peek -e "%a=0..5000; Dump \%a"  
  2. SV = RV(0x299120) at 0x299114  
  3.   REFCNT = 1  
  4.   FLAGS = (TEMP,ROK)  
  5.   RV = 0x182a9cc  
  6.   SV = PVHV(0x29e7bc) at 0x182a9cc  
  7.     REFCNT = 2  
  8.     FLAGS = (SHAREKEYS)  
  9.     ARRAY = 0x18d12c4  (0:2236, 1:1341, 2:417, 3:85, 4:14, 5:3)  
  10.         (ARRAY中有2236个位置未占用,  
  11.          有1341个位置包含1个元素,  
  12.          有417个位置包含2个元素,  
  13.          有85个位置包含3个元素,  
  14.          有14个位置包含4个元素,  
  15.          有3个位置包含5个元素,  
  16.          1341+417+85+14+3=1860,正好是FILL的值  
  17.         )  
  18.     hash quality = 98.9%  
  19.     KEYS = 2501 (hash包含的元素个数)  
  20.     FILL = 1860 (hash code占用多少ARRAY位置,说明有多个值占用一个坑)  
  21.     MAX = 4095  (ARRAY分配了4096)  
  22.     RITER = -1  
  23.     EITER = 0x0  
  24.     Elt "1648" HASH = 0x2bb13004  
  25.     SV = IV(0x185a840) at 0x185a844  
  26.       REFCNT = 1  
  27.       FLAGS = (IOK,pIOK)  
  28.       IV = 1649  
  29.     Elt "3596" HASH = 0x4b03006  
  30.     SV = IV(0x18a4e38) at 0x18a4e3c  
  31.       REFCNT = 1  
  32.       FLAGS = (IOK,pIOK)  
  33.       IV = 3597  
  34.     Elt "3074" HASH = 0x1256006  
  35.     SV = IV(0x18beff0) at 0x18beff4  
  36.       REFCNT = 1  
  37.       FLAGS = (IOK,pIOK)  
  38.       IV = 3075  
下边用一个例子说明RITER, EITER的变化过程。

取上边3个元素的hash code并与MAX 4095做& 运算,求得前三个元素在数组ARRAY中位置为4 6 6。

  1. printf "%d\n",  0x2bb13004 & 4095;  
  2. printf "%d\n",  0x4b03006 & 4095;  
  3. printf "%d\n",  0x1256006 & 4095;  
  4.   
  5. output:  
  6. 4  
  7. 6  
  8. 6  
依次取hash 值并观察RITER, EITER的变化。
  1. use Devel::Peek;  
  2.   
  3. %a = 0..5000;  
  4. #~ Dump \%a;  
  5.   
  6. $i = 0;  
  7. while( my($a,$b) =each %a){  
  8.     last if $i++ > 4;  
  9.     print "$a $b\n";  
  10.     Dump \%a;  
  11. }  
值输出,同上边相同:
  1. 1648 1649  
  2. 3596 3597  
  3. 3074 3075  
  4. 2266 2267  
  5. 1178 1179  
RITER EITER在循环过程中ARRAY位置4 6 6与预计相同。
  1. SV = RV(0x298f98) at 0x298f8c  
  2.   REFCNT = 1  
  3.   FLAGS = (TEMP,ROK)  
  4.   RV = 0x182a8cc  
  5.   SV = PVHV(0x29e59c) at 0x182a8cc  
  6.     REFCNT = 2  
  7.     FLAGS = (OOK,SHAREKEYS)  
  8.     ARRAY = 0x18d1164  (0:2236, 1:1341, 2:417, 3:85, 4:14, 5:3)  
  9.     hash quality = 98.9%  
  10.     KEYS = 2501  
  11.     FILL = 1860  
  12.     MAX = 4095  
  13.     RITER = 4  
  14.     EITER = 0x18a3c50  
  15. SV = RV(0x298f98) at 0x298f8c  
  16.   REFCNT = 1  
  17.   FLAGS = (TEMP,ROK)  
  18.   RV = 0x182a8cc  
  19.   SV = PVHV(0x29e59c) at 0x182a8cc  
  20.     REFCNT = 2  
  21.     FLAGS = (OOK,SHAREKEYS)  
  22.     ARRAY = 0x18d1164  (0:2236, 1:1341, 2:417, 3:85, 4:14, 5:3)  
  23.     hash quality = 98.9%  
  24.     KEYS = 2501  
  25.     FILL = 1860  
  26.     MAX = 4095  
  27.     RITER = 6  
  28.     EITER = 0x18c3f48  
  29. SV = RV(0x298f98) at 0x298f8c  
  30.   REFCNT = 1  
  31.   FLAGS = (TEMP,ROK)  
  32.   RV = 0x182a8cc  
  33.   SV = PVHV(0x29e59c) at 0x182a8cc  
  34.     REFCNT = 2  
  35.     FLAGS = (OOK,SHAREKEYS)  
  36.     ARRAY = 0x18d1164  (0:2236, 1:1341, 2:417, 3:85, 4:14, 5:3)  
  37.     hash quality = 98.9%  
  38.     KEYS = 2501  
  39.     FILL = 1860  
  40.     MAX = 4095  
  41.     RITER = 6  
  42.     EITER = 0x18ba91c  
  43. SV = RV(0x298f98) at 0x298f8c  
  44.   REFCNT = 1  
  45.   FLAGS = (TEMP,ROK)  
  46.   RV = 0x182a8cc  
  47.   SV = PVHV(0x29e59c) at 0x182a8cc  
  48.     REFCNT = 2  
  49.     FLAGS = (OOK,SHAREKEYS)  
  50.     ARRAY = 0x18d1164  (0:2236, 1:1341, 2:417, 3:85, 4:14, 5:3)  
  51.     hash quality = 98.9%  
  52.     KEYS = 2501  
  53.     FILL = 1860  
  54.     MAX = 4095  
  55.     RITER = 9  
  56.     EITER = 0x18b1c4c  
  57. SV = RV(0x298f98) at 0x298f8c  
  58.   REFCNT = 1  
  59.   FLAGS = (TEMP,ROK)  
  60.   RV = 0x182a8cc  
  61.   SV = PVHV(0x29e59c) at 0x182a8cc  
  62.     REFCNT = 2  
  63.     FLAGS = (OOK,SHAREKEYS)  
  64.     ARRAY = 0x18d1164  (0:2236, 1:1341, 2:417, 3:85, 4:14, 5:3)  
  65.     hash quality = 98.9%  
  66.     KEYS = 2501  
  67.     FILL = 1860  
  68.     MAX = 4095  
  69.     RITER = 11  
  70.     EITER = 0x1899dcc  

GV

GV "Global Value" 或者“符号表”,存储着变量或者函数的指针GP,由GP中slot 相关的指示来决定是否存在对应这个变量相应的类型。如图所示:
Perl 内部结构详解
GP可以在多个GV中共享,试运行下例查看结果。
  1. D:\perl -MDevel::Peek -e "*test=*Dump; Dump *Dump; Dump *test"  
  2. SV = PVGV(0x285cda4) at 0x286c0dc  
  3.   REFCNT = 5  
  4.   FLAGS = (PADTMP,MULTI,ASSUMECV,IN_PAD,IMPORT( CV ))  
  5.   NAME = "Dump"  
  6.   NAMELEN = 4  
  7.   GvSTASH = 0x3a8fd4    "main"  
  8.   GP = 0x2864ffc  
  9.     SV = 0x0  
  10.     REFCNT = 2  
  11.     IO = 0x0  
  12.     FORM = 0x0    
  13.     AV = 0x0  
  14.     HV = 0x0  
  15.     CV = 0x2883874  
  16.     CVGEN = 0x0  
  17.     LINE = 67  
  18.     FILE = "D:/Perl/lib/Exporter.pm"  
  19.     FLAGS = 0x8e  
  20.     EGV = 0x286c0dc "Dump"  
  21. SV = PVGV(0x285cf04) at 0x285a85c  
  22.   REFCNT = 3  
  23.   FLAGS = (MULTI,IN_PAD)  
  24.   NAME = "test"  
  25.   NAMELEN = 4  
  26.   GvSTASH = 0x3a8fd4    "main"  
  27.   GP = 0x2864ffc  
  28.     SV = 0x0  
  29.     REFCNT = 2  
  30.     IO = 0x0  
  31.     FORM = 0x0    
  32.     AV = 0x0  
  33.     HV = 0x0  
  34.     CV = 0x2883874  
  35.     CVGEN = 0x0  
  36.     LINE = 67  
  37.     FILE = "D:/Perl/lib/Exporter.pm"  
  38.     FLAGS = 0xa  
  39.     EGV = 0x286c0dc "Dump"  
REFCNT 引用数量,上边例子可以观察到。
EGV (effective gv) 表示创建这个GP的GV地址。
LINE 文件中行数。
FILE_HEK 在哪个文件中创建这个GV。

对应变量的GV可以用*var 来表示,字段GvSTASH 表示字段所在的命名空间,默认有一个main 命名空间。所有存储于GV中的类型都是全局的。
  1. D:\perl -MDevel::Peek -e "$a = 123; Dump *a"  
  2. SV = PVGV(0x285ced4) at 0x285a82c  
  3.   REFCNT = 3  
  4.   FLAGS = (MULTI,IN_PAD)  
  5.   NAME = "a"  
  6.   NAMELEN = 1  
  7.   GvSTASH = 0x3a8f9c    "main"  
  8.   GP = 0x286438c  
  9.     SV = 0x285a83c  
  10.     REFCNT = 1  
  11.     IO = 0x0  
  12.     FORM = 0x0    
  13.     AV = 0x0  
  14.     HV = 0x0  
  15.     CV = 0x0  
  16.     CVGEN = 0x0  
  17.     LINE = 1  
  18.     FILE = "-e"  
  19.     FLAGS = 0xa  
  20.     EGV = 0x285a82c "a"  

如果是从其它模块中引入的,则是通过Export引入main命名空间,实际指向的还是原始模块中的GV,看以下两个例子,函数Dump CV相同。
  1. D:\Tmp>perl -MDevel::Peek -e "Dump *Devel::Peek::Dump"  
  2. SV = PVGV(0x285c894) at 0x286c714  
  3.   REFCNT = 3  
  4.   FLAGS = (MULTI,IN_PAD)  
  5.   NAME = "Dump"  
  6.   NAMELEN = 4  
  7.   GvSTASH = 0x285aa0c   "Devel::Peek"  
  8.   GP = 0x2874484  
  9.     SV = 0x0  
  10.     REFCNT = 1  
  11.     IO = 0x0  
  12.     FORM = 0x0    
  13.     AV = 0x0  
  14.     HV = 0x0  
  15.     CV = 0x2883854  
  16.     CVGEN = 0x0  
  17.     LINE = 44  
  18.     FILE = "D:/Perl/lib/Devel/Peek.pm"  
  19.     FLAGS = 0xa  
  20.     EGV = 0x286c714 "Dump"  
  21.   
  22. D:\Tmp>perl -MDevel::Peek -e "Dump *Dump"  
  23. SV = PVGV(0x285cd8c) at 0x286c0f4  
  24.   REFCNT = 3  
  25.   FLAGS = (PADTMP,MULTI,ASSUMECV,IN_PAD,IMPORT( CV ))  
  26.   NAME = "Dump"  
  27.   NAMELEN = 4  
  28.   GvSTASH = 0x3a8fc4    "main"  
  29.   GP = 0x2864fe4  
  30.     SV = 0x0  
  31.     REFCNT = 1  
  32.     IO = 0x0  
  33.     FORM = 0x0    
  34.     AV = 0x0  
  35.     HV = 0x0  
  36.     CV = 0x2883854  
  37.     CVGEN = 0x0  
  38.     LINE = 67  
  39.     FILE = "D:/Perl/lib/Exporter.pm"  
  40.     FLAGS = 0x8e  
  41.     EGV = 0x286c0f4 "Dump"  

Stashes

GVs 同Stashes 共同作用实现了Perl 的命名空间,Stash 实际是HV,所有内容都指向GV。命名空间root 是defstash,指向main Stash。一个多层次模块,每一层都有一个相应的Stash。如下所示整体命名空间结构:
Perl 内部结构详解

示例察看main 空间的内容,*:: 等同于*main::
  1. D:\Tmp>perl -MDevel::Peek -e "Dump *::"  
  2. SV = PVGV(0x2851fdc) at 0x3a8fe4  
  3.   REFCNT = 2  
  4.   FLAGS = (READONLY,MULTI,IN_PAD)  
  5.   NAME = "main::"  
  6.   NAMELEN = 6  
  7.   GvSTASH = 0x3a8fc4    "main"  
  8.   GP = 0x2852fd4  
  9.     SV = 0x0  
  10.     REFCNT = 1  
  11.     IO = 0x0  
  12.     FORM = 0x0    
  13.     AV = 0x0  
  14.     HV = 0x3a8fc4  
  15.     CV = 0x0  
  16.     CVGEN = 0x0  
  17.     LINE = 0  
  18.     FILE = ""  
  19.     FLAGS = 0xa  
  20.     EGV = 0x3a8fe4  "main::"  

查看main 中HV 包含的内容,节选了部分来演示:
  1. D:\Tmp>perl -MDevel::Peek -e "Dump \%::,1000"  
  2. SV = RV(0x3a90e0) at 0x3a90d4  
  3.   REFCNT = 1  
  4.   FLAGS = (TEMP,ROK)  
  5.   RV = 0x3a8fc4  
  6.   SV = PVHV(0x3ae2dc) at 0x3a8fc4  
  7.     REFCNT = 3  
  8.     FLAGS = (OOK,SHAREKEYS)  
  9.     ARRAY = 0x287b234  (0:74, 1:44, 2:8, 3:2)  
  10.     hash quality = 105.9%  
  11.     KEYS = 66  
  12.     FILL = 54  
  13.     MAX = 127  
  14.     RITER = -1  
  15.     EITER = 0x0  
  16.     NAME = "main"  
  17.     BACKREFS = 0x3a8ff4  
  18.       
  19.     ......  
  20.       
  21.     Elt "Devel::" HASH = 0xfa558e0e  
  22.     SV = PVGV(0x285c314) at 0x285a9cc  
  23.       REFCNT = 1  
  24.       FLAGS = (MULTI)  
  25.       NAME = "Devel::"  
  26.       NAMELEN = 7  
  27.       GvSTASH = 0x3a8fc4    "main"  
  28.       GP = 0x28654ac  
  29.         SV = 0x0  
  30.         REFCNT = 1  
  31.         IO = 0x0  
  32.         FORM = 0x0    
  33.         AV = 0x0  
  34.         HV = 0x285a9dc  
  35.         CV = 0x0  
  36.         CVGEN = 0x0  
  37.         LINE = 4  
  38.         FILE = "D:/Perl/lib/Devel/Peek.pm"  
  39.         FLAGS = 0x2  
  40.         EGV = 0x285a9cc "Devel::"  
  41.     ......  
  42.     Elt "Dump" HASH = 0xbbea0f76  
  43.     SV = PVGV(0x285cd94) at 0x286c0f4  
  44.       REFCNT = 2  
  45.       FLAGS = (PADTMP,MULTI,ASSUMECV,IN_PAD,IMPORT( CV ))  
  46.       NAME = "Dump"  
  47.       NAMELEN = 4  
  48.       GvSTASH = 0x3a8fc4    "main"  
  49.       GP = 0x2864fe4  
  50.         SV = 0x0  
  51.         REFCNT = 1  
  52.         IO = 0x0  
  53.         FORM = 0x0    
  54.         AV = 0x0  
  55.         HV = 0x0  
  56.         CV = 0x2883854  
  57.         CVGEN = 0x0  
  58.         LINE = 67  
  59.         FILE = "D:/Perl/lib/Exporter.pm"  
  60.         FLAGS = 0x8e  
  61.         EGV = 0x286c0f4 "Dump"  
  62.     ......  
  63.     Elt "main::" HASH = 0x40383f65  
  64.     SV = PVGV(0x2851fdc) at 0x3a8fe4  
  65.       REFCNT = 2  
  66.       FLAGS = (READONLY,MULTI,IN_PAD)  
  67.       NAME = "main::"  
  68.       NAMELEN = 6  
  69.       GvSTASH = 0x3a8fc4    "main"  
  70.       GP = 0x2852fd4  
  71.         SV = 0x0  
  72.         REFCNT = 1  
  73.         IO = 0x0  
  74.         FORM = 0x0    
  75.         AV = 0x0  
  76.         HV = 0x3a8fc4  
  77.         CV = 0x0  
  78.         CVGEN = 0x0  
  79.         LINE = 0  
  80.         FILE = ""  
  81.         FLAGS = 0xa  
  82.         EGV = 0x3a8fe4  "main::"  
  83.     Elt "ENV" HASH = 0x62ffe0d6  
  84.     SV = PVGV(0x285c2b4) at 0x285a55c  
  85.       REFCNT = 1  
  86.       FLAGS = (MULTI)  
  87.       NAME = "ENV"  
  88.       NAMELEN = 3  
  89.       GvSTASH = 0x3a8fc4    "main"  
  90.       GP = 0x285e184  
  91.         SV = 0x0  
  92.         REFCNT = 1  
  93.         IO = 0x0  
  94.         FORM = 0x0    
  95.         AV = 0x0  
  96.         HV = 0x285a56c  
  97.         CV = 0x0  
  98.         CVGEN = 0x0  
  99.         LINE = 0  
  100.         FILE = "-e"  
  101.         FLAGS = 0x2  
  102.         EGV = 0x285a55c "ENV"  

查看多层次模块中的内容:
  1. D:\Tmp>perl -MDevel::Peek -e "Dump \%Devel::,1000"  
  2. SV = RV(0x3a90e0) at 0x3a90d4  
  3.   REFCNT = 1  
  4.   FLAGS = (TEMP,ROK)  
  5.   RV = 0x285a9dc  
  6.   SV = PVHV(0x3ae57c) at 0x285a9dc  
  7.     REFCNT = 2  
  8.     FLAGS = (OOK,SHAREKEYS)  
  9.     ARRAY = 0x2865534  (0:7, 1:1)  
  10.     hash quality = 100.0%  
  11.     KEYS = 1  
  12.     FILL = 1  
  13.     MAX = 7  
  14.     RITER = -1  
  15.     EITER = 0x0  
  16.     NAME = "Devel"  
  17.     BACKREFS = 0x285a9fc  
  18.     SV = PVAV(0x3aa32c) at 0x285a9fc  
  19.       REFCNT = 2  
  20.       FLAGS = ()  
  21.       ARRAY = 0x286561c  
  22.       FILL = 0  
  23.       MAX = 3  
  24.       ARYLEN = 0x0  
  25.       FLAGS = ()  
  26.       Elt No. 0  
  27.       SV = PVGV(0x285c334) at 0x285a9ec  
  28.         REFCNT = 1  
  29.         FLAGS = (MULTI)  
  30.         NAME = "Peek::"  
  31.         NAMELEN = 6  
  32.         GvSTASH = 0x285a9dc "Devel"  
  33.         GP = 0x28655dc  
  34.           SV = 0x0  
  35.           REFCNT = 1  
  36.           IO = 0x0  
  37.           FORM = 0x0    
  38.           AV = 0x0  
  39.           HV = 0x285aa0c  
  40.           CV = 0x0  
  41.           CVGEN = 0x0  
  42.           LINE = 4  
  43.           FILE = "D:/Perl/lib/Devel/Peek.pm"  
  44.           FLAGS = 0x2  
  45.           EGV = 0x285a9ec   "Peek::"  
  46.     Elt "Peek::" HASH = 0xf40ca9c  
  47.     SV = PVGV(0x285c334) at 0x285a9ec  
  48.       REFCNT = 1  
  49.       FLAGS = (MULTI)  
  50.       NAME = "Peek::"  
  51.       NAMELEN = 6  
  52.       GvSTASH = 0x285a9dc   "Devel"  
  53.       GP = 0x28655dc  
  54.         SV = 0x0  
  55.         REFCNT = 1  
  56.         IO = 0x0  
  57.         FORM = 0x0    
  58.         AV = 0x0  
  59.         HV = 0x285aa0c  
  60.         CV = 0x0  
  61.         CVGEN = 0x0  
  62.         LINE = 4  
  63.         FILE = "D:/Perl/lib/Devel/Peek.pm"  
  64.         FLAGS = 0x2  
  65.         EGV = 0x285a9ec "Peek::"  

希望通过这一节都能对Perl的整体有一个了解,相信其他语言也有类似的概念结构来实现命名空间的引入。

CV

code value,函数的结构,如下图:

Perl 内部结构详解

下边示例可以看到几个主要参数:

函数所在的Stash & GV。

函数的参数表和临时变量表PADLIST。

  1. D:\Tmp>perl -MDevel::Peek -e "sub a{my ($a,$b,$t); } Dump \&a"  
  2. SV = RV(0x3a90f0) at 0x3a90e4  
  3.   REFCNT = 1  
  4.   FLAGS = (TEMP,ROK)  
  5.   RV = 0x285a864  
  6.   SV = PVCV(0x285923c) at 0x285a864  
  7.     REFCNT = 2  
  8.     FLAGS = ()  
  9.     COMP_STASH = 0x3a8fd4   "main"  
  10.     START = 0x2885fa8 ===> 0  
  11.     ROOT = 0x2885f64  
  12.     GVGV::GV = 0x285abd4    "main" :: "a"  
  13.     FILE = "-e"  
  14.     DEPTH = 0  
  15.     FLAGS = 0x0  
  16.     OUTSIDE_SEQ = 96  
  17.     PADLIST = 0x285a874  
  18.     PADNAME = 0x285a8a4(0x2865424) PAD = 0x285a974(0x288292c)  
  19.        1. 0x285a8c4<1> (96,97) "$a"  
  20.        2. 0x285a894<1> (96,97) "$b"  
  21.        3. 0x285a904<1> (96,97) "$t"  
  22.     OUTSIDE = 0x3a9274 (MAIN)  


PAD

PAD是一个列表,为每个函数存储局部变量。第0个slot 存储参数名字,运行时产生新的AV存储实际参数,DEPTH也加深,递归函数观察效果明显。

Perl 内部结构详解

  1. use Devel::Peek;  
  2. use Data::Dumper;  
  3.   
  4. sub test{  
  5.     my ($a,$b) = @_;  
  6.     Dump \&test;  
  7.     test(++$a,--$b) if $b>0;  
  8. }  
  9.   
  10. Dump \&test;  
  11. test(1,2);  
  12.   
  13.   
  14. 输出:  
  15. SV = RV(0x298f50) at 0x298f44  
  16.   REFCNT = 1  
  17.   FLAGS = (TEMP,ROK)  
  18.   RV = 0x182a834  
  19.   SV = PVCV(0x1829024) at 0x182a834  
  20.     REFCNT = 2  
  21.     FLAGS = ()  
  22.     COMP_STASH = 0x298e34   "main"  
  23.     START = 0x18ba94c ===> 0  
  24.     ROOT = 0x18ba4cc  
  25.     GVGV::GV = 0x184b34c    "main" :: "test"  
  26.     FILE = "tmp.pl"  
  27.     DEPTH = 0  
  28.     FLAGS = 0x0  
  29.     OUTSIDE_SEQ = 535  
  30.     PADLIST = 0x182a864  
  31.     PADNAME = 0x182a934(0x18c7c6c) PAD = 0x182a8c4(0x18c7be4)  
  32.        1. 0x182a854<1> (535,536) "$a"  
  33.        2. 0x182a884<1> (535,536) "$b"  
  34.     OUTSIDE = 0x299094 (MAIN)  
  35. SV = RV(0x298f50) at 0x298f44  
  36.   REFCNT = 1  
  37.   FLAGS = (TEMP,ROK)  
  38.   RV = 0x182a834  
  39.   SV = PVCV(0x1829024) at 0x182a834  
  40.     REFCNT = 4  
  41.     FLAGS = ()  
  42.     COMP_STASH = 0x298e34   "main"  
  43.     START = 0x18ba94c ===> 0  
  44.     ROOT = 0x18ba4cc  
  45.     GVGV::GV = 0x184b34c    "main" :: "test"  
  46.     FILE = "tmp.pl"  
  47.     DEPTH = 1  
  48.     FLAGS = 0x0  
  49.     OUTSIDE_SEQ = 535  
  50.     PADLIST = 0x182a864  
  51.     PADNAME = 0x182a934(0x18c7c6c) PAD = 0x182a8c4(0x18c7be4)  
  52.        1. 0x182a854<1> (535,536) "$a"  
  53.        2. 0x182a884<1> (535,536) "$b"  
  54.     OUTSIDE = 0x299094 (MAIN)  
  55. SV = RV(0x1899240) at 0x1899234  
  56.   REFCNT = 1  
  57.   FLAGS = (TEMP,ROK)  
  58.   RV = 0x182a834  
  59.   SV = PVCV(0x1829024) at 0x182a834  
  60.     REFCNT = 4  
  61.     FLAGS = ()  
  62.     COMP_STASH = 0x298e34   "main"  
  63.     START = 0x18ba94c ===> 0  
  64.     ROOT = 0x18ba4cc  
  65.     GVGV::GV = 0x184b34c    "main" :: "test"  
  66.     FILE = "tmp.pl"  
  67.     DEPTH = 2  
  68.     FLAGS = 0x0  
  69.     OUTSIDE_SEQ = 535  
  70.     PADLIST = 0x182a864  
  71.     PADNAME = 0x182a934(0x18c7c6c) PAD = 0x182a8c4(0x18c7be4)  
  72.        1. 0x182a854<1> (535,536) "$a"  
  73.        2. 0x182a884<1> (535,536) "$b"  
  74.     OUTSIDE = 0x299094 (MAIN)  
  75. SV = RV(0x1863a28) at 0x1863a1c  
  76.   REFCNT = 1  
  77.   FLAGS = (TEMP,ROK)  
  78.   RV = 0x182a834  
  79.   SV = PVCV(0x1829024) at 0x182a834  
  80.     REFCNT = 4  
  81.     FLAGS = ()  
  82.     COMP_STASH = 0x298e34   "main"  
  83.     START = 0x18ba94c ===> 0  
  84.     ROOT = 0x18ba4cc  
  85.     GVGV::GV = 0x184b34c    "main" :: "test"  
  86.     FILE = "tmp.pl"  
  87.     DEPTH = 3  
  88.     FLAGS = 0x0  
  89.     OUTSIDE_SEQ = 535  
  90.     PADLIST = 0x182a864  
  91.     PADNAME = 0x182a934(0x18c7c6c) PAD = 0x182a8c4(0x18c7be4)  
  92.        1. 0x182a854<1> (535,536) "$a"  
  93.        2. 0x182a884<1> (535,536) "$b"  
  94.     OUTSIDE = 0x299094 (MAIN)  

词法变量(my & our)会设置SVs_PADMY/SVs_PADOUR标记,目标设置SVs_PADTMP标记。


你可能感兴趣的:(Perl 内部结构详解)