41

http://www.linux-nantes.org/~fmonnier/ocaml/ocaml-wrapping-c.phpのガイドに基づいて、CZMQ 用の OCaml バインディングをいくつか作成しました。たとえば、zstr_send は次のとおりです。

CAMLprim value
caml_zstr_send(value socket_val, value string_val)
{
    CAMLparam2 (socket_val, string_val);

    void *sock = CAML_CZMQ_zsocket_val(socket_val);
    char *string = String_val(string_val);
    int rc = zstr_send(sock, string);

    CAMLreturn (Val_int(rc));
}

ほとんどのコードでこれらのバインディングを使用してメッセージを送受信できます。ただし、シグナルハンドラー内で送受信を行い、他のコードのバックグラウンドでメッセージパッシングを行うシナリオがあります。次の簡単な例を見てください。

open ZMQ
exception SocketBindFailure

let bg_ctx = zctx_new ();;
let pub_sock = zsocket_new bg_ctx ZMQ_PUB;;

let handler _ =
    print_endline "enter handler";
    print_endline (string_of_int (zstr_send pub_sock "hello"));
    print_endline "end handler";
;;

let () =
    (try (
        (* bind pub socket *)
        let rc = zsocket_bind pub_sock "tcp://*:5556" in
        if (rc < 0) then ( raise SocketBindFailure );

        Sys.set_signal 
            Sys.sigalrm
            (Sys.Signal_handle handler);

        ignore
             (Unix.setitimer
                 Unix.ITIMER_REAL
                 { Unix.it_interval = 0.01 ; Unix.it_value = 0.01 });

        (* do some work *)
    )
    with 
    | SocketBindFailure -> raise SocketBindFailure) 
;;

トップレベルから、これは出力で失敗します:

enter handler
0
end handler
Fatal error: exception Sys_blocked_io

上記の OCaml に似た C コードは問題なく動作します。この例外を引き起こしている方程式に OCaml が追加しているものは何ですか?

4

1 に答える 1

1

次の 2 つの潜在的な問題があります。

シグナル ハンドラー内では、非同期シグナル セーフ関数のみを呼び出すことができます。ほとんどの関数は、非同期信号に対して安全ではありません。

この制限の理由は、関数が同じ関数の実行中に呼び出される可能性があるためです。したがって、内部状態が破損する可能性があります。非同期信号に対して安全な関数はほとんどなく、メモリを動的に割り当てるものは安全ではありません。OCaml では、多くの割り当てが「舞台裏」で行われるため、コードが async シグナルセーフではない可能性があります。

あなたの場合、標準出力に書き込む関数を呼び出しています。C では、プリミティブ関数を除いて、これは決して非同期シグナルセーフではありません。write()これは生のシステム コール (ファイル記述子で動作する) であり、カーネル自体がシグナル ハンドラーにいることを気にせず、制御をユーザーに返す前に完全にクリーンアップするという単純な理由から、非同期シグナルに対して安全です。

シグナルが非同期で (この場合)、それ自体が安全でない関数に割り込まれた場合に、シグナル ハンドラーから安全でない関数を呼び出すことは、Cでは未定義の動作です。他のエラーだけでなく、攻撃者が任意のコードを実行できるようにします。これは通常、C などの低水準言語に関連しており、OCaml では通常発生しないものです。

OCaml は巧妙なトリックを使用します。OCaml でハンドラーが設定されているシグナルを受信すると、セーフポイントまでハンドラーの実行を延期します。その結果、ハンドラーではボックス化されていない数量を変数に安全に設定できますref。ただし、 のような他の関数printは、内部状態を持つ可能性があるため、再入可能ではない可能性があります。一般に、シグナル ハンドラー内では、フラグを設定してすぐに戻る以上のことを避けるようにする必要があります。OCaml では、フラグは 31 ビットまたは 63 ビットの整数、またはブール値である必要があります。これらはボックス化されていないためです。C では、フラグはvolatile sig_atomic_tC11 アトミック タイプまたは (これについてはよくわかりません) のいずれかでなければなりません。

@TheCodeArtist は、エラーの他の考えられる理由を示します。

于 2014-07-24T00:24:51.723 に答える