Lua_第28章 资源管理(上)

Lua_第28章  资源管理 (上) 

在前面一章介绍的数组实现方法,我们不必担心如何管理资源,只需要分配内存。 每一个表示数组的 userdatum 都有自己的内存,这个内存由 Lua 管理。当数组变为垃圾 (也就是说,当程序不需要)的时候,Lua 会自动收集并释放内存。

生活总是不那么如意。有时候,一个对象除了需要物理内存以外,还需要文件描述符、窗口句柄等类似的资源。(通常这些资源也是内存,但由系统的其他部分来管理)。 在这种情况下,当一个对象成为垃圾并被收集的时候,这些相关的资源也应该被释放。一些面向对象的语言为了这种需要提供了一种特殊的机制(称为 finalizer   或者析构器)。Lua以_gc 元方法的方式提供了 finalizers。这个元方法只对
userdata 类型的值有效
。当一个userdatum 将被收集的时候,并且usedatum 有一个_gc 域,Lua 会调用这个域的值 (应该是一个函数):以 userdatum  作为这个函数的参数调用。这个函数负责释放与 userdatum 相关的所有资源。

为了阐明如何将这个元方法和 API作为一个整体使用,这一章我们将使用 Lua扩展应用的方式,介绍两个例子。第一个例子是前面己经介绍的遍历一个目录的函数的另一种实现。第二个例子是一个绑定 ExpatCExpat 开源的 XML 解析器)实现的 XML 解析 器。

29.1 目录迭代器

前面我们实现了一个 dir 函数,给定一个目录作为参数,这个函数以一个table的方 式返回目录下所有文件。我们新版本的dir函数将返回一个迭代子,每次调用这个迭代 子的时候会返回目录中的一个入口(entry)。按新版本的实现方式,我们可以使用循环 来遍历整个目录:

for fname in dir(".") do print(fname)  end

在 C 语言中,我们需要 DIR这种结构才能够迭代一个目录。通过 opendir 才能创建 一个 DIR 的实例,并且必须显式的调用 closedir 来释放资源。我们以前实现的 dir 用一个 本地变量保存 DIR 的实例,并且在获取目录中最后一个文件名之后关闭实例。但我们新实现的 dir中不能在本地变量中保存 DIR 的实例,因为有很多个调用都要访问这个值,另外,也不能仅仅在获取目录中最后一个文件名之后关闭目录。如果程序循环过程中中断退出,迭代子根本就不会取得最后一个文件名,所以,为了保证
DIR 的实例一定能够被释放掉,我们将它的地址保存在一个 userdatum 中,并使用这个 userdatum 的 gc的 元方法来释放目录结构。

尽管我们实现中userdatum的作用很重要,但这个用来表示一个目录的userdatum,并不需要在Lua可见范围之内。Dir函数返回一个迭代子函数,迭代子函数需要在Lua的可见 范围之内。目录可能是迭代子函数的一个upvalue。这样一来,迭代子函数就可以直接访问这个结构(指目录结构,即userdatum),但是Lua不可以(也不需要)访问这个结构。

总的来说,我们需要三个 C 函数第一,dir 函数,一个 Lua 调用他产生迭代器的 工厂,这个函数必须打开 DIR结构并将他作为迭代函数的 upvalue。第二,我们需要一 个迭代函数。第三,_gc 元方法,负责关闭 DIR 结构。一般来说,我们还需要一个额外的函数来进行一些初始的操作,比如为目录创建
metatable,并初始化这个 metatable。

首先看我们的 dir 函数:

#include <dirent.h>
#include <errno.h>

/* forward declaration for the iteratorfunction */
static int dir_iter (lua_State *L);

