3

由于作品版权,我无法发布我的实际代码,所以我将尝试用简单的示例代码来展示我的问题。

我有一个 C 扩展,其简化版本如下所示:

#include <ruby.h>
#include <termios.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

VALUE test(VALUE self, VALUE string);
void Init_module_name() {
     module_name = rb_define_module("Modulename");
     c_modulename = rb_define_class_under(modulename, "Class", rb_cObject);
     rb_define_method(c_modulename, "test", test, 1);

     e_ModuleNameError = rb_define_class_under(modulename, "Error", rb_eStandardError);
}

VALUE test(VALUE self, VALUE string) {
    char *c_string = StringValueCStr(string);
    int fd = open(c_string, O_RDWR | O_NOCTTY | O_NONBLOCK);

    if (fd == -1) {
       rb_raise(e_ModuleNameError, "Failed to open file");
    }
    if (!isatty(fd)) {
       rb_raise(e_ModuleNameError, "File is not a tty");
    }

    struct termios config;
    int termios_ret = init_termios(config, fd)
    if (termios_ret != OK) { // OK defined by enum in modulename's header
        close(fd);
        rb_raise(e_ModuleNameError, "Termios init failed.");
    }

    int success = write(fd, "I'm a string", str_length);
    if (success < str_length) {
        close(fd);
        rb_raise(e_ModuleNameError, "Failed to write to file.");
    }

    close(fd);
    return rb_str_new2("Success");
}

然后,需要这个的 ruby​​ 代码如下所示:

require 'modulename'

class ModuleName
  attr_acessor :file

  def initialize(file)
    @file = file
    @object = Modulename::Class.new
  end

  def test
    @object.test @file
  end
end

然后在我的生产项目中调用它,例如:

require "modulename_ruby_file"

x = ModuleName "/dev/pts/1"
x.test

这是有趣的事情。当我在生产环境中运行此代码时,上面 x.test 的返回值为 false(字面意义上的值为 false,而不是字符串)。此外,对文件的写入永远不会发生。但是,如果我在一些简化的测试代码中执行此操作,它会像预期的那样返回字符串“Success”,并且确实完成了写入。

有谁知道会导致此函数不执行写入并返回 false 的任何情况?我已经尝试在它周围进行救援,以防它抛出一个 rb_raises,但它似乎没有。

我和我团队的其他 3 名成员整个下午都在看这个问题,但没有找到答案。

4

1 回答 1

3

终于弄清楚了,这与@NeilSlater 在问题评论中所说的非常相似。

我们向 C 代码添加了大量调试,并将其写入日志文件,并发现 C 函数(在我的示例中为测试)实际上从未被调用过。因此,我们查看了 .so 的符号表和 gcc 正在生成的汇编代码,两者看起来都很好。最后我们只是说,“让我们更改函数的名称,看看是否有帮助”并且......它起作用了。实际函数名为 logout,将其更改为 project_name_logout 有效,因此显然存在某种命名空间冲突。所以,就像@NeilSlater 所说的那样,它与环境有关!

因此,对于在 google 上找到此内容的任何其他人:通过在所有函数前面加上您的项目名称,将“命名空间”添加到您的 C 代码中,您应该能够避免这个问题。[其中一位成员后来提到,无论如何,这在 C 语言中是一个很好的做法。]

注意:我们没有花时间追踪与注销发生冲突的原因。

于 2013-04-15T15:46:58.410 回答