web-dev-qa-db-fra.com

Pourquoi les pilotes et les firmwares sont-ils presque toujours écrits en C ou ASM et non en C++?

Je suis juste curieux de savoir pourquoi les pilotes et les firmwares sont presque toujours écrits en C ou Assembly, et pas en C++?

J'ai entendu dire qu'il y a une raison technique à cela.

Est-ce que quelqu'un le sait?

Beaucoup d'amour, Louise

45
Louise

Parce que, la plupart du temps, le système d'exploitation (ou une "bibliothèque d'exécution") fournit la fonctionnalité stdlib requise par C++.

En C et ASM, vous pouvez créer des exécutables nus, qui ne contiennent aucune dépendance externe.

Cependant, étant donné que Windows prend en charge la bibliothèque stdlib C++, la plupart des pilotes Windows sont écrits en C++ (un sous-ensemble limité de).

De même, lorsque le micrologiciel est écrit ASM, cela est généralement dû au fait que (A) la plate-forme sur laquelle elle s'exécute n'a pas de compilateur C++ ou (B) il existe des contraintes de vitesse ou de taille extrêmes.

Notez que (B) n’a généralement pas posé de problème depuis le début des années 2000.

31
John Gietzen

Le code dans le noyau s'exécute dans un environnement très différent de celui de l'espace utilisateur. Il n'y a pas de séparation de processus, les erreurs sont donc beaucoup plus difficiles à récupérer; les exceptions sont quasiment hors de question. Il existe différents allocateurs de mémoire. Il peut donc être plus difficile d’obtenir new et delete pour fonctionner correctement dans un contexte de noyau. Il y a moins de bibliothèque standard disponible, ce qui rend beaucoup plus difficile l'utilisation efficace d'un langage tel que C++.

Windows permet l'utilisation d'un sous-ensemble très limité de C++ dans les pilotes du noyau; essentiellement, les choses qui pourraient être traduites trivialement en C, telles que les déclarations de variables à des endroits autres que le début des blocs. Ils déconseillent d'utiliser new et delete et ne prennent pas en charge RTTI ou la plupart des bibliothèques standard C++.

Mac OS X utilise I/O Kit , qui est un framework basé sur un sous-ensemble limité de C++, bien que, pour autant que je sache, plus complet que celui autorisé sous Windows. C'est essentiellement C++ sans exceptions et RTTI.

La plupart des systèmes d'exploitation de type Unix (Linux, les BSD) sont écrits en C, et je pense que personne n'a jamais vraiment constaté l'avantage de l'ajout du support C++ au noyau, étant donné que le C++ dans le noyau est généralement si limité.

26
Brian Campbell

1) "Parce que cela a toujours été ainsi" - cela explique en réalité plus que vous ne le pensez - étant donné que les API sur à peu près tous les systèmes actuels ont été écrites à l'origine sur un modèle basé sur C ou ASM, et qu'un grand nombre de codes antérieurs existent dans C et ASM, il est souvent plus facile de «suivre le courant» que de comprendre comment tirer parti du C++.

2) Environnement - Pour utiliser toutes les fonctionnalités de C++, vous avez besoin d’un environnement d’exécution, dont certaines sont difficiles à fournir à un pilote. C'est plus facile à faire si vous limitez votre jeu de fonctionnalités, mais entre autres choses, la gestion de la mémoire peut devenir très intéressante en C++, si vous n'avez pas beaucoup de mémoire. Les exceptions sont également très intéressantes à prendre en compte dans cet environnement, tout comme RTTI.

3) "Je ne vois pas ce que ça fait". Il est possible pour tout programmeur raisonnablement qualifié d'examiner une ligne de C et d'avoir une bonne idée de ce qui se passe au niveau du code machine pour implémenter cette ligne. Évidemment, l'optimisation change quelque peu, mais vous pouvez généralement savoir ce qui se passe. En C++, étant donné la surcharge d'opérateurs, les constructeurs, les destructeurs, les exceptions, etc., il devient très difficile d'avoir une idée de ce qui va se passer sur une ligne de code donnée. Lorsque vous écrivez des pilotes de périphérique, cela peut être fatal, car vous DEVEZ souvent savoir si vous allez interagir avec le gestionnaire de mémoire ou si la ligne de code affecte (ou dépend) les niveaux d'interruption ou le masquage.

Il est tout à fait possible d'écrire des pilotes de périphérique sous Windows en utilisant C++ - je l'ai fait moi-même. La mise en garde est que vous devez être prudent sur les fonctionnalités C++ que vous utilisez, et d'où vous les utilisez.

11
Michael Kohne

