Quel est le but de while(1);
? Je suis conscient que while(1)
(sans point-virgule) boucle en boucle et ressemble à une situation de spinlock. Cependant, je ne vois pas où while(1);
pourrait être utilisé?
if(!condition)
{
while(1);
}
Remarque: Il ne s’agit pas de do
-while()
ou de plain while(1)
.
Veuillez noter que toutes les déclarations valides de la langue ne doivent pas servir . Ils sont valables selon la grammaire de la langue. On peut construire de nombreuses instructions "inutiles" similaires, telles que if (1);
. Je vois de telles déclarations comme la conjonction d’un conditionnel (if
, while
, etc.) et de la déclaration vide ;
(Qui est également une déclaration valide bien qu’elle ne serve évidemment but spécifique).
Cela étant dit, j'ai rencontré while (1);
dans le code de sécurité. Lorsque l'utilisateur fait quelque chose de très grave avec un périphérique intégré, il peut être judicieux de l'empêcher d'essayer autre chose. Avec while (1);
, nous pouvons bloquer un périphérique sans condition jusqu'à ce qu'un opérateur accrédité le redémarre manuellement.
while(1);
peut également faire partie de la mise en œuvre d'une panique du noyau, bien qu'une boucle for(;;) {}
semble être un moyen plus courant d'exprimer la boucle infinie, et il peut y avoir une boucle non vide body (par exemple to panic_blink()
).
Si vous creusez jusqu'à l'assemblage, (cela est plus facile à comprendre du point de vue des systèmes intégrés, ou si vous avez essayé de programmer un chargeur de démarrage)
vous vous rendrez compte qu'une boucle while est juste une instruction jmp ... c'est à dire
(pseudo code: starting loop address)
add ax, bx
add ax, cx
cmp ax, dx
jz (pseudo code: another address location)
jmp (pseudo code: starting loop address)
Expliquons comment cela fonctionne, le processeur continuera à exécuter les instructions de manière séquentielle ... quoi qu'il arrive. Donc, au moment où il entre dans cette boucle, il ajoute les registres bx à ax et stocke dans ax, ajoute les registres cx à ax et stocke dans ax, cmp ax, dx (cela signifie soustraire dx de ax) l'instruction jz signifie sauter à (une autre adresse emplacement) si le drapeau zéro est défini (ce qui est un bit dans le registre des drapeaux qui sera défini si le résultat de la soustraction ci-dessus est zéro), puis jmp adresse l'adresse de boucle de début (assez simple) et refait le tout.
La raison pour laquelle je vous ai dérangé avec toute cette Assemblée est de vous montrer que cela se traduirait en C par
int A,B,C,D;
// initialize to what ever;
while(true)
{
A = A + B;
A = A + C;
if((A-D)==0)
{break;}
}
// if((X-Y)==0){break;} is the
// cmp ax, dx
// jz (pseudo code: another address location)
Alors imaginez le sénateur dans Assembly si vous avez juste une très longue liste d’instructions qui ne se terminent pas par un jmp (la boucle while) pour répéter une section, charger un nouveau programme ou faire quelque chose ... Le processeur finira par atteindre le dernière instruction puis chargez l’instruction suivante pour ne rien trouver (elle gèlera ou triple faute ou quelque chose).
C’est exactement pourquoi, lorsque vous souhaitez que le programme ne fasse rien jusqu’à ce qu’un événement soit déclenché, vous devez utiliser une boucle while (1), afin que le processeur continue de sauter à sa place et n’atteigne pas cette adresse d’instruction vide. Lorsque l'événement est déclenché, il saute à l'adresse des instructions du gestionnaire d'événements, l'exécute, efface l'interruption et revient à votre boucle while (1) sautant juste à sa place en attendant d'autres interruptions. Btw the while (1) est appelé une super boucle si vous voulez en savoir plus à ce sujet ... Juste pour tous ceux qui ont très envie de discuter et de commenter négativement à ce stade, ce n'est pas un tutoriel de l'Assemblée, une conférence ou quoi que ce soit. C'est simplement une explication en anglais qui est aussi simple que possible, qui néglige de nombreux détails sous-jacents, tels que les pointeurs et les piles, etc., et parfois même simplifie les choses pour faire passer le message. Personne ne recherche l'exactitude de la documentation ici et je sais que ce code C ne sera pas compilé de la sorte, mais c'est uniquement pour Demo !!
Ceci est marqué C, mais je vais commencer par une perspective C++. En C++ 11, le compilateur est libre d'optimiser while(1);
.
À partir du projet de norme C++ 11 n3092, section 6.5, paragraphe 5 (souligné par moi)
Une boucle qui, en dehors de l'instruction for-init dans le cas d'une instruction for,
- n'appelle pas les fonctions d'E/S de la bibliothèque et
- n'accède ni ne modifie les objets volatiles, et
- n'effectue aucune opération de synchronisation (1.10) ou opération atomique (Clause 29)
peut être supposé par la mise en œuvre se terminer. [ Remarque: Ceci est destiné à permettre les transformations du compilateur, telles que la suppression des boucles vides, même lorsque la terminaison ne peut pas être prouvée. - end note]
La norme C11 a une entrée similaire, mais avec une différence essentielle. Du projet de norme C11 n1570, (c'est moi qui souligne):
Une instruction d'itération dont l'expression de contrôle n'est pas une expression constante ,156) qui n'effectue aucune opération d'entrée/sortie, n'accède pas aux objets volatils et n'exécute aucune opération de synchronisation ou atomique dans son corps, contrôlant l'expression, ou (dans le cas d'une instruction for), sa expression-3 =, peut être supposé par l'implémentation se terminer.157)
156) Une expression de contrôle omise est remplacée par une constante non nulle, qui est une expression constante.
157) Ceci est destiné à permettre des transformations du compilateur telles que la suppression des boucles vides même lorsque la terminaison ne peut pas être prouvée.
Cela signifie que while(1);
peut être supposé se terminer en C++ 11 mais pas en C11. Même avec cela, la note 157 (non contraignante) est interprétée par certains fournisseurs comme leur permettant de supprimer cette boucle vide. La différence entre while(1);
en C++ 11 et C11 est celle du comportement défini par rapport au comportement non défini. Comme la boucle est vide, elle peut être supprimée en C++ 11. En C11, while(1);
est manifestement non terminé, ce qui est un comportement indéfini. Puisque le programmeur a appelé UB, le compilateur est libre de faire n'importe quoi, y compris de supprimer cette boucle fautive.
Il y a eu un certain nombre de discussions empilées sur l'optimisation des compilateurs supprimant while(1);
. Par exemple, Les compilateurs sont-ils autorisés à éliminer des boucles infinies? , ne boucle for vide utilisée comme sommeil sera-t-elle optimisée? , Optimisation de la suppression d'un "while 1); "en C++ 0x . Notez que les deux premiers étaient spécifiques à C.
Une utilisation sur un logiciel embarqué consiste à implémenter une réinitialisation logicielle à l'aide du chien de garde:
while (1);
ou équivalent mais plus sûr car cela clarifie l'intention:
do { /* nothing, let's the dog bite */ } while (1);
Si le chien de garde est activé et qu'il n'est pas acquitté après x millisecondes, nous savons qu'il réinitialisera le processeur. Utilisez-le pour mettre en œuvre une réinitialisation logicielle.
Je suppose que la while(1);
n'est pas associée à une boucle do
...
La seule implémentation semi-utile de while(1);
que j'ai vue est une boucle qui ne fait rien, en attente d'une interruption; comme un processus parent en attente d’un SIGCHLD, indiquant qu’un processus enfant est terminé. Le gestionnaire SIGCHLD du parent, une fois tous les processus enfants terminés, peut terminer le thread parent.
Cela fait l'affaire, mais fait perdre beaucoup de temps processeur. Une telle utilisation devrait peut-être permettre d’endormir le processeur périodiquement.
Un endroit où j'ai vu une while(1);
est la programmation intégrée.
L'architecture utilisait un thread principal pour surveiller les événements et des threads de travail pour les gérer. Un horloge de surveillance matérielle (explication ici ) effectuait une réinitialisation logicielle du module après un certain temps. Dans la boucle d'interrogation principale de thread, il réinitialiserait cette minuterie. Si le thread principal a détecté une erreur irrémédiable, une while(1);
sera utilisée pour lier le thread principal, ce qui déclenchera la réinitialisation du chien de garde. Je crois que l'échec d'assert a été implémenté avec un while(1);
également.
Comme d’autres l’ont dit, c’est juste une boucle infinie qui ne fait rien, complètement analogue à
while (1) {
/* Do nothing */
}
La boucle avec le point-virgule a un corps. Lorsqu'il est utilisé en tant qu'instruction, un seul point-virgule est un instruction null, et le corps de la boucle est constitué de cette instruction null.
Pour des raisons de lisibilité, afin de faire comprendre au lecteur que l'instruction null est le corps de la boucle, je vous recommande de l'écrire sur une ligne distincte:
while (1)
;
Sinon, il est facile de la rater à la fin de la ligne "tant que", où il n’ya généralement pas de point-virgule, et le lecteur peut confondre la ligne suivante avec le corps de la boucle.
Ou utilisez plutôt une instruction composée vide.
while(1);
est réellement très utile. Surtout quand il s'agit d'un programme qui a une sorte de code d'authentification et que vous voulez désactiver l'utilisation du programme pour l'utilisateur car, par exemple, il a entré un code erroné à 3 reprises. L'utilisation d'une while(1);
arrêterait la progression du programme et rien ne se passerait jusqu'à ce que le programme soit redémarré, principalement pour des raisons de sécurité.
Ceci peut être utilisé pour attendre Interrupt . En gros, vous initialisez tout ce dont vous avez besoin et attendez que quelque chose se produise. Après cela, une fonction spécifique est appelée et exécutée, après quoi elle retourne à l'état d'attente.
Cette chose peut être appuyée sur un bouton, cliquer/déplacer avec la souris, recevoir des données, etc.
De plus, je dirais que des éléments similaires sont très souvent utilisés par les frameworks d'interface utilisateur. Pendant qu'il attend des signaux concernant les actions de l'utilisateur.
Dans la programmation de jeux de puces AVR (utilisant le langage de programmation C), cette instruction est fréquemment utilisée. Elle joue le rôle de boucle d’événement.
Supposons que je veuille concevoir un compteur, afin que je puisse utiliser ce code pour l'implémenter:
void interrupt0() {
/* check if key pressed, count up the counter */
}
void main() {
/* Common inits */
/* Enable interrupt capability and register its routine */
/* Event loop */
while(1);
}
Comme la condition est toujours vraie, nous pouvons dire que nous utilisons une tautologie logique connue en mathématiques. Bien que la boucle prouve qu'elle est toujours vraie, elle ne s'arrêtera pas si le code ne le force pas ou jusqu'à ce que les ressources soient réduites.
Je pense que la raison pour laquelle while(1);
est utilisée est parce que plus tôt dans le code, un gestionnaire d'événements ou une interruption a été défini sur ce fil. L'utilisation du code de verrouillage Thread Safe standard peut s'avérer assez coûteuse (à temps) lorsque vous savez que votre code "n'attend" que très peu de temps. Par conséquent, vous pouvez configurer l'interruption et la "rotation" à l'aide de while(1);
qui, bien qu'il s'agisse d'une attente occupée (ne laisse pas le processeur inactif/ne traite pas les autres threads), nécessite très peu de cycles de configuration.
En résumé, c'est un spinlock "bon marché" pendant que votre thread attend une interruption ou un événement.