从信号处理程序调用OCaml包装的ZeroMQ代码

时间:2013-05-20 15:42:09

标签: c ocaml zeromq

我根据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添加到导致此异常的等式中?

1 个答案:

答案 0 :(得分:1)

有两个潜在的问题:

在信号处理程序中,您只能调用异步信号安全函数。大多数功能都不是异步信号安全。

限制的原因是可以在同一函数的执行过程中调用函数。因此,内部状态可能被破坏。很少有函数是异步信号安全的,任何动态分配内存的函数都不是。在OCaml中,许多分配都是“幕后”发生的,因此您的代码很可能不是异步信号安全的。

在您的情况下,您正在调用写入标准输出的函数。在C中,这是从不异步信号安全,有一个例外:原始write()函数。这是原始系统调用(对文件描述符进行操作)并且异步信号是安全的,原因很简单,因为内核本身并不关心您是否处于信号处理程序中,并且在将控制权交还给您之前会完全清除。

从信号处理程序调用不安全的函数,当信号是异步的(这里的情况)并且本身中断了一个不安全的函数是C中的未定义的行为。这意味着任何事情都可能发生< / em> - 包括您的程序正常工作,但也包括分段错误或其他错误,以及允许攻击者执行任意代码。这通常与C等低级语言相关联,并且通常不会出现在OCaml中。

OCaml使用一个巧妙的技巧:当接收到已在OCaml中设置了处理程序的信号时,它会延迟执行处理程序直到安全点。结果是,在处理程序中,将未装箱的数量设置为ref变量是安全的。但是,print等其他函数可能不具有可重入性,因为它们可能具有内部状态。通常,在信号处理程序中,您应该尽量避免设置标志并立即返回。在OCaml中,标志应该是31位或63位整数或布尔值,因为它们是未装箱的。在C中,标志必须是volatile sig_atomic_t或(我不确定)C11原子类型。

@TheCodeArtist给出了错误的其他可能原因。