-->


前段时间,在给我们游戏服务器写 lua 的脚本的时候,发现了一个奇怪的现象,一段 lua 代码只要一执行就把服务器给搞挂了,仔细分析了一下,发现这段 lua 代码并没有执行什么特别的操作,甚至都没有跟我们服务器的 C++ 层交互,仅仅只是使用 lua 自身的一些库函数,而且只对 windows 平台下的服务端会产生这个崩溃。初步认为是 windows 平台的原因。于是我在 windows 平台下编译了 lua 的源码,跟进去后发现原来是宕在了 windows 的 CRT 函数里,解释一下 CRT 是 windows 的 C 运行时库,如果有朋友不清楚,可以看看 《程序员自我修养》 这本书中关于运行时库的章节。


接着说我的问题,宕在了一次调用 lua 内置的 os 库的 date() 函数,调用如下:

os.date("%C")

跟进lua源码后发现,源码里最后调用了 C 库的 strftime 函数:

![1638516399324](/D:/Gridea/home/post-images/1638516399324.jpg)![1638516399324](/D:/Gridea/home/post-images/1638516399324.jpg)![1638516399324](/D:/Gridea/home/post-images/1638516399324.jpg)static int os_date (lua_State *L) {
  const char *s = luaL_optstring(L, 1, "%c");
  time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL));
  struct tm *stm;
  if (*s == '!') {  /* UTC? */
    stm = gmtime(&t);
    s++;  /* skip `!' */
  }
  else
    stm = localtime(&t);
  if (stm == NULL)  /* invalid date? */
    lua_pushnil(L);
  else if (strcmp(s, "*t") == 0) {
    lua_createtable(L, 0, 9);  /* 9 = number of fields */
    setfield(L, "sec", stm->tm_sec);
    setfield(L, "min", stm->tm_min);
    setfield(L, "hour", stm->tm_hour);
    setfield(L, "day", stm->tm_mday);
    setfield(L, "month", stm->tm_mon+1);
    setfield(L, "year", stm->tm_year+1900);
    setfield(L, "wday", stm->tm_wday+1);
    setfield(L, "yday", stm->tm_yday+1);
    setboolfield(L, "isdst", stm->tm_isdst);
  }
  else {
    char cc[3];
    luaL_Buffer b;
    cc[0] = '%'; cc[2] = '\0';
    luaL_buffinit(L, &b);
    for (; *s; s++) {
      if (*s != '%' || *(s + 1) == '\0')  /* no conversion specifier? */
        luaL_addchar(&b, *s);
      else {
        size_t reslen;
        char buff[200];  /* should be big enough for any conversion result */
        cc[1] = *(++s);
        reslen = strftime(buff, sizeof(buff), cc, stm);
        luaL_addlstring(&b, buff, reslen);
      }
    }
    luaL_pushresult(&b);
  }
  return 1;
}


因为我们的服务端编的都是 debug 版本,vs 编 debug 版默认使用的多线程的那个 debug 版本的库:

打开源码找到对应的 strftime 函数,然后一路跟进去,最后终于找到了罪魁祸首!尼玛居然有一行 ASSERT!

最后就因为这个 ASSERT,我们的服务端光荣的挂了,而且我们外网的服务端就用的是 debug 版本,不过幸好 glibc 的库下不存在这个问题。


既然都看到这里了,我就顺带对比了一下,vs 这个版本的 CRT 库里 strftime 能够支持的字符和标准有什么区别: CRT 能够支持的字符有:aAbBcdHIjmMpSUwWxXyYZz%’\004’‘\015’ 而最新的 c 和 c++ 可以支持的果然更多,可以在 cplusplus.com 对比一下。



Comments





© 2024 菜鸟浮出水.
all original content is public domain under UNLICENSE