为什么重新加载动态库时 ncurses 会失败?

Why does ncurses fail when I reload a dynamic library?

我一直在关注“Interactive Programming in C”上的博客 post。它背后的总体理想是它在 ncurses 中显示 Conway 的生命游戏的实现,但游戏逻辑是作为动态库加载的。 main() 循环监视游戏的 .so 并在发现更新版本时以交互方式重新加载它。

我已经在 LinuxMint 17 中尝试过,但是 ncurses 在动态重新加载共享库时挂起。我已经进行了一些调试 fprintfs 并跟踪了 GDB 中的程序执行,程序似乎正确地重新加载了库并像往常一样继续主循环。

看来问题出在ncurses,但我不知道如何调试它。一旦重新加载动态库,game.c:draw() 中的 ncurses 函数 refresh() 开始返回 -1。谁能帮忙?代码可以在博客 post.

中链接的 github repo 中找到

更新

看起来 ncurses 库中的变量 stdscr 在调用 dlclose() 之后就消失了。这不会发生在我工作的 RedHat 机器上。

ncurses 是 libgame.so 的依赖项,而不是 main 可执行文件的依赖项。所以如果你卸载动态库并加载更改后的版本,你也会卸载 ncurses 然后再次加载它。但是您随后无法重新初始化它。这就是你的程序失败的原因。

正确的解决方法是在game_unload中调用endwin()来清理ncurses的状态,然后在game_reload中重新初始化它:

static void game_reload(struct game_state *state)
{
    initscr();
    raw();
    timeout(0);
    noecho();
    curs_set(0);
    keypad(stdscr, TRUE);
}

static void game_unload(struct game_state *state)
{
    endwin();
}

另一种解决方案是强制 linker 对主要可执行文件执行 link ncurses。这将防止动态 linker 在您卸载游戏库时卸载它。您可以通过在编译 main 可执行文件时在 $(LDLIBS) 变量之前添加 -Wl,--no-as-needed 标志来做到这一点:

main : main.c game.h
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -Wl,--no-as-needed $(LDLIBS)

如果您更喜欢此解决方案,请考虑将 ncurses initialization/cleanup 也移动到 main.c 文件中。这没有技术原因,这只是一个干净的编码风格问题。