2013-05-20 2 views
40

나는 http://www.linux-nantes.org/~fmonnier/ocaml/ocaml-wrapping-c.php에있는 가이드를 기반으로 CZMQ에 대한 OCaml 바인딩을 작성했습니다. 이것은 꽤 잘 작동하는 것 같습니다. 예를 들어 여기 zstr_send입니다 :신호 처리기에서 OCaml-wrapped ZeroMQ 코드 호출하기

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)); 
} 

I 보내고 잘 내 코드의 대부분이 바인딩을 사용하여 메시지를받을 수 있습니다. 그러나 나는 신호 처리기 내부에서 송수신을하고 다른 코드의 배경에서 메시지 전달을 마칠 때 시나리오를 사용합니다. 이 간단한 예를 보자 최상위에서

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) 
;; 

을이 출력 실패 다음 OCaml의 유사

enter handler 
0 
end handler 
Fatal error: exception Sys_blocked_io 

C 코드는 위의 잘 작동합니다. OCaml이이 예외의 원인이되는 방정식에 무엇을 추가합니까?

+11

일반적으로 신호 처리기 내에서 루틴을 호출하는 것은 코드를 감사 할 때 빨간색 플래그입니다.그 이유는 다양하지만 그 주안점은 루틴이 비동기 안전하다는 것을 100 % 확신해야한다는 것입니다. [자세한 내용은 여기를 참조하십시오] –

+1

I (보안) 왜 C 나 OCaml 이건간에 처리기 내에서 IO를 시도하여 이미 위험한 상황에 처했다는 것을 제외하면 예외가 발생한 이유를 말할 수 없습니다. 위의 링크는 신호 처리기 호출을 안전하게 로깅 할 수있는 몇 가지 방법을 제공합니다. –

+0

정보를 제공해 주셔서 감사합니다, Dave. 나는 다른 접근법을 택할 것이다. – user1494672

답변

1

이 잠재적 인 문제가 있습니다 : 신호 처리기 내부

, 당신은 비동기 신호 안전 함수를 호출 할 수 있습니다. 대부분의 기능은 비동기 신호에 안전하지 않습니다.

제한의 이유는 같은 함수의 실행 중간에 함수를 호출 할 수 있기 때문입니다. 따라서 내부 상태가 손상 될 수 있습니다. 비동기 신호 안전성이 거의없는 기능은 거의 없으며 동적으로 메모리를 할당하는 기능은 없습니다. OCaml에서 많은 배정이 "배후에서"일어나기 때문에 코드가 비동기 시그널 안전하지 않을 가능성이 있습니다.

귀하의 경우 표준 출력에 쓰는 함수를 호출하고 있습니다. C에서 이것은 결코 비동기 신호 안전, 한가지 예외 : 원시 write() 기능입니다. 이것은 원시 시스템 호출 (파일 설명자에서 작동)이며 커널 자체가 신호 처리기에 신경 쓰지 않고 제어권을 반환하기 전에 완전히 정리 된 간단한 이유 때문에 비동기 신호 안전입니다. 프로그램의 작업을 포함하여 - 신호가 (여기 경우) 비동기이고 자체가 안전하지 않은 기능을 중단 신호 처리기에서 안전하지 않은 함수를 호출

이것은 아무것도 일어날 수 있음을 의미 C.에서 정의되지 않은 동작입니다 정확하게는 아니지만 세그먼테이션 오류 또는 기타 오류를 포함하여 공격자가 임의 코드를 실행할 수 있도록합니다. 이것은 일반적으로 C와 같은 저수준 언어와 관련이 있으며 OCaml에서는 일반적으로 발생하지 않는 것입니다.

OCaml은 OCaml에 핸들러가 설정되어있는 신호가 수신되면 safepoint가 될 때까지 핸들러 실행을 연기합니다. 결과적으로 처리기에서 상자에 수량을 ref 변수로 설정하는 것이 안전합니다. 그러나 print과 같은 다른 함수는 내부 상태를 가질 수 있으므로 재진입 가능하지 않을 수 있습니다. 일반적으로 시그널 핸들러 내에서 플래그를 설정하고 즉시 반환하는 것 이상의 일을 피하려고 노력해야합니다. OCaml에서 플래그는 31 또는 63 비트 정수 또는 부울 값이어야합니다. 이는 언 박싱되어 있기 때문입니다. C에서 플래그는 volatile sig_atomic_t이거나 C11 기본 유형 (확실하지 않습니다)이어야합니다.

@TheCodeArtist가 오류의 다른 가능한 원인을 제공합니다.

관련 문제