Enquête: qu'est-ce qui est supérieur aux priorités des threads dans Windows?

Cette enquête, comme beaucoup d'autres, a commencé par le fait que je faisais ma propre entreprise, sans essayer de chercher des problèmes par moi-même. Cette fois, tout ce que j'ai fait, c'est d'ouvrir le couvercle de l'ordinateur portable et j'ai essayé de me connecter au système.

Pour les premières fois, lorsque cela a entraîné un retard de vingt secondes, j'ai ignoré le problème, en espérant qu'il se résoudrait lui-même. Les prochaines fois, j'ai pensé à l'enquête, mais les problèmes de performances qui surviennent avant même que vous vous connectiez soient plus difficiles à résoudre, et j'étais paresseux.

Quand j'ai remarqué que j'évitais de fermer l'ordinateur portable parce que j'avais peur de ces retards trop fréquents, j'ai réalisé qu'il était temps de le faire sérieusement.

Heureusement, j'ai récemment corrigé la trace du tampon en anneau UIforETWce qui le rend fiable, alors je l'ai commencé et j'ai commencé à attendre le prochain événement de retard. Je n'ai pas dû attendre longtemps.

Il m'a fallu plusieurs fois pour obtenir la trace ETW parfaitement bien avec moi . Et comme ce territoire ne m'était pas familier, il a fallu un certain temps pour comprendre ce qui se passait. Je n'ai toujours pas bien compris le problème, mais 90% ont compris les raisons de son apparition. J'ai réussi à en apprendre beaucoup, y compris de nouveaux détails sur le planificateur Windows, et j'ai également trouvé une solution absolument efficace.

La trace idéale que j'ai finalement enregistrée lors du chargement dans Microsoft Windows Performance Analyzer (WPA) ressemble à ceci:


Événements standard, fenêtres en bref et utilisation du processeur.

Ce tableau et deux graphiques contiennent une tonne d'informations. Le tableau supérieur ( Événements génériques ) affiche les séquences de touches enregistrées pour UIforETW. J'ai essayé d'appuyer sur une touche (code de touche virtuelle 162) une fois par seconde jusqu'à ce qu'un champ de saisie de mot de passe apparaisse. Puisque ces 17 frappes sont sélectionnées, dans le graphique ci-dessous, elles sont représentées par des lignes bleues verticales pour une visualisation simplifiée du temps d'exécution des événements critiques. L'axe des x représente le temps en secondes.

Les barres horizontales dans le graphique supérieur ( Window in Focus ) indiquent quel processus a le focus pendant cette période. Il y a six processus différents au total. La période de traçage est la courte période pendant laquelle l'ordinateur portable a été fermé.

Le graphique du bas montre l'utilisation du processeur . Les informations sont obtenues à partir des données de changement de contexte, elles doivent donc être complètement précises et complètes. Dans cette trace, une valeur de 100% indique le moment où les huit processeurs logiques de mon ordinateur portable à quatre cœurs à huit fils ont été utilisés.

Après avoir reçu les données de trace, j'ai dû comprendre ce que mon ordinateur portable fait secrètement lorsque le couvercle est fermé et jusqu'au moment où je reviens au système.

Tempête avant l'accalmie


Comme nous pouvons le voir, l'ordinateur portable au début de la trace de l'ordinateur portable est relativement simple, comme il se doit. Puis j'ai fermé son couvercle. Cela semble avoir provoqué un pic d'activité du processeur et un changement dans le focus de Windows. Window in Focus est passé de UIforETW à Idle, puis à csrss, de nouveau à Idle, à LogonUI, puis de nouveau à Idle. Qui aurait pensé?

