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 のように)。また、ファイルへの書き込みは発生しません。ただし、単純化されたテストコードでそれを行うと、期待どおりに文字列「成功」が返され、書き込みは実際に完了します。

この関数が書き込みを実行せず、false を返す状況を知っている人はいますか? rb_raise の 1 つをスローしていた場合に備えて、すでにその周りにレスキューを配置しようとしましたが、そうではないようです。

私と私のチームの他の 3 人のメンバーは、午後中ずっとこれを見てきましたが、答えは見つかりませんでした。

4

1 に答える 1

3

最終的にこれを理解し、質問に対するコメントで@NeilSlaterが言ったことと非常に似ていました。

C コードに大量のデバッグを追加し、ログ ファイルに書き込み、C 関数 (私の例ではテスト) が文字通り呼び出されていないことがわかりました。そこで、.so のシンボル テーブルと、gcc が生成していたアセンブリ コードを調べたところ、どちらも問題ないように見えました。最後に、「関数の名前を変更して、それが役立つかどうか見てみましょう」と言ったところ、... うまくいきました。実際の関数は logout という名前で、それを project_name_logout に変更すると機能したため、何らかの名前空間の衝突があったようです。だから、@NeilSlaterが言ったように、それは環境と関係がありました!

だから、グーグルでこれを見つけた他の人のために:プロジェクト名をすべての関数の前に付けてCコードに「名前空間」を追加すると、この問題を回避できるはずです。[他のメンバーの 1 人は、事後的に、とにかくこれは C での良い習慣であると述べました。]

注: 何がログアウトと競合しているのかを追跡する時間はありませんでした。

于 2013-04-15T15:46:58.410 に答える