Quelqu'un peut-il m'expliquer où exactement setjmp()
et les longjmp()
fonctions peuvent être utilisées pratiquement dans la programmation embarquée? Je sais que ce sont pour la gestion des erreurs. Mais j'aimerais connaître quelques cas d'utilisation.
97
longjmp()
pour sortir d'un gestionnaire de signaux, en particulier des choses comme unBUS ERROR
. Ce signal ne peut généralement pas redémarrer. Une application embarquée peut souhaiter gérer ce cas pour la sécurité et un fonctionnement robuste.setjmp
entre BSD et Linux, consultez «Timing setjmp, and the Joy of Standards» , qui suggère d'utilisersigsetjmp
.Réponses:
Gestion des erreurs
Supposons qu'il y ait une erreur au fond d'une fonction imbriquée dans de nombreuses autres fonctions et que la gestion des erreurs n'a de sens que dans la fonction de niveau supérieur.
Ce serait très fastidieux et gênant si toutes les fonctions intermédiaires devaient retourner normalement et évaluer les valeurs de retour ou une variable d'erreur globale pour déterminer qu'un traitement ultérieur n'a pas de sens ou même serait mauvais.
C'est une situation où setjmp / longjmp a du sens. Ces situations sont similaires à celles où l'exception dans d'autres langages (C ++, Java) a du sens.
Coroutines
Outre la gestion des erreurs, je peux également penser à une autre situation où vous avez besoin de setjmp / longjmp en C:
C'est le cas lorsque vous devez implémenter des coroutines .
Voici un petit exemple de démonstration. J'espère que cela satisfait la demande de Sivaprasad Palas pour un exemple de code et répond à la question de TheBlastOne comment setjmp / longjmp prend en charge l'implémentation de corroutines (autant que je vois, il ne repose sur aucun comportement non standard ou nouveau).
EDIT:
Il se peut que ce soit en fait un comportement indéfini pour faire un
longjmp
down the callstack (voir le commentaire de MikeMB; bien que je n'ai pas encore eu l'occasion de le vérifier).La figure suivante montre le déroulement de l'exécution:
Note d'avertissement
Lorsque vous utilisez setjmp / longjmp, sachez qu'ils ont un effet sur la validité des variables locales souvent non prises en compte.
Cf. ma question sur ce sujet .
la source
setjmp
avant vouslongjmp
. Ce n'est pas standard.routineA
etroutineB
utiliser la même pile, cela ne fonctionne que pour les coroutines très primitives. SiroutineA
appelle un profondément imbriquéroutineC
après le premier appel àroutineB
et que celaroutineC
s'exécuteroutineB
comme coroutine, alorsroutineB
peut même détruire la pile de retour (pas seulement les variables locales) deroutineC
. Donc, sans allouer une pile exclusive (alloca()
après avoir appelérountineB
?), Vous aurez de sérieux problèmes avec cet exemple s'il est utilisé comme recette.La théorie est que vous pouvez les utiliser pour la gestion des erreurs afin de pouvoir sortir d'une chaîne d'appels profondément imbriquée sans avoir à gérer les erreurs de gestion dans chaque fonction de la chaîne.
Comme toute théorie intelligente, cela s'effondre en rencontrant la réalité. Vos fonctions intermédiaires alloueront de la mémoire, saisiront les verrous, ouvriront des fichiers et feront toutes sortes de choses qui nécessitent un nettoyage. Donc dans la pratique
setjmp
/longjmp
sont généralement une mauvaise idée sauf dans des circonstances très limitées où vous avez un contrôle total sur votre environnement (certaines plates-formes embarquées).D'après mon expérience, dans la plupart des cas, chaque fois que vous pensez que l'utilisation de
setjmp
/longjmp
fonctionnerait, votre programme est suffisamment clair et simple pour que chaque appel de fonction intermédiaire dans la chaîne d'appels puisse gérer les erreurs, ou il est si compliqué et impossible à résoudre que vous devriez le faireexit
lorsque vous rencontrer l'erreur.la source
libjpeg
. Comme en C ++, la plupart des collections de routines C prennent unstruct *
pour opérer sur quelque chose en tant que collectif. Au lieu de stocker vos allocations de mémoire de fonctions intermédiaires en tant que locaux, elles peuvent être stockées dans la structure. Cela permet à unlongjmp()
gestionnaire de libérer de la mémoire. De plus, cela n'a pas tellement de tables d'exceptions foudroyées que tous les compilateurs C ++ génèrent encore 20 ans après les faits.Like every clever theory this falls apart when meeting reality.
En effet, l'allocation temporaire et autres compliquent lalongjmp()
tâche, car vous devez ensuitesetjmp()
plusieurs fois dans la pile d'appels (une fois pour chaque fonction qui doit effectuer une sorte de nettoyage avant de se terminer, qui doit alors "re-lever l'exception" en fonctionlongjmp()
du contexte qu'il avait initialement reçu). Cela devient encore pire si ces ressources sont modifiées après lesetjmp()
, car vous devez les déclarervolatile
pour éviter que le ne leslongjmp()
écrase.La combinaison de
setjmp
etlongjmp
est "super forcegoto
". Utiliser avec un soin EXTRÊME. Cependant, comme d'autres l'ont expliqué, alongjmp
est très utile pour sortir d'une situation d'erreur désagréable, lorsque vous le souhaitezget me back to the beginning
rapidement, plutôt que de devoir renvoyer un message d'erreur pour 18 couches de fonctions.Cependant, tout comme
goto
, mais pire, vous devez VRAIMENT faire attention à la façon dont vous l'utilisez. Alongjmp
vous ramènera simplement au début du code. Cela n'affectera pas tous les autres états qui peuvent avoir changé entre lesetjmp
et le retour au point desetjmp
départ. Ainsi, les allocations, verrous, structures de données à moitié initialisées, etc., sont toujours alloués, verrouillés et à moitié initialisés lorsque vous revenez à l'endroit où asetjmp
été appelé. Cela signifie que vous devez vraiment vous soucier des endroits où vous faites cela, qu'il est VRAIMENT correct d'appelerlongjmp
sans causer PLUS de problèmes. Bien sûr, si la prochaine chose que vous faites est de "redémarrer" [après avoir stocké un message sur l'erreur, peut-être] - dans un système embarqué où vous avez découvert que le matériel est en mauvais état, par exemple, alors très bien.J'ai également vu
setjmp
/longjmp
utilisé pour fournir des mécanismes de filetage très basiques. Mais c'est un cas assez particulier - et certainement pas comment fonctionnent les threads "standard".Edit: On pourrait bien sûr ajouter du code pour "s'occuper du nettoyage", de la même manière que C ++ stocke les points d'exception dans le code compilé et sait ensuite ce qui a donné une exception et ce qui doit être nettoyé. Cela impliquerait une sorte de table de pointeur de fonction et le stockage de "si nous sautons de dessous ici, appelons cette fonction, avec cet argument". Quelque chose comme ça:
Avec ce système, vous pouvez effectuer une «gestion complète des exceptions comme C ++». Mais c'est assez compliqué et dépend du fait que le code est bien écrit.
la source
setjmp
à guard chaque initialisation, à la C ++… et il convient de mentionner que son utilisation pour le threading n'est pas standard.Puisque vous mentionnez embarqué, je pense qu'il vaut la peine de noter un cas de non-utilisation : lorsque votre norme de codage l'interdit. Par exemple MISRA (MISRA-C: 2004: règle 20.7) et JFS (règle 20 AV): "La macro setjmp et la fonction longjmp ne doivent pas être utilisées."
la source
setjmp
etlongjmp
peut être très utile dans les tests unitaires.Supposons que nous voulions tester le module suivant:
Normalement, si la fonction à tester appelle une autre fonction, vous pouvez déclarer une fonction stub à appeler qui imitera ce que fait la fonction réelle pour tester certains flux. Dans ce cas cependant, la fonction appelle
exit
qui ne retourne pas. Le stub doit en quelque sorte émuler ce comportement.setjmp
etlongjmp
peut le faire pour vous.Pour tester cette fonction, nous pouvons créer le programme de test suivant:
Dans cet exemple, vous utilisez
setjmp
avant d'entrer la fonction à tester, puis dans le stubbed queexit
vous appelezlongjmp
pour revenir directement à votre cas de test.Notez également que le redéfini
exit
a une variable spéciale qu'il vérifie pour voir si vous voulez réellement quitter le programme et appelle_exit
pour le faire. Si vous ne le faites pas, votre programme de test risque de ne pas se fermer correctement.la source
J'ai écrit un mécanisme de gestion de type Java exception en C en utilisant
setjmp()
,longjmp()
et les fonctions du système. Il détecte les exceptions personnalisées mais également des signaux commeSIGSEGV
. Il propose une imbrication infinie des blocs de gestion des exceptions, qui fonctionne sur les appels de fonction et prend en charge les deux implémentations de thread les plus courantes. Il vous permet de définir une arborescence de classes d'exceptions qui présentent l'héritage au moment de la liaison, et lecatch
instruction parcourt cette arborescence pour voir si elle doit intercepter ou transmettre.Voici un exemple de l'apparence du code en utilisant ceci:
Et voici une partie du fichier d'inclusion qui contient beaucoup de logique:
Il existe également un module C qui contient la logique de gestion du signal et une certaine comptabilité.
C'était extrêmement difficile à mettre en œuvre, je peux vous le dire et j'ai presque arrêté. J'ai vraiment poussé à le rendre aussi proche que possible de Java; J'ai trouvé surprenant à quel point je suis arrivé avec seulement C.
Donnez-moi un cri si vous êtes intéressé.
la source
main()
sortira sur une exception non interceptée. S'il vous plaît upvote cette réponse :-)Progagation
section dans le README J'ai publié mon code d'avril 1999 sur GitHub (voir le lien dans la réponse modifiée). Regarde; c'était difficile à casser. Ce serait bien d'entendre ce que vous pensez.Sans conteste, l'utilisation la plus cruciale de setjmp / longjmp est qu'il agit comme un "saut goto non local". La commande Goto (et dans de rares cas où vous devrez utiliser goto over for et while boucles) est la plus utilisée en toute sécurité dans la même portée. Si vous utilisez goto pour sauter à travers les étendues (ou à travers l'allocation automatique), vous endommagerez probablement la pile de votre programme. setjmp / longjmp évite cela en enregistrant les informations de la pile à l'emplacement auquel vous souhaitez accéder. Ensuite, lorsque vous sautez, il charge ces informations de pile. Sans cette fonctionnalité, les programmeurs C devraient très probablement se tourner vers la programmation d'assemblage pour résoudre des problèmes que seul setjmp / longjmp pourrait résoudre. Dieu merci, cela existe. Tout dans la bibliothèque C est extrêmement important. Vous saurez quand vous en aurez besoin.
la source