Pendant cet intervalle, l'ordinateur portable a effectué environ 17 secondes de traitement CPU de différents types. Une partie de cela est le travail nécessaire pour fermer. Partie - ce sont des programmes (y compris les outils internes de Google) qui sont enregistrés dans le Planificateur de tâches pour l'exécution de «Lorsqu'un utilisateur verrouille un poste de travail» - cela a du sens. J'ai même remarqué que le travail est en cours pour créer des éléments d'interface utilisateur pour la connexion lorsque l'utilisateur continue de travailler - vous devez être préparé à l'avance, non?

17 secondes de CPU - un temps assez long pour que l'ordinateur portable s'endorme. Même sur mon ordinateur portable avec quatre cœurs et huit threads, le processus prend plus de quatre secondes. Sur mon ordinateur portable, il faut plus de 13 secondes de temps processeur pour s'endormir, et presque tous passent au code Windows. Le service de stratégie de diagnostic doit-il vraiment exécuter quelques SruDbTableSearches avant que l'ordinateur portable puisse se reposer?

Je pense que ce travail excessif quand je vais dormir est aussi un problème, mais ce n'est pas le problème que je recherche. J'ai donc décidé de lui tourner le dos.

Et ce n'est que bien plus tard que j'ai réalisé que c'est à cette époque que les grains de destruction de mon insecte ont été jetés ...

Dormir


Après avoir bloqué l'ordinateur portable, il n'y a plus d'activité CPU. Dans ce test particulier, l'ordinateur portable a été verrouillé pendant environ 16 secondes.

Éveil convulsif


L'activité du processeur lors de la transition vers le sommeil est incomparable avec le moment où il a commencé à se réveiller. Pendant ce temps, mon ordinateur portable surchargé a pris environ 172 secondes de temps CPU (!!!) pendant 22,6 secondes. C'est beaucoup de travail.

L'un des mystères de ce processus est la baisse de l'utilisation du processeur à près de zéro environ une seconde après le début de l'activité. Cette courte période d'indisponibilité semble plutôt anormale, compte tenu du chaos qui l'entoure. Mais je pense que cette fonctionnalité n'est pas liée au problème, donc je n'y ai pas fait attention.

Un autre mystère est pourquoi tant deles programmes prennent vie après cette brève pause. C'est drôle que l'intrus le plus sérieux responsable de 31,6 des 172 secondes du CPU soit Windows Performance Analyzer (WPA) - le programme même que j'utilise pour analyser les traces. Les trois copies que j'ai laissées en cours d'exécution travaillent dur pour rendre mon interface utilisateur, même si elle n'est pas encore visible.

En outre, des motifs sombres se produisent lors de la tentative d'initialisation des périphériques portables. KeStallExecutionProcessor est une boucle d'attente, et il était étrange de voir que c'est la fonction la plus exécutable de tout le système. Un deuxième cycle d'attente est-il le seul moyen de démarrer l'équipement? Est-il vraiment nécessaire de passer 700 ms de temps CPU à initialiser la souris et le clavier ? Microsoft et Intel devraient-ils ignorer la recommandation de Microsoft surun maximum de 50 microsecondes ?


Pilotes d'un cycle d'attente. i8042prt.sys est écrit par Microsoft. Les deux suivants sont créés par Intel.

En fin de compte, de nombreux programmes fonctionnent activement pendant cette période . La plupart d'entre eux semblent être confrontés au même problème que WPA - ils cherchent désespérément à dessiner des pixels sur un écran caché, et cela fait allusion à un bogue Windows. Mais même sans ce bug explorer.exe et d'autres programmes cherchent activement quelque chose à faire. Mais au final, bien que cette utilisation excessive du CPU soit une partie nécessaire du problème, ce n'est pas le problème lui-même . Encore une fois, j'ai cessé de lui prêter attention.

Concentrer


Lors de l'analyse des traces, il est important de savoir quand des actions importantes se produisent. La principale preuve était les événements de saisie, car j'ai cessé de cliquer sur le contrôle après l'apparition du formulaire de saisie du mot de passe. Voici les trois dernières touches de la touche de contrôle dans une forme approximative sur le graphique Fenêtre en bref :