À l'exception d'une prise en charge plus large des outils et de la portabilité du matériel, je ne pense pas qu'il y ait une raison impérieuse de vous limiter au C uniquement. Je vois souvent des tâches compliquées codées à la main effectuées en C qui peuvent être plus naturellement réalisées en C++:

  • Regroupement en "modules" de fonctions (non générales) ne fonctionnant que sur la même structure de données (souvent appelée "objet") -> Utiliser les classes C++.
  • Utilisation d'un pointeur "handle" afin que les fonctions de module puissent fonctionner avec des "instances" de structures de données -> Utiliser les classes C++.
  • Utilisation de macros de type fonction -> Modèles C++ et fonctions inline
  • Comportement d'exécution différent en fonction d'un ID de type avec vtable ("descripteur") ou envoyé avec une instruction switch -> polymorphisme C++
  • Arithmétique de pointeur sujette à erreur pour le marshalling/demarshalling de données depuis/vers un port de communication, ou l'utilisation de structures non portables -> Concept de flux C++ (pas nécessairement std :: iostream)
  • Préfixer l'enfer de tout pour éviter les conflits de noms: Espaces de noms C++

Aucune des fonctionnalités C++ décrites ci-dessus ne coûte plus cher que les implémentations C manuscrites. Il me manque probablement un peu plus. Je pense que l'inertie de C dans ce domaine a plus à voir avec le fait que C soit principalement utilisé.

Bien sûr, vous ne pourrez peut-être pas utiliser STL librement (ou pas du tout) dans un environnement contraint, mais cela ne signifie pas que vous ne pouvez pas utiliser C++ en tant que "meilleur C".

10
Emile Cormier

Les commentaires que je rencontre pour expliquer pourquoi un magasin utilise le C pour un système intégré par rapport au C++ sont les suivants: 

  1. C++ produit du code
  2. Les exceptions C++ prennent trop de place .
  3. Le polymorphisme C++ et les tables virtuelles Utilisent trop de mémoire ou d'exécution .
  4. Les personnes dans le magasin ne connaissent pas Le langage C++.

La seule raison valable peut être la dernière. J'ai vu des programmes en langage C incorporant la POO, les objets fonction et les fonctions virtuelles. Il devient très vite très moche et gonfle le code.

La gestion des exceptions en C, lorsqu'elle est correctement implémentée, prend beaucoup de place. Je dirais à peu près la même chose que C++. L'avantage des exceptions C++: elles se trouvent dans le langage et les programmeurs n'ont pas à repenser la roue.

La raison pour laquelle je préfère C++ au C dans les systèmes embarqués est que C++ est un langage typé plus fort. Plus de problèmes peuvent être trouvés dans le temps de compilation, ce qui réduit le temps de développement. De plus, C++ est un langage plus facile à implémenter que C pour implémenter des concepts. 

La plupart des raisons contre C++ sont liées aux concepts de conception plutôt qu'au langage réel.

7
Thomas Matthews

La raison pour laquelle C, et non C++, est utilisé n'est PAS:

  • Parce que C++ est plus lent
  • Ou parce que le c-runtime est déjà présent.

Il IS parce que C++ utilise des exceptions. La plupart des implémentations d’exceptions de langage C++ sont inutilisables dans le code de pilote car les pilotes sont appelés lorsque le système d’exploitation répond aux interruptions matérielles. Pendant une interruption matérielle, le code du pilote N'EST PAS autorisé à utiliser des exceptions car cela provoquerait/pourrait provoquer des interruptions récursives. En outre, l'espace de pile disponible pour le code dans le contexte d'une interruption est généralement très petit (et ne peut pas être développé en conséquence de la règle sans exception).

Vous pouvez bien sûr utiliser new (std :: nothrow), mais comme les exceptions en c ++ sont maintenant omniprésentes, cela signifie que vous ne pouvez vous fier à aucun code de bibliothèque pour utiliser la sémantique std :: nothrow.

Il IS également parce que C++ a abandonné quelques fonctionnalités de C: - Dans les pilotes, le placement du code est important. Les pilotes de périphérique doivent pouvoir répondre aux interruptions. Le code d'interruption DOIT être placé dans des segments de code "non paginés" ou mappés de manière permanente dans la mémoire, car, si le code était dans la mémoire paginée, il pourrait être paginé à la demande, ce qui provoquerait une exception, qui est interdite. Dans les compilateurs C utilisés pour le développement de pilotes, il existe des directives #pragma permettant de contrôler le type de fonctions de mémoire utilisées. Comme le pool non paginé est une ressource très limitée, vous ne souhaitez PAS marquer tout votre pilote comme étant non paginé: C++ génère toutefois beaucoup de code implicite. Les constructeurs par défaut, par exemple. Il n’existe aucun moyen de mettre entre crochets le code C++ généré implicitement pour contrôler son emplacement et, les opérateurs de conversion étant automatiquement appelés, il n’existe aucun moyen pour les audits de code de garantir qu’il n’existe aucun effet secondaire faisant appel au code paginé.

