首先看一下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使用以SvNULL为虚拟基类的多重继承,所有的类型都是根据type(small number)来确定并标记,因此你可以获取一个object的数据类型并且执行相应的操作。
下边为类型type 的定义,相应的解释分析见随后的blog:
- typedef enum {
- SVt_NULL, /* 0 */
- SVt_IV, /* 1 */
- SVt_NV, /* 2 */
- SVt_RV, /* 3 */
- SVt_PV, /* 4 */
- SVt_PVIV, /* 5 */
- SVt_PVNV, /* 6 */
- SVt_PVMG, /* 7 */
- SVt_PVBM, /* 8 */
- SVt_PVLV, /* 9 */
- SVt_PVAV, /* 10 */
- SVt_PVHV, /* 11 */
- SVt_PVCV, /* 12 */
- SVt_PVGV, /* 13 */
- SVt_PVFM, /* 14 */
- SVt_PVIO, /* 15 */
- SVt_LAST /* keep last in enum. used to size arrays */
- } svtype;
-
_SV_HEAD and struct sv
下面来看一下最简单的类型struct sv,代表了SV,GV,CV,AV,HV,IO的通用结构。如下图所示:
第一个字段Any可以指向任意结构,除了RV,所有的其他类型都是由Any指向的附加数据来实现。
第二个字段REFCNT表明了有多少pointers引用了这个object。初始置为1,当有pointers指向它或者被销毁的时候,这个值需要相应的加1或者减1,当值为0的时候,内存释放。
第三个字段包含了FLAGS & TYPE,是一个32 bit unsigned int。如下图:
常见的flags见下边具体示例。
- C:\>perl -MDevel::Peek -e "Dump $a"
- SV = NULL(0x0) at 0x182a9fc
- REFCNT = 1
- FLAGS = ()
-
- C:\>perl -MDevel::Peek -e "Dump \$a"
- SV = RV(0x299158) at 0x29914c
- REFCNT = 1
- FLAGS = (TEMP,ROK)
- RV = 0x182a9fc
- SV = NULL(0x0) at 0x182a9fc
- REFCNT = 2
- FLAGS = ()
SvIV and SvNV
整数和小树,结构如图所示:
- C:\>perl -MDevel::Peek -e "$a = 123; Dump $a"
- SV = IV(0x182aa20) at 0x182aa24
- REFCNT = 1
- FLAGS = (IOK,pIOK)
- IV = 123
-
- C:\>perl -MDevel::Peek -e "$a = 123.9; Dump $a"
- SV = NV(0x184882c) at 0x182aa2c
- REFCNT = 1
- FLAGS = (NOK,pNOK)
- NV = 123.9
SvPV
字符串,结构如图所示:
除了SV以外,额外的结构xpv被分配,它包含三部分:
PVX 指向实际的字符串。
CUR 标记字符串的长度,PVX+CUR 处的字符应该为'\0',标记字符串的结束。
LEN 标记内存分配给char * 的长度,以4为增量。
POK标记表示PVX所指向的内存包含有效字符串,否则所包含的字符串无效。
- C:\>perl -MDevel::Peek -e "$a = 't'; Dump $a"
- SV = PV(0x296fec) at 0x182aa24
- REFCNT = 1
- FLAGS = (POK,pPOK)
- PV = 0x182472c "t"\0
- CUR = 1
- LEN = 4
-
- C:\>perl -MDevel::Peek -e "$a = 'test'; Dump $a"
- SV = PV(0x296fec) at 0x182aa2c
- REFCNT = 1
- FLAGS = (POK,pPOK)
- PV = 0x182466c "test"\0
- CUR = 4
- LEN = 8 (随着长度增加,以4为增量)
-
- C:\>perl -MDevel::Peek -e "$a = 'test'; $a = undef; Dump $a"
- SV = PV(0x296ffc) at 0x182aa34
- REFCNT = 1
- FLAGS = () (包含字符串,但是无效)
- PV = 0x1824674 "test"\0
- CUR = 4
- LEN = 8
SvOOK
为了提高移除字符串开头字符的速度,使用了OOK标记,IVX存储着偏移量,结构如图所示:
- C:\>perl -MDevel::Peek -e "$a = 'xtesting'; $a=~s/.//; Dump $a"
- SV = PVIV(0x182005c) at 0x182aa4c
- REFCNT = 1
- FLAGS = (POK,OOK,pPOK)
- IV = 1 (OFFSET)
- PV = 0x182467d ( "x" . ) "testing"\0
- CUR = 7
- LEN = 11
SvPVIV and SvPVNV
类似字符串,但是PVIV储存了额外的整数或者小数信息,可以根据flag直接进行数学运算,结构如图所示:
- C:\>perl -MDevel::Peek -e "$a = '123.0'; 0+$a; Dump $a"
- SV = PVNV(0x29838c) at 0x182aa34
- REFCNT = 1
- FLAGS = (IOK,NOK,POK,pIOK,pNOK,pPOK)
- IV = 123
- NV = 123
- PV = 0x1824674 "123.0"\0
- CUR = 5
- LEN = 8
-
- C:\>perl -MDevel::Peek -e "$a = '123testing'; 0+$a; Dump $a"
- SV = PVNV(0x298394) at 0x182aa34
- REFCNT = 1
- FLAGS = (POK,pIOK,pNOK,pPOK)
- IV = 123
- NV = 123
- PV = 0x1824674 "123testing"\0
- CUR = 10
- LEN = 12
SvRV
类似指针,指向任意其他结构,结构如图所示:
- C:\>perl -MDevel::Peek -e "$a = '123testing'; $b = \$a ; Dump $b"
- SV = RV(0x182ab68) at 0x182ab5c
- REFCNT = 1
- FLAGS = (ROK)
- RV = 0x182aa4c
- SV = PV(0x296ffc) at 0x182aa4c
- REFCNT = 2
- FLAGS = (POK,pPOK)
- PV = 0x182467c "123testing"\0
- CUR = 10
- LEN = 12
-
AV
数组,结构如图所示:
字段ALLOC 指向实际分配的SV 数组的开头地址。
字段ARRAY 指向数组本身的开头地址,由于数组的改变,这个地址比起实际分配的地址,可能会产生一定的偏移。
字段FILL 指向数组本身最后一个位置的偏移。
字段MAX 指向分配的SV 数组最后一个位置的偏移。
- C:\>perl -MDevel::Peek -e "@a = 1..2; Dump \@a"
- SV = RV(0x299120) at 0x299114
- REFCNT = 1
- FLAGS = (TEMP,ROK)
- RV = 0x182a9cc
- SV = PVAV(0x29a03c) at 0x182a9cc
- REFCNT = 2
- FLAGS = ()
- ARRAY = 0x1852984
- FILL = 1 (数组最后一个元素index为1)
- MAX = 3 (分配的数组空间最大index为3,说明还有剩余空间来分配)
- ARYLEN = 0x0
- FLAGS = (REAL)
- Elt No. 0
- SV = IV(0x299220) at 0x299224
- REFCNT = 1
- FLAGS = (IOK,pIOK)
- IV = 1
- Elt No. 1
- SV = IV(0x299230) at 0x299234
- REFCNT = 1
- FLAGS = (IOK,pIOK)
- IV = 2
shift pop 操作可以通过调节ARRAY FILL MAX来完成。
- C:\>perl -MDevel::Peek -e "@a = 0..3; Dump \@a ; shift @a ; pop @a ; Dump \@a"
- SV = RV(0x299130) at 0x299124
- REFCNT = 1
- FLAGS = (TEMP,ROK)
- RV = 0x182aa14
- SV = PVAV(0x29a04c) at 0x182aa14
- REFCNT = 2
- FLAGS = ()
- ARRAY = 0x1824afc
- FILL = 3
- MAX = 3
- ARYLEN = 0x0
- FLAGS = (REAL)
- Elt No. 0
- SV = IV(0x299280) at 0x299284
- REFCNT = 1
- FLAGS = (IOK,pIOK)
- IV = 0
- ...
- SV = RV(0x2991e0) at 0x2991d4
- REFCNT = 1
- FLAGS = (TEMP,ROK)
- RV = 0x182aa14
- SV = PVAV(0x29a04c) at 0x182aa14
- REFCNT = 2
- FLAGS = ()
- ARRAY = 0x1824b00 (offset=1)
- ALLOC = 0x1824afc
- FILL = 1
- MAX = 2
- ARYLEN = 0x0
- FLAGS = (REAL)
- Elt No. 0
- SV = IV(0x299200) at 0x299204
- REFCNT = 1
- FLAGS = (IOK,pIOK)
- IV = 1
- Elt No. 1
- SV = IV(0x182aa40) at 0x182aa44
- REFCNT = 1
- FLAGS = (IOK,pIOK)
- IV = 2
FLAGS REAL 表示所有内部的SV都需要有自己的引用计数,一般的AV 都是使用这个标记。 FLAGS REIFY 表示这个数组不是一个REAL 类型的,当这个数组发生改变的时候需要置为REAL,默认参数@_ 都是使用REIFY 标记。
HV
hash table是最复杂的数据结构,HV使用HE struct表示key/value结构,使用HEK表示key。
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 为空。
- C:\>perl -MDevel::Peek -e "%a=0..5000; Dump \%a"
- SV = RV(0x299120) at 0x299114
- REFCNT = 1
- FLAGS = (TEMP,ROK)
- RV = 0x182a9cc
- SV = PVHV(0x29e7bc) at 0x182a9cc
- REFCNT = 2
- FLAGS = (SHAREKEYS)
- ARRAY = 0x18d12c4 (0:2236, 1:1341, 2:417, 3:85, 4:14, 5:3)
- (ARRAY中有2236个位置未占用,
- 有1341个位置包含1个元素,
- 有417个位置包含2个元素,
- 有85个位置包含3个元素,
- 有14个位置包含4个元素,
- 有3个位置包含5个元素,
- 1341+417+85+14+3=1860,正好是FILL的值
- )
- hash quality = 98.9%
- KEYS = 2501 (hash包含的元素个数)
- FILL = 1860 (hash code占用多少ARRAY位置,说明有多个值占用一个坑)
- MAX = 4095 (ARRAY分配了4096)
- RITER = -1
- EITER = 0x0
- Elt "1648" HASH = 0x2bb13004
- SV = IV(0x185a840) at 0x185a844
- REFCNT = 1
- FLAGS = (IOK,pIOK)
- IV = 1649
- Elt "3596" HASH = 0x4b03006
- SV = IV(0x18a4e38) at 0x18a4e3c
- REFCNT = 1
- FLAGS = (IOK,pIOK)
- IV = 3597
- Elt "3074" HASH = 0x1256006
- SV = IV(0x18beff0) at 0x18beff4
- REFCNT = 1
- FLAGS = (IOK,pIOK)
- IV = 3075
下边用一个例子说明RITER, EITER的变化过程。
取上边3个元素的hash code并与MAX 4095做& 运算,求得前三个元素在数组ARRAY中位置为4 6 6。
- printf "%d\n", 0x2bb13004 & 4095;
- printf "%d\n", 0x4b03006 & 4095;
- printf "%d\n", 0x1256006 & 4095;
-
- output:
- 4
- 6
- 6
依次取hash 值并观察RITER, EITER的变化。
- use Devel::Peek;
-
- %a = 0..5000;
- #~ Dump \%a;
-
- $i = 0;
- while( my($a,$b) =each %a){
- last if $i++ > 4;
- print "$a $b\n";
- Dump \%a;
- }
值输出,同上边相同:
- 1648 1649
- 3596 3597
- 3074 3075
- 2266 2267
- 1178 1179
RITER EITER在循环过程中ARRAY位置4 6 6与预计相同。
- SV = RV(0x298f98) at 0x298f8c
- REFCNT = 1
- FLAGS = (TEMP,ROK)
- RV = 0x182a8cc
- SV = PVHV(0x29e59c) at 0x182a8cc
- REFCNT = 2
- FLAGS = (OOK,SHAREKEYS)
- ARRAY = 0x18d1164 (0:2236, 1:1341, 2:417, 3:85, 4:14, 5:3)
- hash quality = 98.9%
- KEYS = 2501
- FILL = 1860
- MAX = 4095
- RITER = 4
- EITER = 0x18a3c50
- SV = RV(0x298f98) at 0x298f8c
- REFCNT = 1
- FLAGS = (TEMP,ROK)
- RV = 0x182a8cc
- SV = PVHV(0x29e59c) at 0x182a8cc
- REFCNT = 2
- FLAGS = (OOK,SHAREKEYS)
- ARRAY = 0x18d1164 (0:2236, 1:1341, 2:417, 3:85, 4:14, 5:3)
- hash quality = 98.9%
- KEYS = 2501
- FILL = 1860
- MAX = 4095
- RITER = 6
- EITER = 0x18c3f48
- SV = RV(0x298f98) at 0x298f8c
- REFCNT = 1
- FLAGS = (TEMP,ROK)
- RV = 0x182a8cc
- SV = PVHV(0x29e59c) at 0x182a8cc
- REFCNT = 2
- FLAGS = (OOK,SHAREKEYS)
- ARRAY = 0x18d1164 (0:2236, 1:1341, 2:417, 3:85, 4:14, 5:3)
- hash quality = 98.9%
- KEYS = 2501
- FILL = 1860
- MAX = 4095
- RITER = 6
- EITER = 0x18ba91c
- SV = RV(0x298f98) at 0x298f8c
- REFCNT = 1
- FLAGS = (TEMP,ROK)
- RV = 0x182a8cc
- SV = PVHV(0x29e59c) at 0x182a8cc
- REFCNT = 2
- FLAGS = (OOK,SHAREKEYS)
- ARRAY = 0x18d1164 (0:2236, 1:1341, 2:417, 3:85, 4:14, 5:3)
- hash quality = 98.9%
- KEYS = 2501
- FILL = 1860
- MAX = 4095
- RITER = 9
- EITER = 0x18b1c4c
- SV = RV(0x298f98) at 0x298f8c
- REFCNT = 1
- FLAGS = (TEMP,ROK)
- RV = 0x182a8cc
- SV = PVHV(0x29e59c) at 0x182a8cc
- REFCNT = 2
- FLAGS = (OOK,SHAREKEYS)
- ARRAY = 0x18d1164 (0:2236, 1:1341, 2:417, 3:85, 4:14, 5:3)
- hash quality = 98.9%
- KEYS = 2501
- FILL = 1860
- MAX = 4095
- RITER = 11
- EITER = 0x1899dcc
-
GV
GV "Global Value" 或者“符号表”,存储着变量或者函数的指针GP,由GP中slot 相关的指示来决定是否存在对应这个变量相应的类型。如图所示:
GP可以在多个GV中共享,试运行下例查看结果。
- D:\perl -MDevel::Peek -e "*test=*Dump; Dump *Dump; Dump *test"
- SV = PVGV(0x285cda4) at 0x286c0dc
- REFCNT = 5
- FLAGS = (PADTMP,MULTI,ASSUMECV,IN_PAD,IMPORT( CV ))
- NAME = "Dump"
- NAMELEN = 4
- GvSTASH = 0x3a8fd4 "main"
- GP = 0x2864ffc
- SV = 0x0
- REFCNT = 2
- IO = 0x0
- FORM = 0x0
- AV = 0x0
- HV = 0x0
- CV = 0x2883874
- CVGEN = 0x0
- LINE = 67
- FILE = "D:/Perl/lib/Exporter.pm"
- FLAGS = 0x8e
- EGV = 0x286c0dc "Dump"
- SV = PVGV(0x285cf04) at 0x285a85c
- REFCNT = 3
- FLAGS = (MULTI,IN_PAD)
- NAME = "test"
- NAMELEN = 4
- GvSTASH = 0x3a8fd4 "main"
- GP = 0x2864ffc
- SV = 0x0
- REFCNT = 2
- IO = 0x0
- FORM = 0x0
- AV = 0x0
- HV = 0x0
- CV = 0x2883874
- CVGEN = 0x0
- LINE = 67
- FILE = "D:/Perl/lib/Exporter.pm"
- FLAGS = 0xa
- EGV = 0x286c0dc "Dump"
REFCNT 引用数量,上边例子可以观察到。
EGV (effective gv) 表示创建这个GP的GV地址。
LINE 文件中行数。
FILE_HEK 在哪个文件中创建这个GV。
对应变量的GV可以用*var 来表示,字段GvSTASH 表示字段所在的命名空间,默认有一个main 命名空间。所有存储于GV中的类型都是全局的。
- D:\perl -MDevel::Peek -e "$a = 123; Dump *a"
- SV = PVGV(0x285ced4) at 0x285a82c
- REFCNT = 3
- FLAGS = (MULTI,IN_PAD)
- NAME = "a"
- NAMELEN = 1
- GvSTASH = 0x3a8f9c "main"
- GP = 0x286438c
- SV = 0x285a83c
- REFCNT = 1
- IO = 0x0
- FORM = 0x0
- AV = 0x0
- HV = 0x0
- CV = 0x0
- CVGEN = 0x0
- LINE = 1
- FILE = "-e"
- FLAGS = 0xa
- EGV = 0x285a82c "a"
如果是从其它模块中引入的,则是通过Export引入main命名空间,实际指向的还是原始模块中的GV,看以下两个例子,函数Dump CV相同。
- D:\Tmp>perl -MDevel::Peek -e "Dump *Devel::Peek::Dump"
- SV = PVGV(0x285c894) at 0x286c714
- REFCNT = 3
- FLAGS = (MULTI,IN_PAD)
- NAME = "Dump"
- NAMELEN = 4
- GvSTASH = 0x285aa0c "Devel::Peek"
- GP = 0x2874484
- SV = 0x0
- REFCNT = 1
- IO = 0x0
- FORM = 0x0
- AV = 0x0
- HV = 0x0
- CV = 0x2883854
- CVGEN = 0x0
- LINE = 44
- FILE = "D:/Perl/lib/Devel/Peek.pm"
- FLAGS = 0xa
- EGV = 0x286c714 "Dump"
-
- D:\Tmp>perl -MDevel::Peek -e "Dump *Dump"
- SV = PVGV(0x285cd8c) at 0x286c0f4
- REFCNT = 3
- FLAGS = (PADTMP,MULTI,ASSUMECV,IN_PAD,IMPORT( CV ))
- NAME = "Dump"
- NAMELEN = 4
- GvSTASH = 0x3a8fc4 "main"
- GP = 0x2864fe4
- SV = 0x0
- REFCNT = 1
- IO = 0x0
- FORM = 0x0
- AV = 0x0
- HV = 0x0
- CV = 0x2883854
- CVGEN = 0x0
- LINE = 67
- FILE = "D:/Perl/lib/Exporter.pm"
- FLAGS = 0x8e
- EGV = 0x286c0f4 "Dump"
Stashes
GVs 同Stashes 共同作用实现了Perl 的命名空间,Stash 实际是HV,所有内容都指向GV。命名空间root 是defstash,指向main Stash。一个多层次模块,每一层都有一个相应的Stash。如下所示整体命名空间结构:
示例察看main 空间的内容,*:: 等同于*main::
- D:\Tmp>perl -MDevel::Peek -e "Dump *::"
- SV = PVGV(0x2851fdc) at 0x3a8fe4
- REFCNT = 2
- FLAGS = (READONLY,MULTI,IN_PAD)
- NAME = "main::"
- NAMELEN = 6
- GvSTASH = 0x3a8fc4 "main"
- GP = 0x2852fd4
- SV = 0x0
- REFCNT = 1
- IO = 0x0
- FORM = 0x0
- AV = 0x0
- HV = 0x3a8fc4
- CV = 0x0
- CVGEN = 0x0
- LINE = 0
- FILE = ""
- FLAGS = 0xa
- EGV = 0x3a8fe4 "main::"
查看main 中HV 包含的内容,节选了部分来演示:
- D:\Tmp>perl -MDevel::Peek -e "Dump \%::,1000"
- SV = RV(0x3a90e0) at 0x3a90d4
- REFCNT = 1
- FLAGS = (TEMP,ROK)
- RV = 0x3a8fc4
- SV = PVHV(0x3ae2dc) at 0x3a8fc4
- REFCNT = 3
- FLAGS = (OOK,SHAREKEYS)
- ARRAY = 0x287b234 (0:74, 1:44, 2:8, 3:2)
- hash quality = 105.9%
- KEYS = 66
- FILL = 54
- MAX = 127
- RITER = -1
- EITER = 0x0
- NAME = "main"
- BACKREFS = 0x3a8ff4
-
- ......
-
- Elt "Devel::" HASH = 0xfa558e0e
- SV = PVGV(0x285c314) at 0x285a9cc
- REFCNT = 1
- FLAGS = (MULTI)
- NAME = "Devel::"
- NAMELEN = 7
- GvSTASH = 0x3a8fc4 "main"
- GP = 0x28654ac
- SV = 0x0
- REFCNT = 1
- IO = 0x0
- FORM = 0x0
- AV = 0x0
- HV = 0x285a9dc
- CV = 0x0
- CVGEN = 0x0
- LINE = 4
- FILE = "D:/Perl/lib/Devel/Peek.pm"
- FLAGS = 0x2
- EGV = 0x285a9cc "Devel::"
- ......
- Elt "Dump" HASH = 0xbbea0f76
- SV = PVGV(0x285cd94) at 0x286c0f4
- REFCNT = 2
- FLAGS = (PADTMP,MULTI,ASSUMECV,IN_PAD,IMPORT( CV ))
- NAME = "Dump"
- NAMELEN = 4
- GvSTASH = 0x3a8fc4 "main"
- GP = 0x2864fe4
- SV = 0x0
- REFCNT = 1
- IO = 0x0
- FORM = 0x0
- AV = 0x0
- HV = 0x0
- CV = 0x2883854
- CVGEN = 0x0
- LINE = 67
- FILE = "D:/Perl/lib/Exporter.pm"
- FLAGS = 0x8e
- EGV = 0x286c0f4 "Dump"
- ......
- Elt "main::" HASH = 0x40383f65
- SV = PVGV(0x2851fdc) at 0x3a8fe4
- REFCNT = 2
- FLAGS = (READONLY,MULTI,IN_PAD)
- NAME = "main::"
- NAMELEN = 6
- GvSTASH = 0x3a8fc4 "main"
- GP = 0x2852fd4
- SV = 0x0
- REFCNT = 1
- IO = 0x0
- FORM = 0x0
- AV = 0x0
- HV = 0x3a8fc4
- CV = 0x0
- CVGEN = 0x0
- LINE = 0
- FILE = ""
- FLAGS = 0xa
- EGV = 0x3a8fe4 "main::"
- Elt "ENV" HASH = 0x62ffe0d6
- SV = PVGV(0x285c2b4) at 0x285a55c
- REFCNT = 1
- FLAGS = (MULTI)
- NAME = "ENV"
- NAMELEN = 3
- GvSTASH = 0x3a8fc4 "main"
- GP = 0x285e184
- SV = 0x0
- REFCNT = 1
- IO = 0x0
- FORM = 0x0
- AV = 0x0
- HV = 0x285a56c
- CV = 0x0
- CVGEN = 0x0
- LINE = 0
- FILE = "-e"
- FLAGS = 0x2
- EGV = 0x285a55c "ENV"
查看多层次模块中的内容:
- D:\Tmp>perl -MDevel::Peek -e "Dump \%Devel::,1000"
- SV = RV(0x3a90e0) at 0x3a90d4
- REFCNT = 1
- FLAGS = (TEMP,ROK)
- RV = 0x285a9dc
- SV = PVHV(0x3ae57c) at 0x285a9dc
- REFCNT = 2
- FLAGS = (OOK,SHAREKEYS)
- ARRAY = 0x2865534 (0:7, 1:1)
- hash quality = 100.0%
- KEYS = 1
- FILL = 1
- MAX = 7
- RITER = -1
- EITER = 0x0
- NAME = "Devel"
- BACKREFS = 0x285a9fc
- SV = PVAV(0x3aa32c) at 0x285a9fc
- REFCNT = 2
- FLAGS = ()
- ARRAY = 0x286561c
- FILL = 0
- MAX = 3
- ARYLEN = 0x0
- FLAGS = ()
- Elt No. 0
- SV = PVGV(0x285c334) at 0x285a9ec
- REFCNT = 1
- FLAGS = (MULTI)
- NAME = "Peek::"
- NAMELEN = 6
- GvSTASH = 0x285a9dc "Devel"
- GP = 0x28655dc
- SV = 0x0
- REFCNT = 1
- IO = 0x0
- FORM = 0x0
- AV = 0x0
- HV = 0x285aa0c
- CV = 0x0
- CVGEN = 0x0
- LINE = 4
- FILE = "D:/Perl/lib/Devel/Peek.pm"
- FLAGS = 0x2
- EGV = 0x285a9ec "Peek::"
- Elt "Peek::" HASH = 0xf40ca9c
- SV = PVGV(0x285c334) at 0x285a9ec
- REFCNT = 1
- FLAGS = (MULTI)
- NAME = "Peek::"
- NAMELEN = 6
- GvSTASH = 0x285a9dc "Devel"
- GP = 0x28655dc
- SV = 0x0
- REFCNT = 1
- IO = 0x0
- FORM = 0x0
- AV = 0x0
- HV = 0x285aa0c
- CV = 0x0
- CVGEN = 0x0
- LINE = 4
- FILE = "D:/Perl/lib/Devel/Peek.pm"
- FLAGS = 0x2
- EGV = 0x285a9ec "Peek::"
希望通过这一节都能对Perl的整体有一个了解,相信其他语言也有类似的概念结构来实现命名空间的引入。
CV
code value,函数的结构,如下图:
下边示例可以看到几个主要参数:
函数所在的Stash & GV。
函数的参数表和临时变量表PADLIST。
- D:\Tmp>perl -MDevel::Peek -e "sub a{my ($a,$b,$t); } Dump \&a"
- SV = RV(0x3a90f0) at 0x3a90e4
- REFCNT = 1
- FLAGS = (TEMP,ROK)
- RV = 0x285a864
- SV = PVCV(0x285923c) at 0x285a864
- REFCNT = 2
- FLAGS = ()
- COMP_STASH = 0x3a8fd4 "main"
- START = 0x2885fa8 ===> 0
- ROOT = 0x2885f64
- GVGV::GV = 0x285abd4 "main" :: "a"
- FILE = "-e"
- DEPTH = 0
- FLAGS = 0x0
- OUTSIDE_SEQ = 96
- PADLIST = 0x285a874
- PADNAME = 0x285a8a4(0x2865424) PAD = 0x285a974(0x288292c)
- 1. 0x285a8c4<1> (96,97) "$a"
- 2. 0x285a894<1> (96,97) "$b"
- 3. 0x285a904<1> (96,97) "$t"
- OUTSIDE = 0x3a9274 (MAIN)
PAD
PAD是一个列表,为每个函数存储局部变量。第0个slot 存储参数名字,运行时产生新的AV存储实际参数,DEPTH也加深,递归函数观察效果明显。
- use Devel::Peek;
- use Data::Dumper;
-
- sub test{
- my ($a,$b) = @_;
- Dump \&test;
- test(++$a,--$b) if $b>0;
- }
-
- Dump \&test;
- test(1,2);
-
-
- 输出:
- SV = RV(0x298f50) at 0x298f44
- REFCNT = 1
- FLAGS = (TEMP,ROK)
- RV = 0x182a834
- SV = PVCV(0x1829024) at 0x182a834
- REFCNT = 2
- FLAGS = ()
- COMP_STASH = 0x298e34 "main"
- START = 0x18ba94c ===> 0
- ROOT = 0x18ba4cc
- GVGV::GV = 0x184b34c "main" :: "test"
- FILE = "tmp.pl"
- DEPTH = 0
- FLAGS = 0x0
- OUTSIDE_SEQ = 535
- PADLIST = 0x182a864
- PADNAME = 0x182a934(0x18c7c6c) PAD = 0x182a8c4(0x18c7be4)
- 1. 0x182a854<1> (535,536) "$a"
- 2. 0x182a884<1> (535,536) "$b"
- OUTSIDE = 0x299094 (MAIN)
- SV = RV(0x298f50) at 0x298f44
- REFCNT = 1
- FLAGS = (TEMP,ROK)
- RV = 0x182a834
- SV = PVCV(0x1829024) at 0x182a834
- REFCNT = 4
- FLAGS = ()
- COMP_STASH = 0x298e34 "main"
- START = 0x18ba94c ===> 0
- ROOT = 0x18ba4cc
- GVGV::GV = 0x184b34c "main" :: "test"
- FILE = "tmp.pl"
- DEPTH = 1
- FLAGS = 0x0
- OUTSIDE_SEQ = 535
- PADLIST = 0x182a864
- PADNAME = 0x182a934(0x18c7c6c) PAD = 0x182a8c4(0x18c7be4)
- 1. 0x182a854<1> (535,536) "$a"
- 2. 0x182a884<1> (535,536) "$b"
- OUTSIDE = 0x299094 (MAIN)
- SV = RV(0x1899240) at 0x1899234
- REFCNT = 1
- FLAGS = (TEMP,ROK)
- RV = 0x182a834
- SV = PVCV(0x1829024) at 0x182a834
- REFCNT = 4
- FLAGS = ()
- COMP_STASH = 0x298e34 "main"
- START = 0x18ba94c ===> 0
- ROOT = 0x18ba4cc
- GVGV::GV = 0x184b34c "main" :: "test"
- FILE = "tmp.pl"
- DEPTH = 2
- FLAGS = 0x0
- OUTSIDE_SEQ = 535
- PADLIST = 0x182a864
- PADNAME = 0x182a934(0x18c7c6c) PAD = 0x182a8c4(0x18c7be4)
- 1. 0x182a854<1> (535,536) "$a"
- 2. 0x182a884<1> (535,536) "$b"
- OUTSIDE = 0x299094 (MAIN)
- SV = RV(0x1863a28) at 0x1863a1c
- REFCNT = 1
- FLAGS = (TEMP,ROK)
- RV = 0x182a834
- SV = PVCV(0x1829024) at 0x182a834
- REFCNT = 4
- FLAGS = ()
- COMP_STASH = 0x298e34 "main"
- START = 0x18ba94c ===> 0
- ROOT = 0x18ba4cc
- GVGV::GV = 0x184b34c "main" :: "test"
- FILE = "tmp.pl"
- DEPTH = 3
- FLAGS = 0x0
- OUTSIDE_SEQ = 535
- PADLIST = 0x182a864
- PADNAME = 0x182a934(0x18c7c6c) PAD = 0x182a8c4(0x18c7be4)
- 1. 0x182a854<1> (535,536) "$a"
- 2. 0x182a884<1> (535,536) "$b"
- OUTSIDE = 0x299094 (MAIN)