对Lua中Userdata的理解

 

目录

 

一、概述

二、源码实现

相关函数实现

1,lua_pushlightuserdata

2,lua_newuserdata

3,luaC_newobj(lgc.c)

4,createstrobj(lstring.c)

三、总结


一、概述


    Lua中userdata分两类:

       1,是轻量级userdata(light userdata),轻量级userdata是一种表示C指针的值,对Lua虚拟机来说,这种数据类型不需要GC(垃圾回收),其指向的内存由用户分配和释放,其实现就是一个void *p指针。

        实现:lightuserdata通过LUA的API(lua_pushlightuserdata())创建,返回一个指针,自己管理分配和回收。

       2,userdata类型完全userdata(full userdata),内存是由Lua虚拟机分配,并有GC机制负责处理。

       实现:userdata通过Lua的API(lua_newuserdata())分配内存,就像C里的malloc()函数分配内存,但不需要调用free()去释放内存,该内存是由LUA的GC机制进行回收。

 

二、源码实现

       userdata内存存储形式类似于字符串,以一个头开始然后紧随头后面保存相关的数据,但是每个userdata都拥有自己独立的元表。下面是userdata对应的头数据结构Udata代码(lobject.h):

431 /*                                                                                                 
432 ** Header for userdata; memory area follows the end of this structure                              
433 */                                                                                                 
434 typedef union Udata {                                                                              
435   L_Umaxalign dummy;  /* ensures maximum alignment for `local' udata */                            
436   struct {                                                                                         
437     CommonHeader;                                                                                  
438     struct Table *metatable;                                                                       
439     struct Table *env;                                                                             
440     size_t len;  /* number of bytes */                                                             
441   } uv;                                                                                            
442 } Udata;

 

成员dummy和CommonHeader含义与TString完全一样。其他成员含义如下:
    struct Table *metatable:指向userdata对应的元表,也就是一个table。可以调用luaL_newmetatable创建一个元表(注意是在注册表上创建的,也就是说userdata的元表保存在注册表中),然后调用lua_setmetatable来设置userdata的元表,注意Lua代码中不能改变userdata的元表。
    struct Table *env:userdata的环境,创建userdata时该值为NULL。可以通过调用lua_setuservalue函数来设置userdata的环境。
    size_t len:保存userdata内存的大小,就紧随头后面数据内存的大小。
userdata的数据部分和字符串一样,都是紧接着放在结构后面,创建userdata的代码如下(lstring.c):
 

175 Udata *luaS_newudata (lua_State *L, size_t s, Table *e) {                                          
176   Udata *u;
177   if (s > MAX_SIZET - sizeof(Udata))
178     luaM_toobig(L);
179   u = &luaC_newobj(L, LUA_TUSERDATA, sizeof(Udata) + s, NULL, 0)->u;                               
180   u->uv.len = s;
181   u->uv.metatable = NULL;
182   u->uv.env = e;
183   return u;                                                                                        
184 }  

 

相关函数实现

1,lua_pushlightuserdata

LUA_API void lua_pushlightuserdata (lua_State *L, void *p) {
  lua_lock(L);
  setpvalue(L->top, p);
  api_incr_top(L);
  lua_unlock(L);
}

2,lua_newuserdata

LUA_API void *lua_newuserdata (lua_State *L, size_t size) {
  Udata *u;
  lua_lock(L);
  luaC_checkGC(L);
  u = luaS_newudata(L, size, NULL);
  setuvalue(L, L->top, u);
  api_incr_top(L);
  lua_unlock(L);
  return u + 1;
}

3,luaC_newobj(lgc.c)

/*
** create a new collectable object (with given type and size) and link
** it to '*list'. 'offset' tells how many bytes to allocate before the
** object itself (used only by states).
*/
GCObject *luaC_newobj (lua_State *L, int tt, size_t sz, GCObject **list,
                       int offset) {
  global_State *g = G(L);
  char *raw = cast(char *, luaM_newobject(L, novariant(tt), sz));
  GCObject *o = obj2gco(raw + offset);
  if (list == NULL)
    list = &g->allgc;  /* standard list for collectable objects */
  gch(o)->marked = luaC_white(g);
  gch(o)->tt = tt;
  gch(o)->next = *list;
  *list = o;
  return o;
}

4,createstrobj(lstring.c)

/*
** creates a new string object
*/
static TString *createstrobj (lua_State *L, const char *str, size_t l,
                              int tag, unsigned int h, GCObject **list) {
  TString *ts;
  size_t totalsize;  /* total size of TString object */
  totalsize = sizeof(TString) + ((l + 1) * sizeof(char));
  ts = &luaC_newobj(L, tag, totalsize, list, 0)->ts;
  ts->tsv.len = l;
  ts->tsv.hash = h;
  ts->tsv.extra = 0;
  memcpy(ts+1, str, l*sizeof(char));
  ((char *)(ts+1))[l] = '\0';  /* ending 0 */
  return ts;
}

三、总结


    1、lua中的userdata类型主要用来表示在C/C++中定义的类型,即用来实现扩展lua,这些扩展代码通常是用C/C++来实现的。对lua 虚拟机来说userdata只是提供了一块原始的内存区域,可以用来存储任何东西,并且在lua中userdata没有任何预定义的操作。注意这块分配的额外内存是由Lua垃圾收集器来管理的,无须关心起释放等情况。
    2、userdata的元表通常用来存储对应C/C++类型的方法,这样在脚本中可以直接调用userdata对应的方法,并且这些方法也是用C/C++实现的。
    3、创建一个新的userdata时,其环境表默认值是空,即成员值env 为NULL,当前这个成员在Lua没被使用。在一些文章指出,这个成员可以保存与userdata实例相关的数据,而其对应的元表保存userdata的方法。
 

 

 

参考资料:

Lua 5.2.1源码

https://blog.csdn.net/MaximusZhou/article/details/45063619

https://blog.csdn.net/fwb330198372/article/details/82217022?utm_medium=distribute.pc_relevant.none-task-blog-searchFromBaidu-13.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-searchFromBaidu-13.control

你可能感兴趣的:(Lua)