Comment une langue se développe-t-elle? [fermé]


208

J'apprends le C ++ et je viens de commencer à découvrir certaines des capacités de Qt pour coder les programmes GUI. Je me suis posé la question suivante:

Comment C ++, qui n'avait auparavant aucune syntaxe capable de demander au système d'exploitation une fenêtre ou un moyen de communiquer via des réseaux (avec des API que je ne comprends pas complètement non plus, j'en conviens), obtient-il soudainement de telles capacités via des bibliothèques écrites en C ++ elles-mêmes? Tout me semble terriblement circulaire. Quelles instructions C ++ pourriez-vous éventuellement trouver dans ces bibliothèques?

Je me rends compte que cette question peut sembler triviale à un développeur de logiciels expérimenté, mais je fais des recherches depuis des heures sans trouver de réponse directe. C'est arrivé au point où je ne peux pas suivre le tutoriel sur Qt car l'existence des bibliothèques est incompréhensible pour moi.


27
Comment std :: cout dessine-t-il même quelque chose sur le moniteur? Ou est-ce qu'il repose sur un compilateur qui comprend votre matériel?
doctorlove

14
Grande question. En fin de compte, il est difficile de répondre jusqu'à ce que vous étudiez le matériel.
user541686

17
Qt n'est pas une extension du langage (qui nécessiterait un compilateur compatible avec Qt). Ce n'est qu'une bibliothèque ajoutée à votre arsenal. Finalement, au niveau le plus bas, toutes les bibliothèques communiquent avec le système d'exploitation via des appels système, qui sont indépendants de la langue, mais très dépendants du système d'exploitation et de l'architecture du processeur.
DevSolar

8
afaik, C ++ a un assemblage en ligne, qui peut faire à peu près n'importe quoi
Nom d'affichage

9
@DevSolar: En fait, Qt étend le langage avec son propre mécanisme de fente de signal, sa réflexion et de nombreuses autres fonctionnalités dynamiques. Et ces choses nécessitent un compilateur (le compilateur de méta-objet) pour compiler en code C ++.
Siyuan Ren

Réponses:


194

Un ordinateur est comme un oignon, il comporte de nombreuses couches, depuis le noyau interne du matériel pur jusqu'à la couche d'application la plus externe. Chaque couche expose des parties d'elle-même à la couche externe suivante, de sorte que la couche externe peut utiliser certaines des fonctionnalités des couches internes.

Dans le cas de Windows par exemple, le système d'exploitation expose la soi-disant API WIN32 pour les applications fonctionnant sous Windows. La bibliothèque Qt utilise cette API pour fournir des applications utilisant Qt à sa propre API. Vous utilisez Qt, Qt utilise WIN32, WIN32 utilise des niveaux inférieurs du système d'exploitation Windows, et ainsi de suite jusqu'à ce que ce soit des signaux électriques dans le matériel.


56
Remarque: Qtfournit ici une abstraction de la couche en dessous, car sous Linux Qtappelle l'API Linux et non l'API WIN32.
Matthieu M.

5
Je voudrais probablement elaboarte un peu plus sur l'exemple de Qt, qu'il apparaît juste, comme il étend sans effort les capacités c ++. Quand la réalité est, ils ont mis beaucoup d'efforts, pour créer une API commune, à (de façon discutable) de nombreux "noyaux d'oignons" différents. Ce sont eux qui fournissent la portabilité au-dessus des backends non standard non portables.
luk32

81
Un ordinateur est comme un oignon: le couper vous fait pleurer, mais c'est un peu savoureux après.
alecov

3
@ChristopherPfohl Oui, je devais l'utiliser car je ne pouvais pas comprendre comment un ordinateur serait comme une boîte de chocolats. :)
Un programmeur du

1
@Celeritas, l'enseignant a probablement dit user32.dll, ou peut-être gdi32.dll.
user253751

59

Vous avez raison de dire qu'en général, les bibliothèques ne peuvent rien faire qui ne soit déjà possible.

Mais les bibliothèques n'ont pas besoin d'être écrites en C ++ pour être utilisables par un programme C ++. Même s'ils sont écrits en C ++, ils peuvent utiliser en interne d'autres bibliothèques non écrites en C ++. Ainsi, le fait que C ++ n'ait fourni aucun moyen de le faire ne l'empêche pas d'être ajouté, tant qu'il existe un moyen de le faire en dehors de C ++.

À un niveau assez bas, certaines fonctions appelées par C ++ (ou par C) seront écrites dans l'assembly, et l'assembly contient les instructions requises pour faire tout ce qui n'est pas possible (ou n'est pas facile) en C ++, par exemple pour appeler une fonction système. À ce stade, cet appel système peut faire tout ce dont votre ordinateur est capable, simplement parce que rien ne l'arrête.


