Question

J'ai besoin d’un tick tick avec une résolution de 1ms sous linux. Il est utilisé pour incrémenter une valeur de temporisation qui est utilisée pour voir si différents événements doivent être déclenchés. POSIX timerfd_create n'est pas une option en raison de l'exigence glibc. J'ai essayé timer_create et timer_settimer, mais le meilleur que je tire d'eux est une résolution de 10 ms, les valeurs plus petites semblent prendre la résolution 10 ms par défaut. Getittimer et setitimer ont une résolution de 10 ms selon la page de manuel.

La seule façon de faire ce minuteur auquel je peux penser actuellement est d’utiliser clock_gettime avec CLOCK_MONOTONIC dans ma boucle principale, un test si un ms est passé et, le cas échéant, augmenter le compteur (puis vérifier si les différents événements doivent se déclencher). ).

Existe-t-il un meilleur moyen de procéder que d'interroger constamment la boucle principale? Quelle est la solution recommandée?

Le langage que j'utilise est tout à fait vieux c

Mettre à jour
J'utilise un noyau 2.6.26. Je sais que vous pouvez l’interrompre à 1 kHz et que les fonctions POSIX timer * peuvent être programmées à 1 ms, mais cela ne semble pas fiable et je ne veux pas l’utiliser, car il se peut qu’il ait besoin d’un nouveau noyau Systèmes. Certains noyaux de stock semblent toujours avoir la configuration 100Hz. Et j'aurais besoin de détecter cela. L’application peut être exécutée sur autre chose que mon système:)

Je ne peux pas dormir pendant 1 ms car il peut y avoir des événements de réseau auxquels je dois réagir.

Comment j'ai résolu le problème Comme ce n’est pas si important, j’ai simplement déclaré que la minuterie globale avait une résolution de 100 ms. Tous les événements utilisant leur propre minuteur doivent définir au moins 100 ms pour l'expiration du minuteur. Je me demandais plus ou moins s'il y aurait un meilleur moyen, d'où la question.

Pourquoi j'ai accepté la réponse Je pense que la réponse de freespace décrit le mieux pourquoi ce n’est pas vraiment possible sans un système Linux en temps réel.

Était-ce utile?

La solution

La scrutation dans la boucle principale n’est pas non plus une réponse: votre processus risque de ne pas prendre beaucoup de temps CPU. Par conséquent, plus de 10 ms s’écouleront avant que votre code ne soit exécuté, ce qui le rend inutilisable.

10ms concerne la résolution standard du minuteur pour la plupart des systèmes d'exploitation en temps réel (RTOS). ). Mais il n’est pas utile dans un système autre que le RTOS: le comportement du planificateur et du répartiteur va grandement influer sur la rapidité avec laquelle vous pouvez répondre à l’expiration d’un chronomètre. Par exemple, même si vous avez un minuteur de résolution inférieur à 10 ms, vous ne pouvez pas répondre à l'expiration du minuteur si votre code n'est pas en cours d'exécution. Étant donné que vous ne pouvez pas prédire quand votre code va être exécuté, vous ne pouvez pas répondre à l'expiration de la minuterie avec précision.

Il y a bien sûr des noyaux Linux en temps réel, voir http://www.linuxdevices.com/articles /AT8073314981.html pour une liste. Un RTOS offre des fonctionnalités qui vous permettent d’obtenir des garanties logicielles ou matérielles quant au moment où votre code va être exécuté. C’est à peu près le seul moyen de réagir de manière fiable et précise aux minuteries expirant, etc.

Autres conseils

Pour obtenir des minuteries de résolution de 1 ms, faites ce que libevent fait.

Organisez vos minuteurs dans un min-heap , c'est-à-dire que le haut du tas est le minuteur avec l'heure d'expiration (absolue) la plus proche (un arbre de type rb fonctionnerait également, mais avec davantage de temps système). Avant d'appeler , sélectionnez () ou epoll () dans votre boucle d'événements principale, calculez le delta en millisecondes entre l'heure d'expiration du minuteur le plus ancien et l'heure actuelle. Utilisez ce delta comme délai d'attente pour select () . Les délais select () et epoll () ont une résolution de 1ms.

J'ai un test de résolution du minuteur qui utilise le mécanisme expliqué ci-dessus (mais pas libevent). Le test mesure la différence entre le délai d'expiration souhaité pour le minuteur et son expiration réelle de minuteries de 1, 5 et 10 ms:

1000 deviation samples of  1msec timer: min=  -246115nsec max=  1143471nsec median=   -70775nsec avg=      901nsec stddev=    45570nsec
1000 deviation samples of  5msec timer: min=  -265280nsec max=   256260nsec median=  -252363nsec avg=     -195nsec stddev=    30933nsec
1000 deviation samples of 10msec timer: min=  -273119nsec max=   274045nsec median=   103471nsec avg=     -179nsec stddev=    31228nsec
1000 deviation samples of  1msec timer: min=  -144930nsec max=  1052379nsec median=  -109322nsec avg=     1000nsec stddev=    43545nsec
1000 deviation samples of  5msec timer: min= -1229446nsec max=  1230399nsec median=  1222761nsec avg=      724nsec stddev=   254466nsec
1000 deviation samples of 10msec timer: min= -1227580nsec max=  1227734nsec median=    47328nsec avg=      745nsec stddev=   173834nsec
1000 deviation samples of  1msec timer: min=  -222672nsec max=   228907nsec median=    63635nsec avg=       22nsec stddev=    29410nsec
1000 deviation samples of  5msec timer: min= -1302808nsec max=  1270006nsec median=  1251949nsec avg=     -222nsec stddev=   345944nsec
1000 deviation samples of 10msec timer: min= -1297724nsec max=  1298269nsec median=  1254351nsec avg=     -225nsec stddev=   374717nsec

Le test s’est déroulé comme un processus en temps réel sur le noyau 2.6.34 de Fedora 13. La meilleure précision obtenue avec le temporisateur de 1 ms était avg = 22nsec stddev = 29410nsec.

Je ne suis pas sûr que ce soit la meilleure solution, mais vous pouvez envisager d'écrire un petit module de noyau utilisant les minuteries haute résolution du noyau pour effectuer le chronométrage. Fondamentalement, vous créez un fichier de périphérique pour lequel les lectures ne renvoient que par intervalles de 1 ms.

Un exemple de ce type d’approche est utilisé dans le PBX Asterisk, via le module ztdummy. Si vous recherchez Google pour ztdummy, vous pouvez trouver le code qui le fait.

Je pense que vous aurez du mal à atteindre une précision de 1 ms avec Linux standard même avec une interrogation constante dans la boucle principale, car le noyau ne garantit pas que votre application obtiendra constamment le processeur. Par exemple, vous pouvez vous endormir pendant des dizaines de millisecondes à cause du multitâche préventif et vous ne pouvez rien y faire.

Vous voudrez peut-être examiner Linux en temps réel .

Si vous ciblez la plate-forme x86, vous devez vérifier les minuteries HPET. C'est une minuterie matérielle avec une grande précision. Il doit être supporté par votre mère (à présent, ils le supportent tous) et votre noyau devrait également contenir le pilote correspondant. Je l’ai utilisé quelques fois sans aucun problème et j’ai pu obtenir une résolution bien supérieure à 1 ms.

Voici une documentation et des exemples:

Il semble que je me souvienne d'avoir obtenu des résultats corrects avec les sondages basés sur gettimeofday / usleep - je n'avais pas besoin de 1000 minuteries par seconde, mais j'avais besoin d'une bonne précision du timing des ticks dont j'avais besoin - mon Contrôleur de boîte à rythmes MIDI, et je crois me rappeler une précision inférieure à la milliseconde, indispensable pour une boîte à rythmes si vous ne voulez pas que cela sonne comme un très mauvais batteur (en particulier en comptant les latences intégrées du MIDI) - iirc (C'était en 2005, ma mémoire est un peu floue.) J'obtenais moins de 200 microsecondes de temps cible.

Cependant, je n’exécutais pas beaucoup d’autres choses sur le système. Si vous avez un environnement contrôlé, vous pourrez peut-être vous en sortir avec une telle solution. Si le système ne se limite plus (observez le lancement de cron mis à jourb, etc.), les choses risquent de s'effondrer.

Utilisez-vous un noyau Linux 2.4?

Extrait de l'article n ° 1420 de la base de connaissances VMware ( http://kb.vmware.com/kb/1420 ).

  

Les systèmes d'exploitation invités Linux conservent   temps en comptant les interruptions de minuterie.   Noyaux 2.4 non corrigés et antérieurs   programmer le temporisateur de système virtuel pour   demande des interruptions d'horloge à 100Hz (100   interruptions par seconde). 2.6 noyaux,   d'autre part, demande d'interruption   à 1000Hz - dix fois plus souvent. Certains   Les noyaux 2.4 modifiés par les fournisseurs de distribution pour contenir les fonctionnalités 2.6   demande des interruptions 1000Hz, ou dans certains   cas, interrompt à d'autres taux, tels que   comme 512Hz.

