PerlGuts Illustrated
直接命令行的方法查看:perl -MDevel::Peek -e "$a = 123; Dump $a"
简单的例子用来查看变量内部结构:
- use?Devel::Peek;??
- ??
- $a??=?123;??
- @a?=?1..10;??
- %a?=?1..10;??
- ??????????
- Dump?$a;??
- Dump?@a ;??
- Dump?%a;??
标量:
- SV?=?IV(0x2410960)?at?0x2410964??
- ??REFCNT?=?1??
- ??FLAGS?=?(IOK,pIOK)??
- ??IV?=?123??
数组:
- SV?=?RV(0x3a7190)?at?0x3a7184??
- ??REFCNT?=?1??
- ??FLAGS?=?(TEMP,ROK)??
- ??RV?=?0x24109c4??
- ??SV?=?PVAV(0x3a80ac)?at?0x24109c4??
- ????REFCNT?=?2??
- ????FLAGS?=?()??
- ????ARRAY?=?0x24c1aec??
- ????FILL?=?9??
- ????MAX?=?9??
- ????ARYLEN?=?0x0??
- ????FLAGS?=?(REAL)??
- ????Elt?No.?0??
- ????SV?=?IV(0x24c0450)?at?0x24c0454??
- ??????REFCNT?=?1??
- ??????FLAGS?=?(IOK,pIOK)??
- ??????IV?=?1??
- ????Elt?No.?1??
- ????SV?=?IV(0x24c0420)?at?0x24c0424??
- ??????REFCNT?=?1??
- ??????FLAGS?=?(IOK,pIOK)??
- ??????IV?=?2??
- ????Elt?No.?2??
- ????SV?=?IV(0x24b7e40)?at?0x24b7e44??
- ??????REFCNT?=?1??
- ??????FLAGS?=?(IOK,pIOK)??
- ??????IV?=?3??
- ????Elt?No.?3??
- ????SV?=?IV(0x24b7e50)?at?0x24b7e54??
- ??????REFCNT?=?1??
- ??????FLAGS?=?(IOK,pIOK)??
- ??????IV?=?4??
Hash:
- SV?=?RV(0xe7190)?at?0xe7184??
- ??REFCNT?=?1??
- ??FLAGS?=?(TEMP,ROK)??
- ??RV?=?0x24aaa0c??
- ??SV?=?PVHV(0x2401c94)?at?0x24aaa0c??
- ????REFCNT?=?2??
- ????FLAGS?=?(SHAREKEYS)??
- ????ARRAY?=?0x24bd47c??(0:3,?1:5)??
- ????hash?quality?=?150.0%??
- ????KEYS?=?5??
- ????FILL?=?5??
- ????MAX?=?7??
- ????RITER?=?-1??
- ????EITER?=?0x0??
- ????Elt?"1"?HASH?=?0x806b80c9??
- ????SV?=?IV(0x24b7ca0)?at?0x24b7ca4??
- ??????REFCNT?=?1??
- ??????FLAGS?=?(IOK,pIOK)??
- ??????IV?=?2??
- ????Elt?"3"?HASH?=?0xa400c7f3??
- ????SV?=?IV(0x24b7c80)?at?0x24b7c84??
- ??????REFCNT?=?1??
- ??????FLAGS?=?(IOK,pIOK)??
- ??????IV?=?4??
- ????Elt?"7"?HASH?=?0xecc9d984??
- ????SV?=?IV(0x24b7c60)?at?0x24b7c64??
- ??????REFCNT?=?1??
- ??????FLAGS?=?(IOK,pIOK)??
- ??????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使用以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 处的字符应该为' ',标记字符串的结束。 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" ??
- ??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" ??
- ??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" ??
- ??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" ??
- ??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" ??
- ??CUR?=?5??
- ??LEN?=?8??
- ??
- C:&;perl?-MDevel::Peek?-e?"$a?=?'123testing';?0+$a;?Dump?$a"??
- SV?=?PVNV(0x298394)?at?0x182aa34??
- ??REFCNT?=?1??
- ??FLAGS?=?(POK,pPOK)??
- ??IV?=?123??
- ??NV?=?123??
- ??PV?=?0x1824674?"123testing" ??
- ??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" ??
- ????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?"%dn",??0x2bb13004?&?4095;??
- printf?"%dn",??0x4b03006?&?4095;??
- printf?"%dn",??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?$bn";??
- ????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,?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,?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,?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,?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,?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,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,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?%Devel::,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)?"$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)?"$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)?"$b"??
- ????OUTSIDE?=?0x299094?(MAIN)??
词法变量(my & our)会设置SVs_PADMY/SVs_PADOUR标记,目标设置SVs_PADTMP标记。
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|