41 votes

Appel du code ZeroMQ enveloppé dans OCaml à partir du gestionnaire de signaux

J'ai écrit certains OCaml liaisons pour CZMQ basée sur le guide à http://www.linux-nantes.org/~fmonnier/ocaml/ocaml-wrapping-c.phpqui semblent fonctionner assez bien. Pour exemple voici 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));
}

Je peux envoyer et recevoir des messages à l'aide de ces fixations en plus de mon code de l'amende juste. Cependant, j'ai un scénario où je voudrais faire un envoie et reçoit à l'intérieur d'un gestionnaire de signal, à la fin de faire de la transmission de message dans le fond de certains autres code. Prenez cet exemple simplifié:

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

À partir de la racine, il échoue à la sortie:

enter handler
0
end handler
Fatal error: exception Sys_blocked_io

C un code similaire à l'OCaml ci-dessus fonctionne très bien. Qu'est-ce que OCaml en ajoutant à l'équation qui est à l'origine de cette exception?

1voto

Demetri Points 715

Il y a deux problèmes potentiels:

À l'intérieur d'un gestionnaire de signal, vous pouvez uniquement appeler asynchrone signal fort de fonctions. La plupart des fonctions ne sont pas async signal fort.

La raison de la restriction est qu'une fonction peut être appelée dans le milieu de la même fonction de l'exécution. Ainsi, l'état interne peut être corrompu. Très peu de fonctions sont asynchrones signal fort, et tout ce qui alloue dynamiquement la mémoire n'est pas. En OCaml, plusieurs allocations de se passer "derrière les coulisses", il est donc probable que votre code n'est pas asynchrone signal fort.

Dans votre cas, vous appelez une fonction qui écrit sur la sortie standard. En C, c'est de ne jamais async signal fort, avec une exception: le primitif write() fonction. C'est la crue de l'appel système (qui fonctionne sur un descripteur de fichier) et est asynchrone signal fort pour la simple raison que le noyau lui-même ne se soucie pas que vous êtes dans un gestionnaire de signal, et sera entièrement nettoyé avant de rendre le contrôle de vous.

L'appel d'une fonction dangereux à partir d'un gestionnaire de signal, lorsque le signal a été asynchrone (le cas ici) et lui-même interrompu un dangereux fonction est un comportement indéfini en C. Cela signifie que quelque chose pourrait se produire , incluant votre programme fonctionne correctement, mais aussi y compris la segmentation des défauts ou d'autres erreurs, ainsi que de permettre à un attaquant d'exécuter du code arbitraire. C'est normalement associé à un faible niveau de langages tels que le C, et c'est quelque chose qui, normalement, ne se produit pas dans le cas d'OCaml.

OCaml utilise une astuce: quand un signal est reçu pour lesquelles un gestionnaire a été mis en OCaml, il reporte l'exécution du gestionnaire jusqu'à ce que d'un point de restauration. Le résultat est que dans un gestionnaire, il est sûr de définir une "unboxed" quantité en ref variable. Cependant, d'autres fonctions comme le print sont peut-être pas réentrant, car ils peuvent avoir l'état interne. En général, au sein d'un gestionnaire de signal, vous devriez essayer d'éviter de faire plus que de fixer un drapeau et rapidement de retour. En OCaml, le drapeau doit être un homme de 31 63 bits d'un entier ou d'un booléen, parce que ce sont des "unboxed". En C, le drapeau doit être soit volatile sig_atomic_t ou (je ne suis pas sûr à ce sujet) un C11 type atomique.

@TheCodeArtist donne l'autre raison possible pour l'erreur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X