static int l_dir (lua_State *L) {
   const char *path = luaL_checkstring(L, 1);

     /* create auserdatum to storea DIR address */
    DIR **d = (DIR**)lua_newuserdata(L, sizeof(DIR *));

     /* set its metatable */
     luaL_getmetatable(L, "LuaBook.dir");
     ua_setmetatable(L, -2);

      /* try toopen the givendirectory */
      *d =opendir(path);
       if (*d == NULL)/* error openingthe directory? */
       luaL_error(L, "cannot open %s: %s", path, strerror(errno));

         /* createsand returns theiterator function (its sole upvalue,the directory userdatum, is already on the stacktop */
         lua_pushcclosure(L, dir_iter, 1);
         return 1;
}

这儿有一点需要注意的,我们必须在打开目录之前创建 userdatum。如果我们先打开 目录,然后调用 lua_newuserdata 会抛出错误,这样我们就无法获取DIR 结构。按照正确 的顺序,DIR 结构一旦被创建,就会立刻和 userdatum  关联起来;之后不管发生什么,_gc元方法都会自动的释放这个结构。

第二个函数是迭代器:

static int dir_iter (lua_State *L) {
    DIR *d = *(DIR**)lua_touserdata(L, lua_upvalueindex(1));
     struct dirent *entry;
     if ((entry = readdir(d)) != NULL) {
      lua_pushstring(L, entry->d_name);
      return 1;
}
else return 0; /* no morevalues to return*/
}

   gc 元方法用来关闭目录,但有一点需要小心:因为我们在打开目录之前创建 userdatum,所以不管 opendir 的结果是什么userdatum 将来都会被收集。如果 opendir 失败,将来就没有什么可以关闭的了:

static int dir_gc (lua_State *L) {
   DIR *d = *(DIR**)lua_touserdata(L, 1);
   if (d) closedir(d);
   return 0;
}

最后一个函数打开这个只有一个函数的库:

int luaopen_dir (lua_State *L) {
    luaL_newmetatable(L, "LuaBook.dir");
   /* set its  gcfield */
   lua_pushstring(L, "_gc");
   lua_pushcfunction(L, dir_gc);
   lua_settable(L,-3);

   /* register the  dirfunction */
   lua_pushcfunction(L, l_dir);
   lua_setglobal(L, "dir");

return 0;
}

整个例子有一个注意点。开始的时候,dir_gc 看起来应该检查他的参数是否是一个目录。否则,一个恶意的使用者可能用其他类型的参数(比如,文件)调用这个函数导 致严重的后果。然而,在 Lua 程序中无法访问这个函数:他被存放在目录的 metatable 中,Lua
程序从来不会访问这些目录。

时间: 2024-11-08 19:03:26

Lua_第28章 资源管理(上)的相关文章

Lua_第28章 资源管理(下)

29.2 XML 解析 现在,我们将要看到一个xml解析器的简单实现,称为lxp(估计是lua xml parser的简写) ,它包括了Lua和Expat.Expat是一个开源的C语言写成的XML  1.0 的解析器.它实现 了SAXC,SAX是XML简单的API,是基于事件的API,这意 味着一个SAX解析器读取有一个XML文档,然后反馈给应用程序他所发现的.举个例子, 我们要通知Expat解析这样一个字符串: <tag cap="5">hi</tag> 它将

Lua_第19章 String 库(上)

Lua_第19章String 库 Lua解释器对字符串的支持很有限.一个程序可以创建字符串并连接字符串,但不能截取子串,检查字符串的大小,检测字符串的内容.在 Lua中操纵字符串的功能基本来自于 string 库. String 库中的一些函数是非常简单的:string.len(s)返回字符串 s 的长度;string.rep(s, n)返回重复 n 次字符串 s 的串;你使用 string.rep("a", 2^20)可以创建一个 1M bytes 的字符 串(比如,为了测试需要);

Lua_第27章 User-Defined Types in C