Il semble que les événements critiques obtiennent le focus de LockApp.exe, après quoi le focus obtient LogonUI.exe presque instantanément. Vraisemblablement, j'ai entré le mot de passe dans LogonUI.exe (il est pratique que la trace n'intercepte pas les événements du clavier), après quoi le focus est brièvement passé à l'explorateur, puis à UIforETW, à partir duquel j'ai commencé.

Il semble également que LogonUI.exe ne puisse pas se concentrer avant LockApp.exe - ce modèle se répète dans toutes les traces que j'ai étudiées.

Donc, après plus de mille mots consacrés à la résolution de cette énigme, nous avons enfin une question claire que nous pouvons étudier: pourquoi LockApp.exe se concentre-t-il après avoir quitté le temps d'arrêt, cela prend vingt secondes?

Nous avons une question? Super, répondons-y


En utilisant les données d'utilisation du processeur (précises) obtenues à partir de la commutation de contenu, j'ai rapidement constaté que dans les vingt secondes après le réveil, LockApp.exe avait reçu moins d'une milliseconde de temps processeur et pendant plus de 14 secondes (de 35,158 s à 49,827 s) ne fonctionnait pas généralement:


LockApp ne fonctionne pas du tout pendant longtemps

La documentation sur la signification des colonnes dans les tableaux d'utilisation du processeur (précis) est ici .

Si un processus ou un thread n'a pas été exécuté depuis un certain temps et que vous souhaitez savoir pourquoi, des indices généralement importants peuvent être trouvés dans le premier changement de contexte après une longue accalmie, à savoir le passage à 49,827 secondes de suivi. J'ai réorganisé les colonnes pour afficher plus de données de ce changement de contexte:


LockApp est préparé mais pas exécuté. Strange ...

Count, égal à 1 signifie que nous regardons les données pour un seul changement de contexte.

Temps écoulé depuis dernier, égal à 38,2 millions de microsecondes, signifie que ce thread ne s'exécutera pas dans les 38,2 secondes. Cela en soi n'est ni bon ni mauvais. Les flux inactifs économisent de l'énergie, et à la fin l'ordinateur portable était dans un rêve depuis un certain temps.

Switch-In Time nous dit simplement quand exactement le thread s'inscrit dans le CPU - quand le contexte bascule vers ce thread.

Et maintenant, nous allons à la colonne Prêt. Il nous indique combien de temps le thread était prêt à s'exécuter, mais pas exécuté. En d'autres termes, ce thread attendait quelque chose (verrou, poignée) et c'est quelque chosea été libéré ou lancé, mais le thread n'a toujours pas fonctionné pendant 19,493 secondes.

Pour mieux comprendre la colonne Ready (us) , vous pouvez consulter la colonne Ready Time (s) . Il nous dit quand le stream est préparé. Nous voyons que pendant 30,333 secondes de traçage, ce thread a été préparé pour l'exécution, mais ne s'est pas exécuté avant 49,827 secondes. Cela semble important.

Cette disposition des colonnes nous montre par ailleurs le même changement de contexte:


Nouvelle pile de threads et pile de threads prêts

