草庐IT

c - 多线程 C Lua 模块导致 Lua 脚本中出现段错误

coder 2023-06-18 原文


我为 Lua 编写了一个非常简单的 C 库,它由一个启动线程的函数组成,该线程除了循环之外什么都不做:

#include "lua.h"
#include "lauxlib.h"
#include <pthread.h>
#include <stdio.h>

pthread_t handle;
void* mythread(void* args)
{
    printf("In the thread !\n");
    while(1);
    pthread_exit(NULL);
}

int start_mythread()
{
    return pthread_create(&handle, NULL, mythread, NULL);
}

int start_mythread_lua(lua_State* L)
{
    lua_pushnumber(L, start_mythread());
    return 1;
}

static const luaL_Reg testlib[] = {
    {"start_mythread", start_mythread_lua},
    {NULL, NULL}
};

int luaopen_test(lua_State* L)
{
/*
    //for lua 5.2
    luaL_newlib(L, testlib);
    lua_setglobal(L, "test");
*/
    luaL_register(L, "test", testlib);
    return 1;
}


现在,如果我编写一个非常简单的 Lua 脚本:

require("test")
test.start_mythread()

使用lua myscript.lua 运行脚本有时会导致段错误。以下是 GDB 对核心转储的看法:

Program terminated with signal 11, Segmentation fault.
#0  0xb778b75c in ?? ()
(gdb) thread apply all bt

Thread 2 (Thread 0xb751c940 (LWP 29078)):
#0  0xb75b3715 in _int_free () at malloc.c:4087
#1  0x08058ab9 in l_alloc ()
#2  0x080513a2 in luaM_realloc_ ()
#3  0x0805047b in sweeplist ()
#4  0x080510ef in luaC_freeall ()
#5  0x080545db in close_state ()
#6  0x0804acba in main () at lua.c:389

Thread 1 (Thread 0xb74efb40 (LWP 29080)):
#0  0xb778b75c in ?? ()
#1  0xb74f6efb in start_thread () from /lib/i386-linux-gnu/i686/cmov/libpthread.so.0
#2  0xb7629dfe in clone () at ../sysdeps/unix/sysv/linux/i386/clone.S:129

主线程的堆栈会不时发生一些变化。
似乎start_thread 函数想要跳转到给定的地址(在本例中为b778b75c),而该地址有时恰好属于无法访问的内存。
编辑
我还有一个 valgrind 输出:

==642== Memcheck, a memory error detector
==642== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==642== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==642== Command: lua5.1 go.lua
==642== 
In the thread !
In the thread !
==642== Thread 2:
==642== Jump to the invalid address stated on the next line
==642==    at 0x403677C: ???
==642==    by 0x46BEEFA: start_thread (pthread_create.c:309)
==642==    by 0x41C1DFD: clone (clone.S:129)
==642==  Address 0x403677c is not stack'd, malloc'd or (recently) free'd
==642== 
==642== 
==642== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==642==  Access not within mapped region at address 0x403677C
==642==    at 0x403677C: ???
==642==    by 0x46BEEFA: start_thread (pthread_create.c:309)
==642==    by 0x41C1DFD: clone (clone.S:129)
==642==  If you believe this happened as a result of a stack
==642==  overflow in your program's main thread (unlikely but
==642==  possible), you can try to increase the size of the
==642==  main thread stack using the --main-stacksize= flag.
==642==  The main thread stack size used in this run was 8388608.
==642== 
==642== HEAP SUMMARY:
==642==     in use at exit: 1,296 bytes in 6 blocks
==642==   total heap usage: 515 allocs, 509 frees, 31,750 bytes allocated
==642== 
==642== LEAK SUMMARY:
==642==    definitely lost: 0 bytes in 0 blocks
==642==    indirectly lost: 0 bytes in 0 blocks
==642==      possibly lost: 136 bytes in 1 blocks
==642==    still reachable: 1,160 bytes in 5 blocks
==642==         suppressed: 0 bytes in 0 blocks
==642== Rerun with --leak-check=full to see details of leaked memory
==642== 
==642== For counts of detected and suppressed errors, rerun with: -v
==642== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Killed


但是,到目前为止,我一直很好,只是打开 lua 解释器并一个接一个地手动输入相同的指令。
另外,一个 C 程序做同样的事情,使用相同的库:

int start_mythread();

int main()
{
    int ret = start_mythread();
    return ret;
}

正如它应该的那样,在我的测试中从未失败过。
我试过 Lua 5.1 和 5.2,但无济于事。
编辑: 我应该指出,我在运行 32 位 Debian Wheezy (Linux 3.2) 的单核 eeePC 上对此进行了测试。
我刚刚在我的主机(4 核 64 位 Arch linux)上再次测试,并使用 lua myscript.lua 段错误启动脚本每次 那里... 从解释器提示符输入命令工作正常,以及上面的 C 程序。