Lua_第27章  User-Defined Types in C 在上一章,我们讨论了如何使用 C 函数扩展 Lua 的功能,现在我们讨论如何使用 C 中新创建的类型来扩展 Lua.我们从一个小例子开始,本章后续部分将以这个小例子 为基础逐步加入 metamethods 等其他内容来介绍如何使用 C 中新类型扩展 Lua. 我们的例子涉及的类型非常简单,数字数组.这个例子的目的在于将目光集中到 API 问题上,所以不涉及复杂的算法.尽管例子中的类型很简单,但很多应用中都会用到这 种类型.一般情

Lua_第19章 String 库(下)

Lua_第19章 String 库(下) 19.3捕获(Captures) Capture(下面译为捕获或者capture,模式中捕获的概念指,使用临时变量来保存匹配的子模式,常用于 向前引用.)是这样一种机制:可以使用模式串的一部分匹配目标串的一部分.将你想捕 获的模式用圆括号括起来,就指定了一个capture.在 string.find 使用captures 的时候,函数会返回捕获的值作为额外的结果.这常被用 来将一个目标串拆分成多个: pair = "name =Anna" _,

Lua_第16 章 Weak 表

Lua_第16 章 Weak 表 Lua自动进行内存的管理.程序只能创建对象(表,函数等),而没有执行删除对象 的函数.通过使用垃圾收集技术,Lua 会自动删除那些失效的对象.这可以使你从内存 管理的负担中解脱出来.更重要的,可以让你从那些由此引发的大部分 BUG中解脱出 来,比如指针挂起(dangling   pointers)和内存溢出. 和其他的不同,Lua 的垃圾收集器不存在循环的问题.在使用循环性的数据结构的 时候,你无须加入特殊的操作;他们会像其他数据一样被收集.当然,有些时候即使更

第28章 CSS3多列布局

第 28章 CSS3多列布局学习要点:1.早期多列问题2.属性及版本3.属性解释 本章主要探讨 HTML5中 CSS3提供的多列布局,通过多列布局我们方便的创建流体的多列布局.一.早期多列问题我们有时想布局成报纸.杂志那样的多列方式(至少两列,一般三列以上),但早期CSS提供的布局方式都有着极大的限制.如果是固体布局,那么使用浮动或定位布局都可以完成.但对于流体的多列,比如三列以上,那只能使用浮动布局进行,而它又有极大的限制.因为它是区块性的,对于动态的内容无法伸缩自适应,基本无能力.//五段内

Lua_第25章 调用 C 函数

第25章 调用 C 函数  扩展 Lua 的基本方法之一就是为应用程序注册新的 C 函数到 Lua中去. 当我们提到 Lua 可以调用 C 函数,不是指 Lua 可以调用任何类型的 C 函数(有一些包可以让 Lua 调用任意的 C 函数,但缺乏便捷和健壮性).正如我们前面所看到的,当C 调用 Lua函数的时候,必须遵循一些简单的协议来传递参数和获取返回结果.相似的, 从Lua 中调用 C 函数,也必须遵循一些协议来传递参数和获得返回结果.另外,从 Lua 调用 C 函数我们必须注册函数,也就是说

第04章(面向对象——上)

1 /***************** 2 ***第四章面向对象(上) 3 *******知识点: 4 **************1.类和对象 5 ******************1.1 关键字 6 ******************1.2 定义 7 ******************1.3 使用 8 **************2.方法和属性 9 ******************2.1 关键字 10 ******************2.2 方法重载和重写 11 ******

《白帽子讲WEB安全》学习笔记之第8章 文件上传漏洞

第8章 文件上传漏洞 8.1 文件上传漏洞概述 文件上传漏洞是指用户上传一个可执行的脚本文件,并通过此脚本文件活动执行服务器端的能力. 原理:由于文件上传功能实现代码没有严格限制用户上传的文件后缀以及文件类型,导致允许攻击者向某个可通过 Web 访问的目录上传任意PHP文件. 文件上传漏洞安全问题: q  上传文件是WEB脚本文件,服务器的WEB服务器解释并执行了用户上传的脚本,导致代码执行: q  上传文件是Flash的策略文件crossdomain.xml,黑客用以控制Flash在该域下的行