{"id":1171,"date":"2011-10-02T08:00:10","date_gmt":"2011-10-02T07:00:10","guid":{"rendered":"http:\/\/www.blaess.fr\/christophe\/?p=1171"},"modified":"2011-10-02T08:00:10","modified_gmt":"2011-10-02T07:00:10","slug":"efficacite-des-ipc-les-signaux-temps-reel","status":"publish","type":"post","link":"https:\/\/www.blaess.fr\/christophe\/2011\/10\/02\/efficacite-des-ipc-les-signaux-temps-reel\/","title":{"rendered":"Efficacit\u00e9 des IPC&nbsp;: les signaux temps-r\u00e9el"},"content":{"rendered":"<p style=\"text-align: justify;\">Nous avions examin\u00e9 dans <a title=\"Efficacit\u00e9 des IPC&nbsp;: les files de messages Posix\" href=\"http:\/\/www.blaess.fr\/christophe\/2011\/09\/17\/efficacite-des-ipc-les-files-de-messages-posix\/\" target=\"_blank\">l&rsquo;article pr\u00e9c\u00e9dent<\/a> de cette s\u00e9rie un premier m\u00e9canisme de communication entre processus&nbsp;: les <em>files de messages Posix<\/em>. Nous avions observ\u00e9 qu&rsquo;il \u00e9tait facile de transmettre un message de quelques kilo-octets entre deux processus, en un temps allant de 8 microsecondes si le processeur \u00e9tait d\u00e9j\u00e0 actif \u00e0 15 microsecondes si les messages \u00e9taient suffisament espac\u00e9s pour laisser au processeur le temps de s&rsquo;endormir et de prendre un temps de r\u00e9veil non n\u00e9gligeable.<\/p>\n<p style=\"text-align: justify;\">Nous allons \u00e0 pr\u00e9sent observer les possibilit\u00e9s li\u00e9es aux <em>signaux temps-r\u00e9el Posix.1b<\/em>.<\/p>\n<p>\n<!--more-->\n<\/p>\n<h1>Signaux temps-r\u00e9el<\/h1>\n<p style=\"text-align: justify;\">Les signaux temps-r\u00e9el introduits par la norme Posix.1b (et qui n&rsquo;ont de temps-r\u00e9el que leur nom) ne sont ni plus rapides ni plus prioritaires que les signaux classiques Unix. Ils sont toutefois un peu plus fiables et peuvent nous offrir un moyen assez m\u00e9connu de communiquer entre des processus.<\/p>\n<p style=\"text-align: justify;\">Les principales diff\u00e9rences entre signaux classiques et signaux temps-r\u00e9el sont les suivantes.<\/p>\n<ul>\n<li style=\"text-align: justify;\">Les signaux temps-r\u00e9el ne sont jamais \u00e9mis spontan\u00e9ment par le syst\u00e8me, ils sont \u00e0 la disposition du d\u00e9veloppeur applicatif. Ils doit y en avoir au moins 8 (sous Linux ils sont 32) dans l&rsquo;intervalle <code>[SIGRTMIN, SIGRTMAX]<\/code>.<\/li>\n<li style=\"text-align: justify;\">Lorsque la d\u00e9livrance d&rsquo;un signal classique est bloqu\u00e9e temporairement par un processus, et que plusieurs occurrences de ce signal arrivent, un seul sera d\u00e9livr\u00e9 lorsque le processus le d\u00e9bloquera. Les signaux temps-r\u00e9el en attente sont mis en file.<\/li>\n<li style=\"text-align: justify;\">Si plusieurs signaux temps-r\u00e9el doivent \u00eatre d\u00e9livr\u00e9s \u00e0 un processus, ils lui arrivent par ordre croissant. Avec les signaux classiques, il n&rsquo;y a aucun ordre normalis\u00e9.<\/li>\n<li style=\"text-align: justify;\">Enfin, un signal temps-r\u00e9el peut \u00eatre accompagn\u00e9 d&rsquo;un entier (ou d&rsquo;un pointeur mais \u00e7a ne sert pratiquement jamais), \u00e0 la diff\u00e9rence d&rsquo;un signal classique qui ne sert que de notification.<\/li>\n<\/ul>\n<p style=\"text-align: justify;\">On peut donc utiliser les signaux temps-r\u00e9el lorsque le message \u00e0 transmettre est tr\u00e8s court (id\u00e9alement la taille d&rsquo;un entier).<\/p>\n<h2>\u00c9mission<\/h2>\n<p style=\"text-align: justify;\">On envoie un signal temps-r\u00e9el avec l&rsquo;appel <code>sigqueue()<\/code><\/p>\n<pre>#include &lt;signal.h&gt;\n\nint <strong>sigqueue<\/strong>(pid_t <em>pid<\/em>, int <em>signal<\/em>, const union sigval <em>valeur<\/em>);<\/pre>\n<p style=\"text-align: justify;\">dont les arguments sont<\/p>\n<ul>\n<li style=\"text-align: justify;\"><code>pid<\/code> : la cible vis\u00e9e par le signal<\/li>\n<li style=\"text-align: justify;\"><code>signal<\/code> : le num\u00e9ro que l&rsquo;on indique avec les notations <code>SIGRTMIN<\/code>, <code>SIGRTMIN+1<\/code>, <code>SIGTRMIN+2<\/code>&#8230; <code>SIGRTMAX-1<\/code>, <code>SIGRTMAX<\/code><\/li>\n<li style=\"text-align: justify;\"><code>valeur<\/code> : cette union contient deux champs superpos\u00e9s&nbsp;: <code>sival_int<\/code> et <code>sival_ptr<\/code> qui transmettent respectivement un entier ou un pointeur.<\/li>\n<\/ul>\n<h2>R\u00e9ception<\/h2>\n<p style=\"text-align: justify;\">Pour recevoir un signal temps-r\u00e9el il faudra \u00e9crire un gestionnaire l\u00e9g\u00e8rement diff\u00e9rent de ceux utilis\u00e9s pour les signaux classiques (afin de pouvoir r\u00e9cuperer la valeur qui l&rsquo;accompagne).<\/p>\n<pre>void <strong>handler<\/strong>(int <em>numero<\/em>, siginfo_t * <em>info<\/em>, void * <em>unused<\/em>);<\/pre>\n<p style=\"text-align: justify;\">La structure obtenue dans le param\u00e8tre <code>info<\/code> contient dans son champs <code>si_value<\/code> l&rsquo;<code>union sigval<\/code> remplie lors de l&rsquo;\u00e9mission. Pour en savoir plus, consultez la page de manuel <code>sigaction(2)<\/code>.<\/p>\n<p style=\"text-align: justify;\">Le dernier param\u00e8tre, bien qu&rsquo;inutile est obligatoire&nbsp;; c&rsquo;est une question de coh\u00e9rence de prototype du handler.<\/p>\n<p style=\"text-align: justify;\">L&rsquo;installation du gestionnaire se fait avec&nbsp;:<\/p>\n<pre>#include &lt;signal.h&gt;\nint <strong>sigaction<\/strong>(int <em>numero<\/em>, const struct sigaction *<em>action<\/em>, struct sigaction *<em>precedent<\/em>);<\/pre>\n<p style=\"text-align: justify;\">Les champs importants de la structure <code>sigaction<\/code> sont les suivants.<\/p>\n<ul>\n<li><code>sa_flags<\/code> doit contenir <code>SA_SIGINFO<\/code> pour que la valeur accompagnant le signal soit transmise au gestionnaire,<code><\/code><\/li>\n<li><code>sa_mask<\/code> est g\u00e9n\u00e9ralement rempli enti\u00e8rement avec <code>sigfillset()<\/code> pour \u00e9viter que le handler puisse \u00eatre interrompu par un autre signal.<code><\/code><\/li>\n<li><code>sa_sigaction<\/code> contient le pointeur sur la fonction <code>handler<\/code>.<\/li>\n<\/ul>\n<h2>Transfert rare<\/h2>\n<p>Nous allons employer un petit programme qui se scinde en deux processus. Le fils envoie un signal temps r\u00e9el \u00e0 son p\u00e8re toutes les secondes, accompagn\u00e9 de la valeur des microsecondes de l&rsquo;heure qu&rsquo;il vient de lire. Le p\u00e8re lit l&rsquo;heure courante et celle transmise par son fils, puis affiche par comparaison la dur\u00e9e \u00e9coul\u00e9e. Les fichiers sources et le <code>Makefile<\/code> sont dans <a title=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2011-10-02\/Efficacite-signaux-temps-reel.tar.bz2\" href=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2011-10-02\/Efficacite-signaux-temps-reel.tar.bz2\" target=\"_blank\">cette archive<\/a>.<\/p>\n<pre><strong>transfert-signaux-01.c :<\/strong>\n#include\n#include\n#include\n#include\n#include \n\nstatic void processus_fils(void);\nstatic void processus_pere(void);\nstatic void handler(int, siginfo_t *, void *);\n\nint main(int argc,char * argv[])\n{\n    pid_t pid;\n    pid = <strong>fork<\/strong>();\n    if (pid &lt; 0) {\n        perror(\"fork\");\n        exit(EXIT_FAILURE);\n    }\n\n    if (pid == 0) {\n        processus_fils();\n    } else {\n        processus_pere();\n    }\n    return EXIT_SUCCESS;\n}\n\nstatic void processus_fils(void)\n{\n    struct timeval heure;\n    union sigval value;\n    sleep(2); \/\/ Pour etre sur que le pere ait installe son handler\n    while (1) {\n        <strong>gettimeofday<\/strong>(&amp; heure, NULL);\n        value.sival_int = heure.tv_usec;\n        <strong>sigqueue<\/strong>(getppid(), SIGRTMIN, value);\n        sleep(1);\n    }\n}\n\nstatic void processus_pere(void)\n{\n    struct sigaction action;\n    action.sa_flags = SA_SIGINFO;\n    action.sa_sigaction = handler;\n    sigfillset(&amp; action.sa_mask);\n    <strong>sigaction<\/strong>(SIGRTMIN, &amp; action, NULL);\n    while (1)\n        pause();\n}\n\nstatic void handler(int unused, siginfo_t * info, void * unused_ptr)\n{\n    struct timeval heure;\n    <strong>gettimeofday<\/strong>(&amp; heure, NULL);\n    heure.tv_usec -= info-&gt;<strong>si_value.sival_int<\/strong>;\n    if (heure.tv_usec &lt; 0)\n        heure.tv_usec += 1000000;\n    fprintf(stdout, \"Duree = %ld micro-secondesn\", heure.tv_usec);\n}<\/pre>\n<p style=\"text-align: justify;\">Testons notre programme&nbsp;:<\/p>\n<pre>$ <strong>.\/transfert-signaux-01 <\/strong>\nDuree = 33 micro-secondes\nDuree = 8 micro-secondes\nDuree = 18 micro-secondes\nDuree = 13 micro-secondes\nDuree = 11 micro-secondes\nDuree = 10 micro-secondes\nDuree = 9 micro-secondes\nDuree = 13 micro-secondes\nDuree = 17 micro-secondes\nDuree = 17 micro-secondes\nDuree = 12 micro-secondes\nDuree = 17 micro-secondes\nDuree = 11 micro-secondes\nDuree = 10 micro-secondes\nDuree = 17 micro-secondes\nDuree = 14 micro-secondes\nDuree = 17 micro-secondes\nDuree = 16 micro-secondes\nDuree = 15 micro-secondes\nDuree = 16 micro-secondes\nDuree = 20 micro-secondes\nDuree = 6 micro-secondes\nDuree = 6 micro-secondes\nDuree = 14 micro-secondes\nDuree = 7 micro-secondes\n<strong><em>(Contr\u00f4le-C)<\/em><\/strong>\n$<\/pre>\n<h2>Transfert \u00e0 d\u00e9bit \u00e9lev\u00e9<\/h2>\n<p style=\"text-align: justify;\">L&rsquo;inconv\u00e9nient avec les signaux, est qu&rsquo;il n&rsquo;y a aucune garantie que le processus cible ait bien re\u00e7u le signal, \u00e0 moins d&rsquo;int\u00e9grer un m\u00e9canisme d&rsquo;acquittement \u00e0 un niveau plus \u00e9lev\u00e9. Nous allons donc programmer une partie de ping-pong o\u00f9 chaque processus renvoie un signal \u00e0 son partenaire d\u00e8s qu&rsquo;il en a lui-m\u00eame re\u00e7u un. Nous afficherons les statistiques de dur\u00e9es toutes les secondes.<\/p>\n<pre><strong>transfert-signaux-02.c:<\/strong>\n#include\n#include\n#include\n#include\n#include \n\nstatic void processus_fils(void);\nstatic void processus_pere(void);\nstatic void handler_pere(int, siginfo_t *, void *);\nstatic void handler_fils(int, siginfo_t *, void *);\n\nint main(int argc,char * argv[])\n{\n    pid_t pid;\n    pid = <strong>fork<\/strong>();\n    if (pid &lt; 0) {\n        perror(\"fork\");\n        exit(EXIT_FAILURE);\n    }\n\n    if (pid == 0) {\n        processus_fils();\n    } else {\n        processus_pere();\n    }\n    return EXIT_SUCCESS;\n}\n\nstatic void processus_fils(void)\n{\n    struct sigaction action;\n    action.sa_flags = SA_SIGINFO;\n    action.sa_sigaction = handler_fils;\n    sigfillset(&amp; action.sa_mask);\n    <strong>sigaction<\/strong>(SIGRTMIN, &amp; action, NULL);\n\n    sleep(2); \/\/ Pour etre sur que le pere ait installe son handler\n    <strong>raise<\/strong>(SIGRTMIN); \/\/ pour amorcer le ping-pong\n    while (1)\n        pause();\n}\n\nstatic void handler_fils(int unused, siginfo_t * info, void * unused_ptr)\n{\n    union sigval value;\n    struct timeval heure;\n\n    <strong>gettimeofday<\/strong>(&amp; heure, NULL);\n    value.sival_int = heure.tv_usec;\n    <strong>sigqueue<\/strong>(getppid(), SIGRTMIN, value);\n}\n\nstatic void processus_pere(void)\n{\n    struct sigaction action;\n    action.sa_flags = SA_SIGINFO;\n    action.sa_sigaction = handler_pere;\n    sigfillset(&amp; action.sa_mask);\n    sigaction(SIGRTMIN, &amp; action, NULL);\n    while (1)\n        pause();\n}\n\nstatic void handler_pere(int unused, siginfo_t * info, void * unused_ptr)\n{\n    long int duree;\n    static long int duree_max = 0;\n    static long int duree_min = -1;\n    static long int somme_durees = 0;\n    static long int nb_recus = 0;\n    static struct timeval debut = { 0, 0};\n    struct timeval heure;\n\n    <strong>gettimeofday<\/strong>(&amp; heure, NULL);\n    duree = heure.tv_usec - info-&gt;si_value.sival_int;\n    if (duree &lt; 0)\n        duree += 1000000;\n\n    if (duree_max &lt; duree)\n        duree_max = duree;\n    if ((duree_min == -1) || (duree_min &gt; duree))\n        duree_min = duree;\n    somme_durees += duree;\n\n    nb_recus ++;\n    if (heure.tv_sec != debut.tv_sec) {\n        fprintf(stdout, \"min =%3ld   max =%3ld  recus = %6ld  moy=%5.1fn\",\n            duree_min, duree_max, nb_recus, ((float) somme_durees) \/ nb_recus);\n        nb_recus = 0;\n        duree_min = -1;\n        duree_max = 0;\n        somme_durees = 0;\n        debut = heure;\n    }\n    <strong>sigqueue<\/strong>(info-&gt;si_pid, SIGRTMIN, (const union sigval) 0);\n}<\/pre>\n<p style=\"text-align: justify;\">Les r\u00e9sultats sont int\u00e9ressants&nbsp;:<\/p>\n<pre>$ <strong>.\/transfert-signaux-02<\/strong>\nmin = 22   max = 22  recus =      1  moy= 22.0\nmin =  3   max = 12  recus =  88491  moy=  3.9\nmin =  3   max = 15  recus = 120337  moy=  4.1\nmin =  3   max = 15  recus = 120674  moy=  4.0\nmin =  3   max = 15  recus = 120715  moy=  4.0\nmin =  3   max = 18  recus = 121055  moy=  4.0\nmin =  3   max = 16  recus = 120594  moy=  4.1\nmin =  3   max = 13  recus = 120779  moy=  4.0\nmin =  3   max = 12  recus = 120779  moy=  4.0\nmin =  3   max = 20  recus = 121118  moy=  4.0\nmin =  3   max = 17  recus = 121028  moy=  4.0\nmin =  3   max = 20  recus = 121122  moy=  4.0\nmin =  3   max = 20  recus = 120719  moy=  4.0\nmin =  3   max = 23  recus = 121064  moy=  4.0\nmin =  3   max = 15  recus = 120781  moy=  4.0\nmin =  3   max = 15  recus = 121103  moy=  4.0\nmin =  3   max = 16  recus = 120611  moy=  4.0\nmin =  3   max = 41  recus = 120611  moy=  4.0\nmin =  3   max = 34  recus = 122327  moy=  4.0\n<strong><em>(Contr\u00f4le-C)<\/em><\/strong>\n$<\/pre>\n<p style=\"text-align: justify;\">Nous voyons que lorsque le processeur reste \u00e9veill\u00e9, le temps de passage d&rsquo;un signal d&rsquo;un processus \u00e0 l&rsquo;autre est tr\u00e8s rapide, en moyenne 4 microsecondes&nbsp;!<\/p>\n<h1>Conclusion<\/h1>\n<p style=\"text-align: justify;\">La quantit\u00e9 d&rsquo;information transport\u00e9e par les signaux temps-r\u00e9el est tr\u00e8s l\u00e9g\u00e8re (un entier), mais cela peut suffire dans certains cas. L&rsquo;int\u00e9r\u00eat est \u00e9galement la notion de notification asynchrone qu&rsquo;induisent par d\u00e9finition les signaux. Le processus cible peut \u00eatre en train de r\u00e9aliser des calculs, il sera averti dans un d\u00e9lai bref de l&rsquo;occurrence d&rsquo;un \u00e9v\u00e9nement par un autre processus. Il existe encore d&rsquo;autres m\u00e9canismes <em>IPC<\/em>, que nous examinerons prochainement.<\/p>","protected":false},"excerpt":{"rendered":"<p>Nous avions examin&eacute; dans l&rsquo;article pr&eacute;c&eacute;dent de cette s&eacute;rie un premier m&eacute;canisme de communication entre processus&nbsp;: les files de messages Posix. Nous avions observ&eacute; qu&rsquo;il &eacute;tait facile de transmettre un message de quelques kilo-octets entre deux processus, en un temps allant de 8 microsecondes si le processeur &eacute;tait d&eacute;j&agrave; actif &agrave; 15 microsecondes si les [&hellip;]<\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8,14],"tags":[],"class_list":["post-1171","post","type-post","status-publish","format-standard","hentry","category-linux-2","category-temps-reel"],"_links":{"self":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts\/1171","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/comments?post=1171"}],"version-history":[{"count":0,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts\/1171\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/media?parent=1171"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/categories?post=1171"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/tags?post=1171"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}