我首先编写这个小库的原因是因为我正在编写一个更大的库,我首先遇到了这个问题。经过数小时毫无结果的调试,包括一个一个地删除每个共享结构/变量(是的,我当时非常绝望),我归结为这段代码。
所以,我的猜测是我对 Lua 做错了什么,但那会是什么?我尽可能多地搜索了这个问题,但我发现大多数人在从多个线程使用 Lua API 时遇到问题(这不是我在这里尝试做的)。
如果您有任何想法,我们将不胜感激。

编辑
更准确地说,我想知道在编写用于 Lua 脚本的 C 库时是否应该对线程采取额外的预防措施。 Lua 需要从动态加载的库中创建的线程在它“卸载”库时终止吗?

最佳答案

Lua模块为什么会出现Segfault?

您的 Lua 脚本在线程完成之前退出,这会导致段错误。 Lua 模块在正常的解释器关闭期间使用 dlclose() 卸载,因此线程的指令从内存中删除,并在读取下一条指令时出现段错误。

有哪些选项?

任何在卸载模块之前停止线程的解决方案都可以工作。在主线程中使用 pthread_join() 将等待线程完成(您可能想使用 pthread_cancel() 终止长时间运行的线程)。在卸载模块之前在主线程中调用 pthread_exit() 也可以防止崩溃(因为它可以防止 dlclose()),但它也会中止正常的清理/Lua解释器的关闭过程。

这里有一些有效的例子:

int pexit(lua_State* L) {
   pthread_exit(NULL);
   return 0; 
} 

int join(lua_State* L)
{
  pthread_join(handle, NULL);
  return 0;
}

static const luaL_Reg testlib[] = {
    {"start_mythread", start_mythread_lua},
    {"join", join},
    {"exit", pexit},
    {NULL, NULL}
};

void* mythread(void* args) {
  int i, j, k;
    printf("In the thread !\n");
    for (i = 0; i < 10000; ++i) {
      for (j = 0; j < 10000; ++j) {
        for (k = 0; k < 10; ++k) {
          pow(1, i);
        }
      }
    }
    pthread_exit(NULL);
}

现在脚本将正常退出:

require('test')
test.start_mythread()
print("launched thread")
test.join() -- or test.exit()
print("thread joined")

要自动执行此操作,您可以连接到垃圾收集器,因为模块中的所有对象都在卸载共享对象之前被释放。 (正如 greatwolf 所建议的那样)

Discussion on calling pthread_exit() from main(): There is a definite problem if main() finishes before the threads it spawned if you don't call pthread_exit() explicitly. All of the threads it created will terminate because main() is done and no longer exists to support the threads. By having main() explicitly call pthread_exit() as the last thing it does, main() will block and be kept alive to support the threads it created until they are done.

(这句话有点误导:从 main() 返回大致等同于调用 exit(),它将退出进程,包括所有正在运行的进程线程。这可能是也可能不是您想要的行为。另一方面,在主线程中调用 pthread_exit() 将退出主线程,但会保持所有其他线程运行,直到它们自行停止或者其他人杀死了他们。同样,这可能是也可能不是你想要的行为。没有问题,除非你为你的用例选择了错误的选项。)

关于c - 多线程 C Lua 模块导致 Lua 脚本中出现段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28527521/

有关c - 多线程 C Lua 模块导致 Lua 脚本中出现段错误的更多相关文章

  1. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  2. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  3. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  4. ruby - 难道Lua没有和Ruby的method_missing相媲美的东西吗? - 2

    我好像记得Lua有类似Ruby的method_missing的东西。还是我记错了? 最佳答案 表的metatable的__index和__newindex可以用于与Ruby的method_missing相同的效果。 关于ruby-难道Lua没有和Ruby的method_missing相媲美的东西吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/7732154/

  5. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  6. ruby-on-rails - 独立 ruby​​ 脚本的配置文件 - 2

    我有一个在Linux服务器上运行的ruby​​脚本。它不使用rails或任何东西。它基本上是一个命令行ruby​​脚本,可以像这样传递参数:./ruby_script.rbarg1arg2如何将参数抽象到配置文件(例如yaml文件或其他文件)中?您能否举例说明如何做到这一点?提前谢谢你。 最佳答案 首先,您可以运行一个写入YAML配置文件的独立脚本:require"yaml"File.write("path_to_yaml_file",[arg1,arg2].to_yaml)然后,在您的应用中阅读它:require"yaml"arg

  7. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  8. ruby - 当使用::指定模块时,为什么 Ruby 不在更高范围内查找类? - 2

    我刚刚被困在这个问题上一段时间了。以这个基地为例:moduleTopclassTestendmoduleFooendend稍后,我可以通过这样做在Foo中定义扩展Test的类:moduleTopmoduleFooclassSomeTest但是,如果我尝试通过使用::指定模块来最小化缩进:moduleTop::FooclassFailure这失败了:NameError:uninitializedconstantTop::Foo::Test这是一个错误,还是仅仅是Ruby解析变量名的方式的逻辑结果? 最佳答案 Isthisabug,or

  9. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  10. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

随机推荐