Voulez-vous dire que ces bibliothèques écrites dans les autres langues sont déjà compilées à l'aide d'autres compilateurs? Et puis il devrait y avoir une sorte de fichier d'interface reliant chaque appel de fonction fourni à C ++ par la bibliothèque à une version précompilée de la bibliothèque? Permettant ainsi au compilateur C ++ de savoir en quoi traduire ces appels?
Med Larbi Sentissi

2
@MedLarbiSentissi 1) Pas nécessairement d'autres compilateurs. Il est possible (et il arrive souvent que ce soit le cas) qu'un seul compilateur soit capable de compiler plusieurs langages, y compris l'assembly, et il peut même être capable de compiler C ++ avec l'assembly inline. 2) Selon le système et le compilateur spécifiques, rendre ces fonctions appelables depuis C ++ peut en effet se faire avec une sorte de fichier d'interface, mais ce type de fichier d'interface peut déjà être un en-tête C (ou même C ++) directement utilisable depuis C ++.

1
@MedLarbiSentissi: De nombreuses bibliothèques Windows sont compilées dans des fichiers dll qui contiennent leur propre interface, ainsi que le code. Vous pouvez jeter un œil à une dll et voir une liste des fonctions qu'elle vous permet d'utiliser. Ils sont souvent accompagnés d'un fichier d'en-tête C. Lorsque vous créez votre exe, il contient une liste de DLL dont il a besoin pour fonctionner. Lorsque le système d'exploitation essaie de charger votre exe, il charge également automatiquement ces DLL avant de commencer l'exécution.
Mooing Duck

8
Cette réponse semble suggérer que la "magie" réside complètement dans d'autres langages appelés, mais en fait, la plupart du code qui constitue les systèmes d'exploitation les plus modernes est C (avec seulement des pièces très liées au matériel ou critiques pour les performances écrites en assembleur) - et il est certainement possible d'utiliser C ++ à la place. Le fait est qu'il n'y a pas de "magie", des langages sont créés pour construire des abstractions aussi puissantes, et une fois que vous pouvez interagir avec du matériel, les possibilités sont presque illimitées.
Matteo Italia