Donc, pour résumer: - C, et non C++, est utilisé pour le développement de pilotes, car les pilotes écrits en C++ consomment une quantité déraisonnable de mémoire non paginée ou font planter le noyau du système d'exploitation.

7
Chris Becke

La principale raison pour laquelle C est utilisé au lieu de dire que Java est extrêmement protégé est qu’il est très facile de voir quelle mémoire est utilisée pour une opération donnée. C est très adressant orienté. Le fait d’écrire du code noyau a pour principal souci d’éviter de faire référence à la mémoire, ce qui pourrait causer une erreur de page à un moment inopportun.

C++ can ne peut être utilisé que si l'exécution est spécialement adaptée pour référencer uniquement les tables internes en mémoire fixe (non paginables) lorsque la machine d'exécution est appelée implicitement, par exemple en utilisant une table virtuelle lors de l'appel de fonctions virtuelles. Cette adaptation spéciale ne vient pas "out of the box" la plupart du temps.

L'intégration de C à une plate-forme est beaucoup plus facile à effectuer car il est facile de supprimer C de sa bibliothèque standard et de garder le contrôle des accès à la mémoire de manière parfaitement explicite. Alors qu’il s’agit également d’un langage bien connu, c’est souvent le choix des concepteurs d’outils pour le noyau.

Edit : Suppression de la référence aux nouveaux appels et aux appels supprimés (ceci était faux/trompeur); remplacé par une expression plus générale "machine d'exécution".

7
martinr

C est très proche d'un langage d'assemblage indépendant de la machine. La plupart des programmations de type OS sont en mode "bare metal". Avec C, le code que vous lisez est le code réel. C++ peut cacher des choses que C ne peut pas.

Ce n’est que mon opinion, mais j’ai passé beaucoup de temps dans ma vie à déboguer les pilotes de périphérique et les éléments liés au système d’exploitation. Souvent, en regardant la langue de l'Assemblée. Restez simple au bas niveau et laissez le niveau de l'application devenir sophistiqué.

5
Richard Pennington

Les pilotes Windows sont écrits en C++.
Les pilotes Linux sont écrits en c parce que le noyau est écrit en c.

3
Martin Beckett

La raison pour laquelle les pilotes et les firmwares sont principalement écrits en C ou ASM est qu’il n’ya pas de dépendance aux bibliothèques d’exécution réelles. Si vous deviez imaginer ce pilote imaginaire écrit en C ici

 # include <stdio.h> 
 
 # define OS_VER 5.10 
 # define DRIVER_VER "1.2.3" 
 
 int drivermain (driverstructinfo ** dsi) {
 if ((* dsi) -> version> OS_VER) {
 (* dsi) -> InitDriver (); 
 printf ("FooBar Pilote chargé\n "); 
 Printf (" Version:% s ", DRIVER_VER); 
 (* Dsi) -> Dispatch = fooDispatch; 
} Autre {
 (* dsi) -> Exit (0); 
} 
} 
 
 void fooDispatch (driverstructinfo * dsi) {
 printf ( "Distribué% d\n", dsi-> GetDispatchId ()); 
} 

Notez que le support de la bibliothèque d’exécution devrait être extrait et lié au cours de la compilation/du lien, cela ne fonctionnerait pas car l’environnement d’exécution (c’est-à-dire lorsque le système d’exploitation est en phase de chargement/d’initialisation) n’est pas entièrement configuré et par conséquent il n'y aurait aucun indice sur la façon de modifier printf et sonnerait probablement le glas du système d'exploitation (panique du noyau pour Linux, écran bleu pour Windows), car il n'existe aucune référence à la manière d'exécuter la fonction.

En d'autres termes, avec un pilote, ce code de pilote a le privilège d'exécuter du code avec le code du noyau qui partagerait le même espace, ring0 est le privilège ultime d'exécution du code (toutes les instructions autorisées), ring3 étant l'endroit où l'extrémité avant de le système d'exploitation s'exécute en (privilège d'exécution limitée), en d'autres termes, un code ring3 ne peut pas avoir d'instruction réservée à ring0, le noyau supprimera le code en le piégeant comme s'il disait: 'Hé, vous n'avez aucun privilège à suivre. up domaine de ring0 '.

L’autre raison pour laquelle il est écrit en assembleur est principalement pour la taille du code et la vitesse native brute, ce pourrait être le cas, par exemple, d’un pilote de port série, où les entrées/sorties sont «critiques» pour la fonction en ce qui concerne la synchronisation, la latence , mise en mémoire tampon.

La plupart des pilotes de périphérique (dans le cas de Windows) disposeraient d'une chaîne d'outils de compilation spéciale ( WinDDK ) qui peut utiliser le code C, mais ne permet pas la liaison avec les bibliothèques d'exécution standard du C.

