从信号处理程序中调用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添加到引起此exception的公式?

有两个潜在的问题:

在信号处理程序中,只能调用asynchronous信号安全函数。 大多数函数不是asynchronous信号安全的。

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

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

在信号处理程序中调用一个不安全的函数时,如果信号是asynchronous的(这里是这种情况),并且本身中断了一个不安全的函数,那么在C中是未定义的行为 。这意味着会发生任何事情 – 包括程序正常工作,还包括段错误其他错误,以及允许攻击者执行任意代码。 这通常与像C这样的低级语言相关联,并且通常不会在OCaml中出现。

OCaml使用了一个巧妙的技巧:当一个信号接收到一个处理程序已经在OCaml中设置,它延迟执行处理程序,直到一个安全点。 结果是在一个处理程序中,将一个取消装箱量设置为一个refvariables是安全的。 但是,像print这样的其他函数可能不是可重入的,因为它们可能有内部状态。 一般来说,在信号处理程序中,您应该尽量避免设置标志并立即返回。 在OCaml中,该标志应该是一个31或63位的整数,或者一个布尔值,因为它们是拆箱的。 在C中,标志必须是volatile sig_atomic_t或者(我不确定)C11的primefacestypes。

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