{"id":2093,"date":"2012-05-07T09:00:53","date_gmt":"2012-05-07T08:00:53","guid":{"rendered":"http:\/\/www.blaess.fr\/christophe\/?p=2093"},"modified":"2012-05-07T09:00:53","modified_gmt":"2012-05-07T08:00:53","slug":"mesure-de-precision-des-timers-de-rtdm-xenomai","status":"publish","type":"post","link":"https:\/\/www.blaess.fr\/christophe\/2012\/05\/07\/mesure-de-precision-des-timers-de-rtdm-xenomai\/","title":{"rendered":"Mesure de pr\u00e9cision des timers de RTDM \/ Xenomai"},"content":{"rendered":"<p style=\"text-align: justify;\">Cette petite exp\u00e9rience va nous permettre de mesurer la pr\u00e9cision des timers kernel programm\u00e9s en utilisant l&rsquo;API <strong>RTDM<\/strong> (<em>Real Time Driver Model<\/em>) propos\u00e9e par Xenomai dans sa version 2.6.0. Il est important de noter que RTDM est une sp\u00e9cification pour le d\u00e9veloppement de drivers temps-r\u00e9el pour Linux qui pourra prochainement \u00eatre utilis\u00e9e directement avec le noyau \u00ab\u00a0patch\u00e9\u00a0\u00bb <em>Linux-rt<\/em>. Il est donc utile de commencer \u00e0 s&rsquo;int\u00e9resser \u00e0 cette API pour tout les d\u00e9veloppements kernel ayant sp\u00e9cifiquement trait au temps r\u00e9el.<\/p>\n<p>\n<!--more-->\n<\/p>\n<h1>Module dans l&rsquo;espace noyau<\/h1>\n<p style=\"text-align: justify;\">Notre module va installer un timer avec une p\u00e9riode d&rsquo;une milliseconde. A chaque d\u00e9clenchement, le timer mesurera l&rsquo;\u00e9cart par rapport au d\u00e9clenchement pr\u00e9c\u00e9dent et proposera ses r\u00e9sultats dans une interface RTDM accessible depuis l&rsquo;espace utilisateur.<\/p>\n<p style=\"text-align: justify;\">Voici un exemple d&rsquo;impl\u00e9mentation, les fichiers sources se trouvent dans <a title=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2012-05-07\/mesure-periode-rtdm.tar.bz2\" href=\"http:\/\/www.blaess.fr\/christophe\/files\/article-2012-05-07\/mesure-periode-rtdm.tar.bz2\">cette archive<\/a>.<\/p>\n<pre><strong>mesure-periode-rtdm.c: <\/strong>\n#include &lt;linux\/version.h&gt;\n#include &lt;linux\/device.h&gt;\n#include &lt;linux\/module.h&gt;\n#include &lt;linux\/sched.h&gt;\n#include &lt;linux\/cdev.h&gt;\n#include &lt;linux\/fs.h&gt;\n\n#include &lt;asm\/uaccess.h&gt;\n\n#include &lt;rtdm\/rtdm_driver.h&gt;\n\n#define nom_device_rtdm \"mesure_periode_rtdm\"\n\nstatic int read_mesure_periode(struct rtdm_dev_context * contexte, rtdm_user_info_t * info, void * buffer, size_t lg);\nstatic int open_nonrt_mesure_periode (struct rtdm_dev_context * contexte, rtdm_user_info_t * info, int flags);\nstatic int close_nonrt_mesure_periode (struct rtdm_dev_context * contexte, rtdm_user_info_t * info);\nstatic void handler_mesure_periode (rtdm_timer_t * timer);\n\nstatic <strong>struct rtdm_device<\/strong> rtdev_mesure_periode = {\n    .struct_version = RTDM_DEVICE_STRUCT_VER,\n    .device_flags   = RTDM_NAMED_DEVICE,\n    .context_size   = 0,\n    .device_name    = nom_device_rtdm,\n    .open_nrt       = open_nonrt_mesure_periode,\n    .ops = {\n        .close_nrt = close_nonrt_mesure_periode,\n        .read_nrt  = read_mesure_periode,\n    },\n    .device_class     = RTDM_CLASS_TESTING,\n    .device_sub_class = 1,\n    .profile_version  = 1,\n    .driver_name      = nom_device_rtdm,\n    .driver_version   = RTDM_DRIVER_VER(1,0,0),\n    .peripheral_name  = nom_device_rtdm,\n    .provider_name    = \"cpb\",\n    .proc_name        = nom_device_rtdm,\n};\n\nstatic nanosecs_abs_t  periode_mini;\nstatic nanosecs_abs_t  periode_maxi;\nstatic nanosecs_abs_t  somme_periodes;\nstatic int             nb_periodes;\nstatic rtdm_mutex_t mtx_statistiques;\n\nstatic rtdm_timer_t rtimer;\nstatic nanosecs_rel_t periode_desiree = 1000000; \/\/ 1 ms\n\nstatic int __init <strong>init_mesure_periode<\/strong> (void)\n{\n    int err;\n\n    <strong>rtdm_mutex_init<\/strong>(&amp; mtx_statistiques);\n\n    periode_mini = -1;\n    periode_maxi = 0;\n    somme_periodes = 0;\n    nb_periodes = 0;\n\n    <strong>rtdm_dev_register<\/strong>(&amp; rtdev_mesure_periode);\n    if ((err = <strong>rtdm_timer_init<\/strong>(&amp; rtimer, handler_mesure_periode, \"Timer_mesure_periode\")) != 0) {\n        rtdm_dev_unregister(&amp; rtdev_mesure_periode, 0);\n        rtdm_mutex_destroy(&amp; mtx_statistiques);\n        return err;\n    }\n\n    if ((err = <strong>rtdm_timer_start<\/strong>(&amp; rtimer, 1000000000, periode_desiree, RTDM_TIMERMODE_RELATIVE)) != 0) {\n        rtdm_timer_destroy(&amp; rtimer);\n        rtdm_dev_unregister(&amp; rtdev_mesure_periode, 0);\n        rtdm_mutex_destroy(&amp; mtx_statistiques);\n        return err;\n    }\n\n    return 0;\n}\n\nstatic void __exit <strong>exit_mesure_periode<\/strong>(void)\n{\n    <strong>rtdm_timer_stop<\/strong>(&amp; rtimer);\n    <strong>rtdm_timer_destroy<\/strong>(&amp; rtimer);\n    <strong>rtdm_dev_unregister<\/strong>(&amp; rtdev_mesure_periode, 0);\n    <strong>rtdm_mutex_destroy<\/strong>(&amp; mtx_statistiques);\n}\n\nstatic int <strong>read_mesure_periode<\/strong>(struct rtdm_dev_context * contexte, rtdm_user_info_t * info, void * buffer, size_t lg)\n{\n    char local_buffer[80];\n    nanosecs_abs_t  _periode_mini;\n    nanosecs_abs_t  _periode_maxi;\n    nanosecs_abs_t  _somme_periodes;\n    int             _nb_periodes;\n\n    <strong>rtdm_mutex_lock<\/strong>(&amp; mtx_statistiques);\n    _periode_mini   = periode_mini;\n    _periode_maxi   = periode_maxi;\n    _somme_periodes = somme_periodes;\n    _nb_periodes    = nb_periodes;\n    <strong>rtdm_mutex_unlock<\/strong>(&amp; mtx_statistiques);\n\n    snprintf(local_buffer, 80, \"%lld %lld %lld %dn\",\n        _periode_mini, _periode_maxi, _somme_periodes, _nb_periodes);\n    if (<strong>rtdm_safe_copy_to_user<\/strong>(info, buffer, local_buffer, strlen(local_buffer)) != 0)\n        return -EFAULT;\n    return strlen(local_buffer);\n}\n\nstatic int <strong>open_nonrt_mesure_periode<\/strong>(struct rtdm_dev_context * contexte, rtdm_user_info_t * info, int flags)\n{\n    return 0;\n}\n\nstatic int <strong>close_nonrt_mesure_periode<\/strong>(struct rtdm_dev_context * contexte, rtdm_user_info_t * info)\n{\n    return 0;\n}\n\nstatic void <strong>handler_mesure_periode<\/strong> (rtdm_timer_t * timer)\n{\n    nanosecs_abs_t         heure;\n    static nanosecs_abs_t  precedente;\n    nanosecs_abs_t         periode;\n\n    heure = <strong>rtdm_clock_read<\/strong>();\n    <strong>rtdm_mutex_lock<\/strong>(&amp; mtx_statistiques);\n    if (nb_periodes &gt; 0) {\n        periode = heure - precedente;\n        if (periode &gt; periode_maxi)\n            periode_maxi = periode;\n        if ((periode_mini == -1) || (periode_mini &gt; periode))\n            periode_mini = periode;\n        somme_periodes += periode;\n    }\n    nb_periodes ++;\n    <strong>rtdm_mutex_unlock<\/strong>(&amp; mtx_statistiques);\n    precedente = heure;\n}\n\n<strong>module_init<\/strong>(init_mesure_periode);\n<strong>module_exit<\/strong>(exit_mesure_periode);\nMODULE_LICENSE(\"GPL\");<\/pre>\n<p style=\"text-align: justify;\">Compilons ce module, chargeons-le et v\u00e9rifions qu&rsquo;il soit bien enregistr\u00e9. Naturellement, il est n\u00e9cessaire d&rsquo;avoir install\u00e9 un noyau Linux\/Xenomai comme d\u00e9crit dans <a title=\"Xenomai 2.6.0 sur Ubuntu 12.04\" href=\"http:\/\/www.blaess.fr\/christophe\/2012\/05\/02\/xenomai-2-6-0-sur-ubuntu-12-04\/\" target=\"_blank\">cet article<\/a> ou <a title=\"Xenomai sur Pandaboard\" href=\"http:\/\/www.blaess.fr\/christophe\/2011\/11\/27\/xenomai-sur-pandaboard\/\" target=\"_blank\">celui-ci<\/a>.<\/p>\n<pre># <strong>make<\/strong>\nmake -C \/lib\/modules\/2.6.38.8-xenomai\/build SUBDIRS=\/home\/cpb\/Documents\/Livres\/Articles\/Blog\/article-2012-05-07  modules\nmake[1]: entrant dans le r\u00e9pertoire \u00ab \/usr\/local\/src\/linux-2.6.38.8-xenomai \u00bb\n  CC [M]  \/home\/cpb\/Documents\/Livres\/Articles\/Blog\/article-2012-05-07\/mesure-periode-rtdm.o\n  Building modules, stage 2.\n  MODPOST 1 modules\n  CC      \/home\/cpb\/Documents\/Livres\/Articles\/Blog\/article-2012-05-07\/mesure-periode-rtdm.mod.o\n  LD [M]  \/home\/cpb\/Documents\/Livres\/Articles\/Blog\/article-2012-05-07\/mesure-periode-rtdm.ko\nmake[1]: quittant le r\u00e9pertoire \u00ab \/usr\/local\/src\/linux-2.6.38.8-xenomai \u00bb\ngcc -I\/usr\/xenomai\/include -D_GNU_SOURCE -D_REENTRANT -Wall -Werror-implicit-function-declaration -pipe -D__XENO__ -lrtdm -L\/usr\/xenomai\/lib -lxenomai -lpthread -lrt -L \/usr\/xenomai\/lib -o mesure-periode mesure-periode.c -lrtdm -lxenomai\n# <strong>insmod .\/mesure-periode-rtdm.ko <\/strong>\n# <strong>cat \/proc\/xenomai\/rtdm\/named_devices<\/strong>\nHash\tName\t\t\t\tDriver\t\t\/proc\n24\trttest-timerbench0             \txeno_timerbench\trttest-timerbench0\n55\trttest-switchtest0             \txeno_switchtest\trttest-switchtest0\nEE\tmesure_periode_rtdm            \tmesure_periode_rtdm\tmesure_periode_rtdm\n#<\/pre>\n<h1>Application dans l&rsquo;espace utilisateur<\/h1>\n<p style=\"text-align: justify;\">Contrairement aux drivers Linux habituels, RTDM ne fournit pas de fichier sp\u00e9cial (avec un type, un num\u00e9ro majeur et un num\u00e9ro mineur) pour acc\u00e9der aux fonctionnalit\u00e9s impl\u00e9ment\u00e9s. Le driver n&rsquo;est pas accessible par un appel <code>open()<\/code> sur un fichier de <code>\/dev<\/code> mais par une op\u00e9ration sp\u00e9cifique <code>rt_dev_open()<\/code> employant le nom du p\u00e9riph\u00e9rique.<\/p>\n<p style=\"text-align: justify;\">Voici un petit utilitaire qui ouvre le p\u00e9riph\u00e9rique <code>mesure_periode_rtdm<\/code> et lit les valeurs. Il affiche sur sa sortie standard les informations obtenues.<\/p>\n<pre><strong>mesure-periode.c: <\/strong>\n#include &lt;fcntl.h&gt;\n#include &lt;stdlib.h&gt;\n#include &lt;stdio.h&gt;\n#include &lt;string.h&gt;\n#include &lt;unistd.h&gt;\n\n#include &lt;rtdm\/rtdm.h&gt;\n\n#define LG_BUFFER 80\n\nint main (int argc, char * argv[])\n{\n    int fd;\n    int i;\n    char buffer[LG_BUFFER];\n    long long int min, max, somme;\n    int nb;\n    fd = <strong>rt_dev_open<\/strong>(\"mesure_periode_rtdm\", O_RDONLY);\n    if (fd &lt; 0) {\n        fprintf(stderr, \"%s: %sn\", argv[1], strerror(-fd));\n        exit(EXIT_FAILURE);\n    }\n\n    while ((i = <strong>rt_dev_read<\/strong>(fd, buffer, LG_BUFFER)) &gt; 0) {\n        buffer[i] ='\u0000';\n        if (sscanf(buffer, \"%lld %lld %lld %d\", &amp;min, &amp;max, &amp;somme, &amp;nb) == 4)\n            printf(\"min = %lld, max = %lld, moy = %lldn\", min, max, somme \/ nb);\n    }\n    <strong>rt_dev_close<\/strong>(fd);\n    return EXIT_SUCCESS;\n}<\/pre>\n<p style=\"text-align: justify;\">Nous pouvons lancer le programme, il affichera sur sa sortie standard les valeurs statistiques obtenues.<\/p>\n<pre># <strong>export LD_LIBRARY_PATH=\/usr\/xenomai\/lib\/<\/strong>\n# <strong>.\/mesure-periode<\/strong>\n[...]\nmin = 968234, max = 1036666, moy = 999995\nmin = 968234, max = 1036666, moy = 999995\nmin = 968234, max = 1036666, moy = 999995\nmin = 968234, max = 1036666, moy = 999995\nmin = 968234, max = 1036666, moy = 999995\nmin = 968234, max = 1036666, moy = 999995\nmin = 968234, max = 1036666, moy = 999995\nmin = 968234, max = 1036666, moy = 999995\nmin = 968234, max = 1036666, moy = 999995\n[...]\n#<\/pre>\n<h1>R\u00e9sultats<\/h1>\n<p style=\"text-align: justify;\">Sur ce poste, nous voyons que le timer \u00e0 1kHz a subi des fluctuations maximales de 37 microsecondes environ, apr\u00e8s un quart d&rsquo;heure de fonctionnement. La machine n&rsquo;\u00e9tait pas excessivement charg\u00e9e, ni en interruptions ni en processus. Avec une charge beaucoup plus \u00e9lev\u00e9e, on pourrait probablement s&rsquo;attendre \u00e0 des fluctuations approchant la centaine de microsecondes.<\/p>\n<p style=\"text-align: justify;\">Cette ex\u00e9cution s&rsquo;est faite sur un poste de bureautique d&rsquo;entr\u00e9e de gamme, qui n&rsquo;est donc pas particuli\u00e8rement con\u00e7u pour le temps r\u00e9el (notamment dans la gestion des interruptions SMI). Avec un PC industriel et un noyau bien configur\u00e9, j&rsquo;ai d\u00e9j\u00e0 observ\u00e9 des fluctuations inf\u00e9rieures \u00e0 35 microsecondes sur deux jours alors que la machine \u00e9tait tr\u00e8s fortement charg\u00e9e, tant en activit\u00e9 CPU (processus, appel-syst\u00e8me) qu&rsquo;en interruptions diverses.<\/p>\n<p style=\"text-align: justify;\">Si vous obtenez des r\u00e9sultats int\u00e9ressants avec cette exp\u00e9rience, communiquez-les moi et je serai heureux de les faire figurer ici.<\/p>","protected":false},"excerpt":{"rendered":"<p>Cette petite exp&eacute;rience va nous permettre de mesurer la pr&eacute;cision des timers kernel programm&eacute;s en utilisant l&rsquo;API RTDM (Real Time Driver Model) propos&eacute;e par Xenomai dans sa version 2.6.0. Il est important de noter que RTDM est une sp&eacute;cification pour le d&eacute;veloppement de drivers temps-r&eacute;el pour Linux qui pourra prochainement &ecirc;tre utilis&eacute;e directement avec le [&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-2093","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\/2093","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=2093"}],"version-history":[{"count":0,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/posts\/2093\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/media?parent=2093"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/categories?post=2093"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.blaess.fr\/christophe\/wp-json\/wp\/v2\/tags?post=2093"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}