Il existe une boîte à outils pouvant vous permettre de créer un pilote dans Visual Studio, VisualDDK . Bien sûr, la construction d’un pilote n’est pas une mince affaire de cœur, vous obtiendrez une activité induite par le stress en regardant les écrans bleus, la panique du noyau et vous demanderez pourquoi, en déboguant les pilotes, etc. 

Le côté de débogage est plus difficile, les codes ring0 ne sont pas facilement accessibles par ce code car les portes sont fermées, c’est à travers la trappe du noyau (faute d’un meilleur mot) et si on vous le demande poliment, la porte reste fermée pendant que le Le noyau délègue la tâche à un gestionnaire résidant sur ring0. Exécutez-la. Quels que soient les résultats renvoyés, ils sont renvoyés au code ring3 et la porte reste toujours fermée. C'est le concept d'analogie de la manière dont le code utilisateur peut exécuter du code privilégié sur ring0.

De plus, ce code privilégié peut facilement piétiner l'espace mémoire du noyau et corrompre quelque chose d'où la panique du noyau/écrans bleus ...

J'espère que cela t'aides.

2
t0mm13b

Peut-être parce qu'un pilote ne nécessite pas de fonctionnalités orientées objet, alors que le fait que C ait encore des compilateurs un peu plus matures ferait une différence.

1
pavpanchekha

Probablement parce que c est toujours souvent plus rapide, plus petit lors de la compilation et plus cohérent dans la compilation entre différentes versions de système d'exploitation, avec moins de dépendances. En outre, comme c ++ est vraiment construit sur c, la question est de savoir si vous avez besoin de ce qu’il fournit. 

Il y a probablement quelque chose dans le fait que les personnes écrivant des pilotes et des microprogrammes sont généralement habitués à travailler au niveau du système d'exploitation (ou inférieur) qui est en c, et sont donc habitués à utiliser c pour ce type de problème.

1
Andrew Kuklewicz

Il existe de nombreux styles de programmation tels que procédurale, fonctionnelle, orientée objet, etc. La programmation orientée objet est plus adaptée à la modélisation du monde réel.

Je voudrais utiliser orienté objet pour les pilotes de périphériques si elle le suites. Mais, la plupart du temps, lorsque vous programmez des pilotes de périphérique, vous n’avez pas besoin des avantages fournis par c ++, tels que l’abstraction, le polymorphisme, la réutilisation de code, etc.

1
chinmaya

Eh bien, les pilotes IOKit pour MacOSX sont écrits en sous-ensemble C++ (sans exception, modèles, héritage multiple). Et il est même possible d'écrire des modules de noyau Linux dans haskell.)

Autrement, C, étant un langage d'assemblage portable, capture parfaitement l'architecture et le modèle de calcul de von Neumann, permettant ainsi de contrôler directement toutes ses particularités et ses inconvénients (comme le "goulot d'étranglement de von Neumann"). C fait exactement ce pour quoi il a été conçu et saisit son modèle d'abstraction cible complètement et parfaitement (enfin, sauf en ce qui concerne l'hypothèse implicite dans un flux à contrôle unique qui aurait pu être généralisée pour couvrir la réalité des threads matériels) et c'est pourquoi je pense que c'est une belle langage.) En limitant le pouvoir d’expression du langage à de telles bases, on élimine la plupart des détails imprévisibles de la transformation lorsque différents modèles informatiques sont appliqués à cette norme de facto. En d’autres termes, C vous oblige à vous en tenir aux bases et à contrôler quasiment directement ce que vous faites, par exemple, lorsque vous modélisez les points communs de comportement avec des fonctions virtuelles, vous contrôlez le mode de stockage et d’utilisation des tables de pointeurs de fonctions lors de la comparaison avec l’allocation vtbl implicite de C++. et le management. Ceci est en fait utile lors de la prise en compte des caches.

Cela dit, le paradigme basé sur les objets est très utile pour représenter les objets physiques et leurs dépendances. En ajoutant l'héritage, nous obtenons un paradigme orienté objet qui, à son tour, est très utile pour représenter la structure et la hiérarchie de comportement des objets physiques. Rien n'empêche quiconque de l'utiliser et de l'exprimer à nouveau en C, ce qui permet un contrôle total sur la manière dont vos objets seront créés, stockés, détruits et copiés. En fait, c'est l'approche adoptée dans le modèle de périphérique Linux. Ils ont obtenu des "objets" pour représenter les périphériques, une hiérarchie d'implémentation d'objets pour modéliser les dépendances en matière de gestion de l'alimentation et une fonctionnalité d'héritage piratée pour représenter les familles de périphériques, le tout en C.

1
Inso Reiges

parce qu'au niveau du système, les pilotes doivent contrôler chaque bit de chaque octet de la mémoire, une autre langue plus élevée ne peut pas le faire, ou ne peut pas le faire en natif, seul C/Asm parvient à ~

0
user247744