Donc, ce thread (que la nouvelle pile de threads s'attendait à ce que NtWaitForWorkViaWorkerFactory affiche) a reçu l'ordre de se réveiller (le processus système appelant KeSetEvent) peu de temps après avoir ouvert le couvercle du portable pour 30,333 secondes de traçage. Mais ça a commencé non pas alors (ce qui serait «bien»), mais après 19,494 s, et c'est mauvais.

En règle générale, lors d'une telle analyse des attentes, je passe beaucoup de temps à comprendre pourquoi le flux attend et ce qui l'a empêché d'être prêt. Mais c'était la première fois que je faisais une analyse des attentes, dans laquelle ce n'était pas important, et la question était de savoir pourquoi ce fil prêt à l'emploi n'était pas exécuté.

Cas ...


La plupart des gens ne passent pas autant de temps à étudier les traces ETW, donc une explication est nécessaire ici. C'est très étrange. Si le thread est prêt, il démarre généralement instantanément, ou après quelques millisecondes. La disponibilité du flux, comme son nom l'indique , signifie que le flux est prêt à être exécuté et presque rien ne peut l'interférer. Mais essayons de comprendre ce qui peut empêcher l'exécution d'un thread fini.

Priorité au fil


Au début, j'ai suggéré qu'il s'agissait d'un cas simple de «faim» du processeur. Des dizaines de processus nécessitent du temps CPU, et pour cette raison, LockApp n'obtient pas le bon jusqu'à ce que la charge diminue. Cependant, cette théorie ne correspond pas tout à fait aux symptômes, car le processus LockApp peut prendre environ 18 secondes même sans obtenir le temps CPU.

La théorie de la faim du processeur est bonne car elle est vérifiable. J'ai pu augmenter la priorité du processus LockApp à l'aide du Gestionnaire des tâches (pendant l'une des brèves périodes où il n'a pas été suspendu par le système UWP), par conséquent, dans la trace finale que j'ai utilisée pour ce poste, LockApp a été exécuté avec une priorité élevée. Un thread Windows standard s'exécute avec une priorité d'environ 8 à 10. La priorité la plus élevée avec laquelle un thread Windows normal (en temps non réel) peut s'exécuter est 15. Mes traces ETW ont montré que LockApp fonctionnait toujours avec la priorité 13 ou supérieure.

Voici une chronologie du processeur pour les 19,494 secondes critiques, regroupées et colorées par priorité de thread ( New In Pri, la priorité actuelle affectée au thread). Nous voyons que les threads avec les priorités 4, 8, 9 et 10 consomment la grande majorité du temps CPU, surtout à la fin:


Utilisation du CPU par priorité

Voici une autre image avec des threads cachés avec des priorités 0-12. Chaque fois que le graphique tombe en dessous de 12,5% (ce qui signifie un processeur logique du temps CPU de mon ordinateur portable à huit threads), LockApp doit être lancé, et il devient absolument incroyable que la priorité l'empêche d'être exécutée si souvent lorsque de nombreux threads avec une priorité inférieure ou égale obtenir une tonne de temps.


Utilisation prioritaire du processeur, threads haute priorité uniquement

Élimine l'inversion de priorité


Il y a des spéculations que les algorithmes d'inversion de priorité de Windows sont si propices aux autres threads que LockApp.exe est bloqué. Mais comme les graphiques ci-dessus montrent que les véritables priorités sont utilisées dans les décisions de planification, cette hypothèse (toujours peu convaincante) devra être abandonnée.

Déchargement du noyau de pile


Lorsque j'ai parlé de ce casse-tête sur Twitter, l'un des commentateurs a suggéré que la pile du noyau de threads était déchargée . Je n'étais pas familier avec cette situation, mais après les explications de John Werth (il comprend dans son domaine), j'ai désactivé l'échange de la pile du noyau et redémarré l'ordinateur. Rien n'a changé. En fait, je ne pensais pas que cela aiderait, étant donné que j'ai 32 Go de mémoire, et le problème se produit à plusieurs reprises et souvent; mais il valait mieux en être sûr.

Processus de pause


Étant donné que LockApp est une application UWP moderne, elle est soumise à des restrictions similaires à celles des applications pour smartphones. Entre autres choses, cela signifie qu'il peut être suspendu lorsqu'il n'est pas au premier plan, puis «dégeler» lorsqu'il est remis au premier plan. James Forshaw a proposé d' enregistrer ETW Microsoft-Windows-Kernel-Process pour obtenir des données à ce sujet.

Les événements sont conçus pour provoquer une confusion maximale. Le nom de la tâche Process Freeze est utilisé à la fois pour «décongeler» et «geler», et la version de l'événement win: Stop signifie que le processus démarre (il a arrêté de geler) et la version de win: Startsignifie que le processus s'arrête (commence à geler). Tout cela est extrêmement logique, mais très déroutant. Si les noms des événements étaient divisés en Freeze et Thaw, il y aurait moins de confusion.

Il n'y a pas de documentation pour ces événements, mais grâce à l'analyse, j'ai déterminé que ces événements sont toujours créés par le service d'infrastructure de tâches / courtier en arrière - plan . Le nom et l'ID de processus du processus correspondant sont indiqués dans le champ FrozenProcessID.


Événements ProcessFreeze (également utilisés pour le dégivrage) Il

était intéressant d'enquêter sur ce fournisseur - il contient de nombreux événements prometteurs - mais au final, il s'est avéré que LockApp n'a pas interrompu ni décongelé pendant le traçage. Cependant, ce fournisseur semblait assez utile, j'ai donc modifié UIforETW pour que les futures versions l'écrivent toujours.

Nous avons déjà tout exclu


Aucune des théories décrites ci-dessus ne me semblait très probable, et maintenant nous les avons toutes exclues. J'ai commencé à chercher de l'aide et m'a demandé de me donner des idées d'un ami de Microsoft. Et à ce moment, j'ai découvert que la priorité de flux 0-31 si bien connue dans Windows n'est en fait que cinq bits de faible priorité d'un système à priorité complète .

Utilisation de la position officielle


Il s'est avéré que mon ignorance était de ma faute. Si je lis attentivement les 108 pages de la section Threads de Windows Internals, 7th Edition, Part 1 , je comprendrais ce qui se passait. Si vous voulez aller de l'avant, ce sujet est révélé aux pages 287 à 295 .

Ce champ super prioritaire que je ne connaissais pas s'appelle Rank . Il apparaît dans WPA comme une colonne cachée par défaut (pour le trouver, vous devez ouvrir l'éditeur de vue) appelé NewThreadRank . Lors de la planification des threads, Thread Rank a la priorité sur la priorité. Presque tous les flux ont le rang 0, et un flux avec le rang 0 a toujours une priorité plus élevée qu'un flux avec le rang 2. En incluant une colonneNewThreadRank et en regardant le côté gauche du tableau, nous pouvons immédiatement voir le problème:


Le rang est plus important que la priorité

. Les flux LockApp.exe ont le rang 2, ce qui signifie que, malgré la priorité 14, ils ont la priorité la plus basse du système.

Une explication presque complète


Puisqu'il s'est avéré que les threads LockApp.exe ont le rang 2, ils ne peuvent être exécutés que si aucun des threads avec le rang 0 "ne veut" s'exécuter. Étant donné que de nombreuses applications (pour des raisons inconnues) rendent activement leurs écrans invisibles, elles se battent pour chaque miette de temps CPU, ne laissant rien pour les rangs supérieurs. Une fois que LockApp.exe reçoit une infime fraction du temps CPU, il passe rapidement au rang 0 (et la charge CPU baisse), après quoi le processus de connexion est effectué de la manière habituelle.

Ayant appris ces informations, j'ai commencé à étudier comment le classement de LockApp change au fil du temps. Au cours des dernières secondes, LockApp est soudainement passé du rang 0 à 2 avant de s'endormir. Le rang est conçu pour empêcher le processeur de prendre trop de temps, comme lorsque Windows Photos est trop intéressé par le traitement d'arrière-plan indésirable et effectue la transition. du rang 2 au 19:


Microsoft.Photos descend le classement

À partir de la documentation, vous pouvez comprendre que l'objectif principal du classement de flux est le partage équitable du temps CPU entre les sessions sur la machine afin que les processus d'un utilisateur ne nuisent pas aux autres. Ces deux options d'utilisation de rank indiquent clairement que le rang du thread ne devrait augmenter que s'il utilise beaucoup de temps CPU, et lorsque l'ordinateur portable s'est mis en veille, LockApp.exe n'a utilisé que 79,3 ms de temps CPU, et le reste du système - 17 à partir du temps CPU . Néanmoins, le système d'exploitation a décidé pour une raison quelconque de rétrograder LockApp à 2 dans le processus de mise en veille.

