反射(Re fl ection)
反射是程序用来检查和修改其自身某些部分的能力
调试库由两类函数组成:自省函数(introspective function)和钩子(hook)。自省函数允许我们检查一个正在运行中的程序的几个方面,例如活动函数的栈、当前正在执行的代码行、局部变量的名称和值。钩子则允许我们跟踪一个程序的执行。
虽然名字里带有“调试”的字眼,但调试库提供的并不是Lua语言的调试器(debugger)。不过,调试库提供了编写我们自己的调试器所需要的不同层次的所有底层机制。
自省机制(Introspective Facility)
调试库中主要的自省函数是getinfo,该函数的第一个参数可以是一个函数或一个栈层次。当为某个函数foo调用debug.getinfo(foo)时,该函数会返回一个包含与该函数有关的一些数据的表。这个表可能具有以下字段。
钩子(Hook)
调试库中的钩子机制允许用户注册一个钩子函数,这个钩子函数会在程序运行中某个特定事件发生时被调用。
有四种事件能够触发一个钩子:
•每当调用一个函数时产生的call事件;
•每当函数返回时产生的return事件;
•每当开始执行一行新代码时产生的line事件;
•执行完指定数量的指令后产生的count事件。(这里的指令指的是内部操作码,在16.2节中对其有简单的描述。)
调优(Profile)
除了调试,反射的另外一个常见用法是用于调优,即程序使用资源的行为分析
沙盒(Sandbox)
利用函数load在受限的环境中运行Lua代码是非常简单的。由于Lua语言通过库函数完成所有与外部世界的通信,因此一旦移除了这些函数也就排除了一个脚本能够影响外部环境的可能。不过尽管如此,我们仍然可能会被消耗大量CPU时间或内存的脚本进行拒绝服务(DoS)攻击。反射,以调试钩子的形式,提供了一种避免这种攻击的有趣方式。
首先,我们使用count事件钩子来限制一段代码能够执行的指令数。示例展示了一个在沙盒中运行指定文件的程序。
这个程序加载了指定的文件,设置了钩子,然后运行文件。该程序把钩子设置为监听count事件,使得Lua语言每执行100条指令就调用一次钩子函数。钩子(函数step)只是递增一个计数器,然后检查其是否超过了某个固定的限制
我们还必须限制所加载的代码段的大小:一段很长的代码只要被加载就可能耗尽内存
使用协程实现多线程
协程能够实现一种协作式多线程(collaborative multithreading)。每个协程都等价于一个线程。一对yield–resume可以将执行权在不同线程之间切换。不过,与普通的多线程的不同,协程是非抢占的。当一个协程正在运行时,是无法从外部停止它的。只有当协程显式地要求时(通过调用函数yield)它才会挂起执行。对于有些应用而言,这并没有问题,而对于另外一些应用则不行。当不存在抢占时,编程简单得多。由于在程序中所有的线程间同步都是显式的,所以我们无须为线程同步问题抓狂,只需要确保一个协程只在它的临界区(critical region)之外调用yield即可。
不过,对于非抢占式多线程来说,只要有一个线程调用了阻塞操作,整个程序在该操作完成前都会阻塞。对于很多应用程序来说,这种行为是无法接受的,