1
@hvd Je pense que tout le conflit dans cette discussion est que vous (et d'autres) définissez C comme les fonctionnalités qui lui sont spécifiées. En fait, les compilateurs ajoutent beaucoup plus que ce qui est spécifié, ce qui rend la question de savoir à quoi C est un peu simple. Pour moi, la particularité d'une langue (donc la chose qu'elle est) est la méta-manière d'exprimer le déroulement du programme et les possibilités de structurer. Les éléments qui sont structurés ne sont pas importants pour cela, car c'est juste un code ASM plus agréable qui peut être ajouté par les compilateurs comme ils le souhaitent
LionC

43

C et C ++ ont 2 propriétés qui permettent toute cette extensibilité dont parle l'OP.

  1. C et C ++ peuvent accéder à la mémoire
  2. C et C ++ peuvent appeler le code assembleur pour obtenir des instructions qui ne sont pas en langage C ou C ++.

Dans le noyau ou dans une plate-forme en mode non protégé de base, des périphériques comme le port série ou le lecteur de disque sont mappés dans la carte mémoire de la même manière que la RAM. La mémoire est une série de commutateurs et le basculement des commutateurs du périphérique (comme un port série ou un pilote de disque) permet à votre périphérique de faire des choses utiles.

Dans un système d'exploitation en mode protégé, lorsque l'on veut accéder au noyau depuis l'espace utilisateur (par exemple lors de l'écriture dans le système de fichiers ou pour dessiner un pixel sur l'écran), il faut effectuer un appel système. C n'a pas d'instructions pour effectuer un appel système mais C peut appeler du code assembleur qui peut déclencher l'appel système correct, c'est ce qui permet à son code C de parler au noyau.

Afin de faciliter la programmation d'une plate-forme particulière, les appels système sont enveloppés dans des fonctions plus complexes qui peuvent exécuter certaines fonctions utiles dans son propre programme. On est libre d'appeler directement les appels système (à l'aide de l'assembleur) mais il est probablement plus facile de simplement utiliser l'une des fonctions d'encapsuleur fournies par la plate-forme.

Il existe un autre niveau d'API qui est beaucoup plus utile qu'un appel système. Prenons par exemple le malloc. Non seulement cela appellera le système pour obtenir de gros blocs de mémoire, mais gérera cette mémoire en faisant tout le livre sur ce qui se passe.

Les API Win32 encapsulent certaines fonctionnalités graphiques avec un ensemble de widgets de plateforme commun. Qt va un peu plus loin en encapsulant l'API Win32 (ou X Windows) de manière multiplateforme.

Fondamentalement, bien qu'un compilateur C transforme le code C en code machine et puisque l'ordinateur est conçu pour utiliser le code machine, vous devez vous attendre à ce que C soit capable d'accomplir le partage lions ou ce qu'un ordinateur peut faire. Tout ce que les bibliothèques d'encapsuleurs font est de faire le gros du travail pour vous afin que vous n'ayez pas à le faire.


Mise en garde à propos de # 2: C et C ++ peuvent uniquement appeler des fonctions qui adhèrent à une "convention d'appel" que le compilateur comprend et attend. (Le code d'assembly peut utiliser n'importe quelle convention qu'il souhaite, voire aucune - donc le code ne peut pas être appelé directement.) Heureusement, chaque compilateur qui se respecte fournit une manière intégrée d'utiliser les conventions courantes de la plate-forme. (Les compilateurs Windows C, par exemple, vous permettent d'utiliser / utiliser des fonctions qui utilisent les conventions "cdecl", "stdcall" ou "fastcall".) Mais le code assembleur doit utiliser une convention connue du compilateur, sinon C et C ++ peuvent ' t l'appeler directement.
cHao

2
Aussi: les E / S mappées en mémoire sont courantes, mais pas toute l'histoire. Les PC, par exemple, adressent généralement les ports série, les lecteurs de disque, etc. en utilisant les «ports d'E / S» du x86, un mécanisme complètement différent. (Le tampon vidéo est généralement mappé en mémoire, mais les modes vidéo, etc. sont généralement contrôlés via des ports d'E / S.)
cHao

@cHao: Bien sûr, l'approche classique utilisant INP et OUTP se détourne en faveur du DMA; la génération PCI semble faire plus avec les registres de fonctions spéciales mappés en mémoire, maintenant qu'il existe un moyen de mapper automatiquement les périphériques vers des régions qui ne se chevauchent pas et de les découvrir à partir des pilotes, et moins avec les ports d'E / S.
Ben Voigt

les périphériques modernes utiliseront le DMA pour le transfert de données en masse, mais vous continuerez de programmer le contrôleur DMA avec une mémoire adressable
doron

@doron: Umm, vous programmez le contrôleur DMA via l'espace adresse E / S (pas l'espace mémoire) sur un PC, du moins si vous êtes sain d'esprit. Les processeurs x86 modernes aiment réorganiser les accès à la mémoire pour améliorer les performances. Avec MMIO, cela peut être désastreux ... vous devez donc faire attention à ne pas mettre ces adresses en cache et à mettre les instructions de sérialisation aux bons endroits. OTOH, le x86 lui-même garantit que les lectures et les écritures dans l'espace d'E / S sont effectuées dans l'ordre du programme. C'est pourquoi une grande partie des tâches importantes se fait toujours via l'espace d'E / S (qui n'est généralement pas accessible via un pointeur), et le sera probablement toujours.
cHao

23

Langues (comme C ++ 11 ) sont des spécifications , sur papier, généralement écrites en anglais. Regardez à l'intérieur de la dernière version de C ++ 11 (ou achetez la spécification finale coûteuse auprès de votre fournisseur ISO).

Vous utilisez généralement un ordinateur avec une implémentation de langage (vous pouvez en principe exécuter un programme C ++ sans ordinateur, par exemple en utilisant un tas d'esclaves humains qui l'interprètent; ce serait contraire à l'éthique et inefficace)

Votre implémentation C ++ générale fonctionne au-dessus d'un système d'exploitation et communique avec lui (en utilisant une implémentation spécifique code , souvent dans une bibliothèque système). Généralement, la communication se fait via les appels système . Recherchez par exemple dans syscalls (2) la liste des appels système disponibles sur le noyau Linux .

Du point de vue de l'application, un syscall est une instruction machine élémentaire comme SYSENTERsur x86-64 avec quelques conventions ( ABI )

Sur mon bureau Linux, les bibliothèques Qt sont au dessus des bibliothèques client X11 communiquant avec les protocoles Xorg Xu X11 du serveur X11 .

Sous Linux, utilisez lddsur votre exécutable pour voir la (longue) liste des dépendances sur les bibliothèques. Utilisez pmapsur votre processus en cours d'exécution pour voir lesquels sont "chargés" au moment de l'exécution. BTW, sous Linux, votre application n'utilise probablement que des logiciels libres, vous pouvez étudier son code source (de Qt, à Xlib, libc, ... le noyau) pour mieux comprendre ce qui se passe


2
Pour référence, ANSI vend la spécification C ++ 11 pour le prix légèrement moins scandaleux de 60 $ US. (Auparavant, c'était la moitié, mais l'inflation.: P) Il est étiqueté INCITS / ISO / IEC 14882, mais c'est au moins la même spécification de base que l'ISO offre. Pas sûr des errata / TR.
cHao

19

Je pense que le concept qui vous manque, ce sont les appels système . Chaque système d'exploitation fournit une énorme quantité de ressources et de fonctionnalités que vous pouvez exploiter pour effectuer des tâches liées au système d'exploitation de bas niveau. Même lorsque vous appelez une fonction de bibliothèque standard, il effectue probablement un appel système en arrière-plan.

Les appels système sont un moyen de bas niveau d'utiliser la puissance du système d'exploitation, mais ils peuvent être complexes et lourds à utiliser, ils sont donc souvent "encapsulés" dans les API afin que vous n'ayez pas à les traiter directement. Mais en dessous, à peu près tout ce que vous faites qui implique des ressources liées à l'O / S utilisera des appels système, y compris l'impression, la mise en réseau et les sockets, etc.

Dans le cas de Windows, Microsoft Windows a son interface graphique réellement écrite dans le noyau, il y a donc des appels système pour créer des fenêtres, peindre des graphiques, etc. Dans d'autres systèmes d'exploitation, l'interface graphique peut ne pas faire partie du noyau, auquel cas pour autant que je sache, il n'y aurait pas d'appels système pour les choses liées à l'interface graphique, et vous ne pourriez travailler qu'à un niveau encore plus bas avec les graphiques de bas niveau et les appels liés aux entrées disponibles.


3
Une chose importante qui manque, c'est que ces appels système ne sont en aucun cas magiques. Ils sont gérés par le noyau qui est généralement écrit en C (++). De plus, les appels système ne sont même pas nécessaires. Dans un système d'exploitation rudimentaire sans protection de la mémoire, les fenêtres pouvaient être dessinées en plaçant les pixels directement dans le tampon de cadre matériel.
el.pescado

15

Bonne question. Chaque nouveau développeur C ou C ++ a cela à l'esprit. Je suppose une machine x86 standard pour le reste de ce post. Si vous utilisez le compilateur Microsoft C ++, ouvrez votre bloc-notes et tapez ceci (nommez le fichier Test.c)

int main(int argc, char **argv)
{
   return 0
}

Et maintenant compilez ce fichier (en utilisant l'invite de commande du développeur) cl Test.c /FaTest.asm

Ouvrez maintenant Test.asm dans votre bloc-notes. Ce que vous voyez est le code traduit - C / C ++ est traduit en assembleur. Comprenez-vous l'indice?

_main   PROC
    push    ebp
    mov ebp, esp
    xor eax, eax
    pop ebp
    ret 0
_main   ENDP

Les programmes C / C ++ sont conçus pour fonctionner sur le métal. Ce qui signifie qu'ils ont accès à du matériel de niveau inférieur, ce qui facilite l'exploitation des capacités du matériel. Dis, je vais écrire une bibliothèque C getch () sur une machine x86.

Selon l'assembleur, je taperais quelque chose de cette façon:

_getch proc 
   xor AH, AH
   int 16h
   ;AL contains the keycode (AX is already there - so just return)
ret

Je l'écrase avec un assembleur et génère un .OBJ - Nommez-le getch.obj.

J'écris ensuite un programme C (je n'inclus rien)

extern char getch();

void main(int, char **)
{
  getch();
}

Nommez maintenant ce fichier - GetChTest.c. Compilez ce fichier en passant getch.obj. (Ou compilez individuellement vers .obj et LINK GetChTest.Obj et getch.Obj ensemble pour produire GetChTest.exe).

Exécutez GetChTest.exe et vous constaterez qu'il attend l'entrée du clavier.

La programmation C / C ++ ne concerne pas seulement le langage. Pour être un bon programmeur C / C ++, vous devez avoir une bonne compréhension du type de machine qu'il exécute. Vous aurez besoin de savoir comment la gestion de la mémoire est gérée, comment les registres sont structurés, etc., Vous n'aurez peut-être pas besoin de toutes ces informations pour une programmation régulière - mais elles vous aideraient énormément. Outre les connaissances matérielles de base, cela aide certainement si vous comprenez comment fonctionne le compilateur (c'est-à-dire comment il se traduit) - ce qui pourrait vous permettre de modifier votre code si nécessaire. C'est un package intéressant!

Les deux langues prennent en charge le mot clé __asm, ce qui signifie que vous pouvez également mélanger le code de votre langage d'assembly. L'apprentissage du C et du C ++ fera de vous un programmeur mieux arrondi dans l'ensemble.

Il n'est pas nécessaire de toujours établir un lien avec l'assembleur. Je l'avais mentionné parce que je pensais que cela vous aiderait à mieux comprendre. La plupart du temps, la plupart de ces appels de bibliothèque utilisent des appels système / API fournis par le système d'exploitation (le système d'exploitation à son tour fait les choses d'interaction matérielle).


10

Comment C ++ ... obtient-il soudainement de telles capacités grâce aux bibliothèques écrites en C ++ elles-mêmes?

Il n'y a rien de magique à utiliser d'autres bibliothèques. Les bibliothèques sont de simples gros sacs de fonctions que vous pouvez appeler.

Considérez que vous écrivez une fonction comme celle-ci

void addExclamation(std::string &str)
{
    str.push_back('!');
}

Maintenant, si vous incluez ce fichier, vous pouvez écrire addExclamation(myVeryOwnString); . Maintenant, vous pourriez vous demander: "Comment C ++ a-t-il soudainement pu ajouter des points d'exclamation à une chaîne?" La réponse est simple: vous avez écrit une fonction pour cela, puis vous l'avez appelée.

Donc, pour répondre à votre question sur la façon dont C ++ peut obtenir des capacités pour dessiner des fenêtres à travers des bibliothèques écrites en C ++, la réponse est la même. Quelqu'un d'autre a écrit des fonctions pour le faire, puis les a compilées et vous les a données sous la forme d'une bibliothèque.

Les autres questions répondent au fonctionnement du dessin de la fenêtre, mais vous sembliez confus quant au fonctionnement des bibliothèques, je voulais donc aborder la partie la plus fondamentale de votre question.


8

La clé est la possibilité pour le système d'exploitation d'exposer une API et une description détaillée de la façon dont cette API doit être utilisée.

Le système d'exploitation propose un ensemble d'API avec des conventions d'appel. La convention d'appel définit la façon dont un paramètre est donné dans l'API et comment les résultats sont renvoyés et comment exécuter l'appel réel.

Les systèmes d'exploitation et les compilateurs qui créent du code pour eux fonctionnent bien ensemble, vous n'avez donc généralement pas à y penser, il suffit de l'utiliser.


7

Il n'est pas nécessaire d'avoir une syntaxe spéciale pour créer des fenêtres. Tout ce qui est requis, c'est que le système d'exploitation fournit une API pour créer des fenêtres. Une telle API se compose d'appels de fonction simples pour lesquels C ++ fournit la syntaxe.

De plus, le C et le C ++ sont des soi-disant langages de programmation système et peuvent accéder à des pointeurs arbitraires (qui peuvent être mappés à un périphérique par le matériel). De plus, il est également assez simple d'appeler des fonctions définies dans l'assembly, ce qui permet la gamme complète des opérations fournies par le processeur. Par conséquent, il est possible d'écrire un système d'exploitation lui-même en utilisant C ou C ++ et une petite quantité d'assemblage.

Il convient également de mentionner que Qt est un mauvais exemple, car il utilise un soi-disant méta-compilateur pour étendre la syntaxe C ++. Cela n'est cependant pas lié à sa capacité à appeler les API fournies par le système d'exploitation pour réellement dessiner ou créer des fenêtres.


7

Tout d'abord, il y a un petit malentendu, je pense

Comment C ++, qui n'avait auparavant aucune syntaxe capable de demander au système d'exploitation une fenêtre ou un moyen de communiquer via des réseaux

Il n'y a pas de syntaxe pour effectuer des opérations OS. C'est la question de la sémantique .

obtenir soudainement de telles capacités grâce aux bibliothèques écrites en C ++ elles-mêmes

Eh bien, le système d'exploitation est écrit principalement en C. Vous pouvez utiliser des bibliothèques partagées (donc, dll) pour appeler le code externe. De plus, le code du système d'exploitation peut enregistrer des routines système sur des appels système * ou des interruptions que vous pouvez appeler à l'aide de l' assembly . Les bibliothèques partagées font souvent que le système appelle pour vous, vous êtes donc épargné par l'assemblage en ligne.

Voici le joli tutoriel à ce sujet: http://www.win.tue.nl/~aeb/linux/lk/lk-4.html
C'est pour Linux, mais les principes sont les mêmes.

Comment le système d'exploitation effectue-t-il les opérations sur les cartes graphiques, les cartes réseau, etc.? C'est un thème très large, mais la plupart du temps, vous devez accéder aux interruptions, aux ports ou écrire des données dans une région de mémoire spéciale. Comme ces opérations sont protégées, vous devez quand même les appeler via le système d'exploitation.


7

Pour essayer de donner une vue légèrement différente aux autres réponses, je répondrai comme ceci.

(Avertissement: je simplifie légèrement les choses, la situation que je donne est purement hypothétique et est écrite comme un moyen de démontrer des concepts plutôt que d'être 100% fidèle à la vie).

Pensez aux choses d'un autre point de vue, imaginez que vous venez d'écrire un système d'exploitation simple avec des capacités de gestion de thread, de fenêtrage et de mémoire de base. Vous voulez implémenter une bibliothèque C ++ pour permettre aux utilisateurs de programmer en C ++ et faire des choses comme créer des fenêtres, dessiner sur des fenêtres, etc. La question est de savoir comment faire cela.

Tout d'abord, puisque C ++ compile en code machine, vous devez définir un moyen d'utiliser le code machine pour interfacer avec C ++. C'est là que les fonctions entrent en jeu, les fonctions acceptent des arguments et donnent des valeurs de retour, elles fournissent donc un moyen standard de transférer des données entre différentes sections de code. Ils le font en établissant quelque chose connu sous le nom de convention d'appel .

Une convention d'appel indique où et comment les arguments doivent être placés en mémoire afin qu'une fonction puisse les trouver lors de son exécution. Lorsqu'une fonction est appelée, la fonction appelante place les arguments en mémoire, puis demande au CPU de passer à l'autre fonction, d'où il fait ce qu'il fait avant de revenir à l'endroit d'où il a été appelé. Cela signifie que le code appelé peut être absolument n'importe quoi et cela ne changera pas la façon dont la fonction est appelée. Dans ce cas cependant, le code derrière la fonction serait pertinent pour le système d'exploitation et fonctionnerait sur l'état interne du système d'exploitation.

Ainsi, plusieurs mois plus tard, toutes vos fonctions du système d'exploitation sont triées. Votre utilisateur peut appeler des fonctions pour créer des fenêtres et dessiner dessus, il peut créer des threads et toutes sortes de choses merveilleuses. Voici le problème cependant, les fonctions de votre système d'exploitation seront différentes des fonctions de Linux ou des fonctions de Windows. Vous décidez donc que vous devez donner à l'utilisateur une interface standard afin qu'il puisse écrire du code portable. C'est là qu'intervient QT.

Comme vous le savez presque certainement, QT dispose de nombreuses classes et fonctions utiles pour faire le genre de choses que font les systèmes d'exploitation, mais d'une manière qui semble indépendante du système d'exploitation sous-jacent. La façon dont cela fonctionne est que QT fournit des classes et des fonctions qui sont uniformes dans leur apparence pour l'utilisateur, mais le code derrière les fonctions est différent pour chaque système d'exploitation. Par exemple, QApplication :: closeAllWindows () de QT appelle en fait la fonction de fermeture de fenêtre spécialisée de chaque système d'exploitation en fonction de la version utilisée. Dans Windows, il appellerait très probablement CloseWindow (hwnd) tandis que sur un système d'exploitation utilisant le système X Window, il pourrait potentiellement appeler XDestroyWindow (affichage, fenêtre).

Comme il est évident, un système d'exploitation comporte de nombreuses couches, qui doivent toutes interagir via des interfaces de nombreuses variétés. Il y a de nombreux aspects que je n'ai même pas abordés, mais les expliquer tous prendrait beaucoup de temps. Si vous êtes davantage intéressé par le fonctionnement interne des systèmes d'exploitation, je vous recommande de consulter le wiki de développement d'OS .

Gardez à l'esprit que la raison pour laquelle de nombreux systèmes d'exploitation choisissent d'exposer des interfaces à C / C ++ est qu'ils compilent en code machine, ils permettent de mélanger les instructions d'assemblage avec leur propre code et ils offrent une grande liberté au programmeur.

Encore une fois, il se passe beaucoup de choses ici. Je voudrais continuer à expliquer comment les bibliothèques comme les fichiers .so et .dll ne doivent pas être écrites en C / C ++ et peuvent être écrites en assembleur ou dans d'autres langages, mais je pense que si j'ajoute plus, je pourrais aussi bien écrire un article entier, et autant que j'aimerais faire, je n'ai pas de site pour l'héberger.


6

Lorsque vous essayez de dessiner quelque chose à l'écran, votre code appelle un autre morceau de code qui appelle un autre code (etc.) jusqu'à ce qu'il y ait finalement un "appel système", qui est une instruction spéciale que le CPU peut exécuter. Ces instructions peuvent être écrites en assembleur ou écrites en C ++ si le compilateur prend en charge leurs "intrinsèques" (qui sont des fonctions que le compilateur gère "spécialement" en les convertissant en code spécial que le CPU peut comprendre). Leur travail consiste à dire au système d'exploitation de faire quelque chose.

Lorsqu'un appel système se produit, une fonction est appelée qui appelle une autre fonction (etc.) jusqu'à ce que le pilote d'affichage soit finalement invité à dessiner quelque chose sur l'écran. À ce stade, le pilote d'affichage examine une région particulière de la mémoire physique qui n'est en fait pas de la mémoire, mais plutôt une plage d'adresses qui peut être écrite comme s'il s'agissait de mémoire. Au lieu de cela, cependant, l'écriture dans cette plage d'adresses entraîne le matériel graphique pour intercepter l'écriture de la mémoire et dessiner quelque chose sur l'écran.
Écrire dans cette région de la mémoire est quelque chose qui pourrait être codé en C ++, car du côté logiciel, c'est juste un accès régulier à la mémoire. C'est juste que le matériel le gère différemment.
donc c'est une explication vraiment basique de la façon dont cela peut fonctionner.


4
Afaik un systemcall n'est pas vraiment une instruction CPU, et n'a rien à voir avec les intrinsèques. C'est plus une fonction du noyau du système d'exploitation, qui communique avec les périphériques.
MatthiasB

3
@MatthiasB: Eh bien, vous vous trompez, car syscall(et son cousin sysenter) est en effet une instruction CPU.
user541686

2
Ce n'était qu'un indice pour améliorer votre réponse, car ce n'était pas clair pour moi. Ne le voyez pas comme une attaque personnelle ou quoi que ce soit.
MatthiasB

1
@MatthiasB: Je ne le prends pas personnellement. Je dis que je sais déjà que la réponse n'est pas exacte à 100%, mais je pense que c'est une assez bonne simplification pour répondre à l'OP - donc, si vous connaissez réellement un moyen d'écrire une meilleure réponse, alors s'il vous plaît écrivez la vôtre répondre ou prendre le temps de modifier le mien. Je n'ai vraiment rien à ajouter qui, je pense, en vaille la peine, donc si vous voulez voir quelque chose de mieux sur cette page, vous devrez faire l'effort vous-même.
user541686

3
Les appels système sont effectués à l'aide d'interruptions logicielles. Des instructions telles que des sysenterchemins d'appel optimisés, car le changement de contexte utilisé par les gestionnaires d'interruptions n'était pas aussi rapide que tout le monde le souhaitait, mais fondamentalement, il s'agit toujours d'une interruption générée par logiciel tandis qu'elle est gérée par vectorisation vers un gestionnaire installé par le noyau du système d'exploitation. Une partie du processus de commutation de contexte qui est effectuée par la sysentermodification des bits de mode dans le processeur pour définir l'anneau 0 - accès complet à toutes les instructions privilégiées, registres et zones de mémoire et d'E / S.
Ben Voigt

4

Votre programme C ++ utilise la bibliothèque Qt (également codée en C ++). La bibliothèque Qt utilisera la fonction Windows CreateWindowEx (qui a été codée en C dans kernel32.dll). Ou sous Linux, il peut utiliser Xlib (également codé en C), mais il pourrait aussi bien envoyer les octets bruts qui dans le protocole X signifient " Veuillez créer une fenêtre pour moi ".

Liée à votre question de rattrapage est la note historique que «le premier compilateur C ++ a été écrit en C ++», bien qu'en réalité c'était un compilateur C avec quelques notions C ++, assez pour qu'il puisse compiler la première version, qui pourrait ensuite se compiler elle-même .

De même, le compilateur GCC utilise des extensions GCC: il est d'abord compilé dans une version puis utilisé pour se recompiler. (Instructions de construction GCC)


2

Comment je vois la question, c'est en fait une question de compilation.

Regardez-le de cette façon, vous écrivez un morceau de code dans Assembly (vous pouvez le faire dans n'importe quelle langue) qui traduit votre langage nouvellement écrit que vous voulez appeler Z ++ en Assembly, pour plus de simplicité, appelons-le un compilateur (c'est un compilateur) .

Maintenant, vous donnez à ce compilateur quelques fonctions de base, de sorte que vous pouvez écrire int, chaîne, tableaux, etc. en fait, vous lui donnez suffisamment de capacités pour que vous puissiez écrire le compilateur lui-même en Z ++. et maintenant vous avez un compilateur pour Z ++ écrit en Z ++, très bien à droite.

Ce qui est encore plus cool, c'est que vous pouvez maintenant ajouter des capacités à ce compilateur en utilisant les capacités qu'il possède déjà, étendant ainsi le langage Z ++ avec de nouvelles fonctionnalités en utilisant les fonctionnalités précédentes

Par exemple, si vous écrivez suffisamment de code pour dessiner un pixel dans n'importe quelle couleur, vous pouvez le développer à l'aide du Z ++ pour dessiner tout ce que vous voulez.


0

Le matériel est ce qui permet que cela se produise. Vous pouvez considérer la mémoire graphique comme un grand tableau (composé de chaque pixel de l'écran). Pour dessiner à l'écran, vous pouvez écrire dans cette mémoire à l'aide de C ++ ou de tout langage permettant un accès direct à cette mémoire. Cette mémoire se trouve être accessible par ou située sur la carte graphique.

Sur les systèmes modernes, l'accès direct à la mémoire graphique nécessiterait l'écriture d'un pilote en raison de diverses restrictions, vous utilisez donc des moyens indirects. Bibliothèques qui créent une fenêtre (vraiment juste une image comme n'importe quelle autre image) puis écrivent cette image dans la mémoire graphique que le GPU affiche ensuite à l'écran. Rien ne doit être ajouté au langage, sauf la possibilité d'écrire dans des emplacements de mémoire spécifiques, ce à quoi servent les pointeurs.


Le point que j'essayais de faire est qu'une langue n'a pas besoin de "se développer" dans le sens où une nouvelle version de la langue doit être réécrite, et qu'elle n'est pas vraiment circulaire car pour faire quoi que ce soit d'intéressant un programme doit s'interfacer avec le matériel.
john
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.