Tout d’abord, récupérez la source du noyau et compilez-la avec un paramètre HZ ajusté.

  • Si HZ = 1000 , le chronomètre s'interrompt 1000 fois par seconde. Vous pouvez utiliser HZ = 1000 pour une machine i386.
  • Sur une machine intégrée, HZ peut être limité à 100 ou 200.

Pour un bon fonctionnement, l'option PREEMPT_KERNEL doit être activée. Il y a les noyaux qui ne supportent pas cette option correctement. Vous pouvez les vérifier par recherche.

Les noyaux récents, c’est-à-dire 2.6.35.10, supportent les options NO_HZ, qui deviennent sur les ticks dynamiques. Cela signifie qu’il n’y aura pas de tics de minuterie en veille, mais une coche de minuterie sera générée au moment spécifié.

Le noyau contient un correctif RT, mais le support matériel est très limité.

En général, RTAI est une solution tout à fait meurtrière à votre problème, mais sa le support matériel est très limité. Cependant, de bons contrôleurs CNC, comme emc2, utilisez RTAI pour leur cadencement, peut-être 5000 Hz, mais cela peut être beaucoup de travail pour l’installer.

Si vous le pouvez, vous pouvez ajouter du matériel pour générer des impulsions. Cela ferait un système qui peut être adapté à n'importe quelle version du système d'exploitation.

Pouvez-vous au moins utiliser nanosleep dans votre boucle pour dormir pendant 1 ms? Ou est-ce une chose glibc?

Mise à jour: Peu importe, je vois dans la page de manuel "Cela peut prendre jusqu'à 10 ms de plus que spécifié jusqu'à ce que le processus soit à nouveau exécutable"

.

Vous n'avez pas besoin d'un RTOS pour une application simple en temps réel. Tous les processeurs modernes ont des minuteries à usage général. Obtenez une fiche technique pour le processeur cible sur lequel vous travaillez. Recherchez dans la source du noyau, sous le répertoire arch, vous trouverez une source spécifique au processeur pour savoir comment gérer ces minuteries.

Il existe deux approches que vous pouvez adopter avec ceci:

1) Votre application exécute UNIQUEMENT votre machine à états et rien d’autre. Linux est simplement votre " chargeur de démarrage. & Quot; Créez un objet de noyau qui installe un périphérique de caractère. Lors de l’insertion dans le noyau, configurez votre minuterie GP pour une exécution continue. Vous connaissez la fréquence à laquelle il fonctionne. Désormais, dans le noyau, désactivez explicitement votre chien de garde. Désactivez maintenant les interruptions (matériel ET logiciel) Sur un noyau Linux à un seul processeur, l'appel de spin_lock () accomplira cette opération (ne le lâchez jamais). Le processeur est le vôtre. Boucle occupée, vérifiant la valeur du TPG jusqu'à ce que le nombre requis de ticks soit passé, quand ils l'ont, définissez une valeur pour le prochain délai d'attente et entrez votre boucle de traitement. Assurez-vous simplement que le temps de rafale de votre code est inférieur à 1 ms

2) Une 2ème option. Cela suppose que vous utilisez un noyau Linux préemptif. Configurez un GPT non utilisé à côté de votre système d'exploitation. Maintenant, configurez une interruption pour déclencher une marge configurable AVANT que le délai d’exécution de 1 ms survienne (par exemple, 50 à 75 USD). Lorsque l’interruption se déclenche, vous désactivez immédiatement les interruptions et attendez que la fenêtre de 1 ms se produise, puis entrez dans votre machine d’état, puis permettant des interruptions sur votre attente OUT. Cela explique le fait que vous coopérez avec AUTRE élément du noyau qui désactive les interruptions. Cela suppose qu’aucune autre activité du noyau ne bloque les interruptions pendant longtemps (plus de 100 us.). Maintenant, vous pouvez MESURER la précision de votre événement de tir et agrandir la fenêtre jusqu’à ce qu’il réponde à vos besoins.

Si vous essayez plutôt d'apprendre comment fonctionne le RTOS ... ou si vous essayez de résoudre un problème de contrôle avec plus d'une responsabilité en temps réel ..., utilisez un RTOS.

Qu'en est-il de l'utilisation de "/ dev / rtc0"? (ou "/ dev / rtc") et son interface ioctl () associée? Je pense qu'il offre un compteur de minuterie précis. Il n’est pas possible de régler le débit sur 1 ms, mais sur une valeur proche ou 1 / 1024sec (1024Hz), ou sur une fréquence plus élevée, telle que 8192Hz.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top