L'OS ne modifie le rang du flux que s'il appartient au «groupe de planification» ( KSCHEDULING_GROUP) et la plupart des unités d'exécution d'une installation Windows standard ne sont pas membres. Par conséquent, la plupart des threads ne sont pas soumis à un changement de classement, ils peuvent donc passer du temps CPU comme ils le souhaitent.

Puzzles restants


Malheureusement, on ne sait toujours pas pourquoi LockApp.exe passe au rang 2 avant d'activer le mode veille. Je suppose que LockApp est dans le groupe de planification et probablement l'un des algorithmes se comporte incorrectement. Mais je n'ai pas pu trouver d'API pour enquêter sur cela, et le temps commençait à manquer. Si vous connaissez des détails, écrivez dans les commentaires de l'article d'origine. Le principe même de l'utilisation du rang comme élément le plus important dans les décisions de planification devrait, me semble-t-il, inévitablement s'effondrer si la plupart des processus du système n'y sont pas impliqués - les fils des groupes de planification courent toujours le risque d'être laissés sans les ressources nécessaires. La planification d'allocation des ressources dynamiques ( DFSS ) est vouée à l'échec si la plupart des unités d'exécution ne sont pas impliquées.

De plus, je ne sais pas pourquoi tant d'applications restent actives après s'être endormi. Cela s'explique généralement par le fait que «de nombreux minuteurs s'arrêtent lorsque l'ordinateur portable est en mode veille pendant plusieurs heures», mais cette explication ne convient pas si l'ordinateur portable était en rêve pendant quelques secondes seulement, et le comportement de rendu WPA indique que quelque chose se passe dans le système de fenêtre Quelque chose ne va pas. Ajoutez à cela les applications à mauvais comportement et les pilotes de cycle d'attente, et tout est empilé dans le temps par le CPU.

Le fait que la désactivation du processeur et LockApp démarrent en même temps conduit à une explication évidente: LockApp ne peut fonctionner que lorsque la demande du processeur diminue. Mais il y a une explication tout aussi convaincante: dès que LockApp a la possibilité de s'exécuter (ou, éventuellement, LogonUI l'obtient), la demande de CPU diminue. Les deux explications fonctionnent, mais je pense que la seconde est plus plausible, car sinon nous ne pouvons pas expliquer pourquoi le rendu apparemment sans fin de WPA s'arrête soudainement.

Solution au problème


Dès que j'ai réalisé que LockApp.exe est une application distincte qui a des problèmes de lancement et que l'augmentation de sa priorité n'aide pas, je l'ai désactivée. Le fichier DisableLockScreen.reg m'a aidé avec ceci:

Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Personalization]
“NoLockScreen”=dword:00000001

En désactivant l'écran de verrouillage, l'ordinateur portable se réveille immédiatement après avoir ouvert le couvercle. Je n'ai remarqué ni freinage ni orages du CPU, et maintenant il faut une étape de moins pour entrer.

Le premier message Twitter que j'ai publié lorsque j'ai rencontré le problème pour la première fois contient un calendrier pour une enquête qui peut être utile à quelqu'un. De plus, beaucoup de gens intelligents de Twitter sont venus à la poste, grâce à eux.

Lorsque je suis retourné à l'article, j'ai découvert qu'après avoir réactivé l'écran de verrouillage, le problème avait disparu. Un simple redémarrage n'a pas résolu le problème - en février, j'ai redémarré plusieurs fois, mais nous ne saurons probablement pas pourquoi il a été perdu.

Discussions



All Articles