Comment se développe une langue?

J’apprends le C ++ et je viens de commencer à apprendre certaines des fonctionnalités de Qt pour coder des programmes graphiques. 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 du tout, j’admets), obtient soudainement de telles capacités via des bibliothèques écrites en C ++? Tout me semble terriblement circulaire. Quelles instructions C ++ pourriez-vous éventuellement proposer dans ces bibliothèques?

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

    Un ordinateur est comme un oignon, il a beaucoup de couches, du kernel interne du matériel pur à la couche d’application la plus extérieure. Chaque couche expose des parties d’elle-même à la couche externe suivante, de sorte que la couche externe peut utiliser certaines fonctionnalités des couches internes.

    Dans le cas, par exemple, de Windows, le système d’exploitation expose l’API WIN32 pour les applications exécutées 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 les signaux élecsortingques soient intégrés au matériel.

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

    Mais les bibliothèques n’ont pas besoin d’être écrites en C ++ pour pouvoir être utilisées 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 ++ ne fournisse 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 ++.

    A un niveau assez bas, certaines fonctions appelées par C ++ (ou par C) seront écrites dans l’assemblage, et l’assemblage contient les instructions nécessaires pour faire tout ce qui n’est pas possible (ou difficile) 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 qu’il n’y a rien qui l’arrête.

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

    1. C et C ++ peuvent accéder à la mémoire
    2. C et C ++ peuvent appeler un code d’assembly pour des instructions qui ne sont pas en langage C ou C ++.

    Dans le kernel ou dans une plate-forme en mode non protégé de base, des périphériques tels que 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 vous permet de faire des choses utiles grâce au port série ou au lecteur de disque.

    Dans un système d’exploitation en mode protégé, lorsque l’on veut accéder au kernel à partir de l’espace utilisateur (par exemple lors de l’écriture sur le système de fichiers ou pour dessiner un pixel à l’écran), il est nécessaire de passer un appel système. C n’a pas pour instruction de faire un appel système, mais C peut appeler un code assembleur pouvant déclencher l’appel système correct qui permet à son code C de communiquer avec le kernel.

    Afin de faciliter la programmation d’une plate-forme particulière, les appels système sont regroupés dans des fonctions plus complexes qui peuvent remplir une fonction utile dans leur propre programme. On est libre d’appeler directement les appels système (en utilisant l’assembleur), mais il est probablement plus facile de simplement utiliser l’une des fonctions d’encapsulation fournies par la plateforme.

    Il y a un autre niveau d’API qui est beaucoup plus utile qu’un appel système. Prenez par exemple malloc. Non seulement cela appellera le système à obtenir de gros blocs de mémoire, mais il gérera également cette mémoire en effectuant tout le suivi de ce qui se passe.

    Les API Win32 enveloppent certaines fonctionnalités graphiques avec un ensemble de widgets de plate-forme commun. Qt prend cela un peu plus loin en encapsulant l’API Win32 (ou X Windows) de manière multi-plateforme.

    Fondamentalement, bien qu’un compilateur C transforme le code C en code machine et que 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 font les librairies d’encapsulage est de faire le gros du travail pour que vous n’ayez pas à le faire.

    Les langues (comme C ++ 11 ) sont des spécifications sur papier, généralement écrites en anglais. Regardez dans le dernier brouillon C ++ 11 (ou achetez la spécification finale coûteuse auprès de votre fournisseur ISO).

    Vous utilisez généralement un ordinateur avec certaines implémentations de langage (vous pourriez en principe exécuter un programme C ++ sans ordinateur, par exemple en utilisant un groupe d’esclaves humains l’interprétant; cela serait contraire à l’éthique et inefficace)

    Votre implémentation générale C ++ fonctionne au-dessus de certains systèmes d’exploitation et communique avec elle (en utilisant un code spécifique à l’implémentation , souvent dans certaines bibliothèques système). Généralement, cette communication se fait par le biais d’ appels système . Recherchez par exemple syscalls (2) pour une liste des appels système disponibles sur le kernel Linux .

    Du sharepoint vue de l’application, un appel système est une instruction machine élémentaire telle que SYSENTER sur x86-64 avec certaines conventions ( ABI )

    Sur mon bureau Linux, les bibliothèques Qt sont au-dessus des bibliothèques client X11 communiquant avec le serveur X11 Xorg via les protocoles X Windows .

    Sous Linux, utilisez ldd sur votre exécutable pour voir la liste (longue) des dépendances sur les bibliothèques. Utilisez pmap sur votre processus en cours pour voir ceux qui sont “chargés” à l’exécution. BTW, sous Linux, votre application utilise probablement uniquement des logiciels libres, vous pouvez étudier son code source (de Qt à Xlib, libc, … le kernel) pour mieux comprendre ce qui se passe

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

    Les appels système sont un moyen simple de tirer parti de la puissance du système d’exploitation, mais ils peuvent être complexes et difficiles à utiliser. Ils sont donc souvent «intégrés» dans les API afin de ne pas avoir à les gérer directement. Mais en dessous, à peu près tout ce que vous faites impliquant des ressources liées à l’o / S utilisera les 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 kernel, 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 kernel. Autant que je sache, il n’y aurait pas d’appels système pour les éléments liés à l’interface graphique, et vous ne pouviez travailler qu’à un niveau encore plus bas avec les graphiques de bas niveau et les appels associés aux entrées.

    Bonne question. Tout nouveau développeur C ou C ++ a ceci en tête. Je suppose une machine x86 standard pour le rest de cet article. 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, comstackz ce fichier (en utilisant l’invite de commande du développeur) cl Test.c /FaTest.asm

    Maintenant, ouvrez Test.asm dans votre bloc-notes. Ce que vous voyez, c’est que le code traduit – C / C ++ est traduit en assembleur. Avez-vous le conseil?

     _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. Cela signifie qu’ils ont access à du matériel de bas niveau, ce qui facilite l’exploitation des capacités du matériel. Disons que je vais écrire un getch () sur une machine x86.

    Selon l’assembleur, je taperais quelque chose comme ceci:

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

    Je l’exécute avec un assembleur et génère un nom de fichier .OBJ – getch.obj.

    J’écris alors un programme C (je n’inclue rien)

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

    Maintenant, nommez ce fichier – GetChTest.c. Comstackz ce fichier en passant getch.obj avec. (Ou comstackr 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 uniquement le langage. Pour être un bon programmeur C / C ++, vous devez bien comprendre le 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’avez peut-être pas besoin de toutes ces informations pour la programmation régulière, mais ils vous aideraient énormément. Outre les connaissances matérielles de base, cela aide certainement si vous comprenez le fonctionnement du compilateur (c.-à-d. Comment il se traduit), ce qui pourrait vous permettre de modifier votre code si nécessaire. C’est un paquet intéressant!

    Les deux langues prennent en charge le mot-clé __asm, ce qui signifie que vous pouvez également mélanger votre code de langage d’assemblage. Apprendre C et C ++ fera de vous un programmeur plus complet.

    Il n’est pas nécessaire de toujours faire le lien avec Assembler. 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 les appels système / API fournis par le système d’exploitation (le système d’exploitation à son tour effectue les tâches d’interaction matérielle).

    Comment C ++ … obtient-il soudainement de telles capacités via les 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-vous écrire une fonction comme celle-ci

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

    Maintenant, si vous incluez ce fichier, vous pouvez écrire addExclamation(myVeryOwnSsortingng); . Maintenant, vous pourriez vous demander, “comment C ++ a-t-il soudain eu la possibilité d’append des points d’exclamation à une chaîne?” La réponse est simple: vous avez écrit une fonction pour le faire, puis vous l’avez appelée.

    Donc, pour répondre à votre question sur la manière dont C ++ peut tirer des capacités pour dessiner des fenêtres via 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 à la manière dont fonctionne le dessin de la fenêtre, mais vous avez semblé confus quant au fonctionnement des bibliothèques. Je voulais donc aborder la partie la plus fondamentale de votre question.

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

    Le système d’exploitation offre un ensemble d’API avec des conventions d’appel. La convention d’appel définit la manière 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, donc vous n’avez généralement pas à y penser, utilisez-le simplement.

    Il n’y a pas besoin de syntaxe spéciale pour créer des fenêtres. Il suffit que le système d’exploitation fournisse une API pour créer des fenêtres. Une telle API consiste en des appels de fonctions simples pour lesquels C ++ fournit une syntaxe.

    De plus, C et C ++ sont des langages de programmation de systèmes et peuvent accéder à des pointeurs arbitraires (qui peuvent être mappés à un périphérique par le matériel). En outre, il est également assez simple d’appeler des fonctions définies dans l’assemblage, 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 compilateur appelé méta pour étendre la syntaxe de C ++. Ceci n’est cependant pas lié à sa capacité à appeler les API fournies par le système d’exploitation pour dessiner ou créer des fenêtres.

    D’abord, il y a un petit malentendu, je pense

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

    Il n’y a pas de syntaxe pour effectuer les opérations du système d’exploitation. C’est la question de la sémantique

    obtenir soudainement de telles capacités grâce à des bibliothèques écrites en C ++ eux-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. En outre, le code du système d’exploitation peut enregistrer des routines système sur les appels système ou les interruptions que vous pouvez appeler à l’aide de assembly . Les bibliothèques partagées ne font souvent que faire appel à ce système, vous évitez donc l’assemblage en ligne.

    Voici le bon 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 des opérations sur les cartes graphiques, les cartes réseau, etc.? C’est un thème très vaste, mais vous devez surtout 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 les appeler via le système d’exploitation.

    Pour tenter de donner un sharepoint vue légèrement différent aux autres réponses, je répondrai comme ceci.

    (Disclaimer: 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 de l’autre sharepoint vue, imaginez que vous venez d’écrire un système d’exploitation simple avec des fonctionnalités de base de threading, de fenêtrage et de gestion de la mémoire. Vous voulez implémenter une bibliothèque C ++ pour laisser les utilisateurs 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.

    Tout d’abord, puisque le C ++ comstack 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, les fonctions acceptent les 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 appelé 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 lorsqu’elle est exécutée. Lorsqu’une fonction est appelée, la fonction appelante place les arguments en mémoire, puis demande au processeur de passer à l’autre fonction, où elle fait ce qu’elle fait avant de revenir à l’endroit d’où elle a été appelée. Cela signifie que le code appelé peut être absolument n’importe quoi et que 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.

    Donc, plusieurs mois plus tard, toutes les fonctions de votre système d’exploitation sont réglées. Votre utilisateur peut appeler des fonctions pour créer des fenêtres et les dessiner, elles peuvent créer des threads et toutes sortes de choses merveilleuses. Voici le problème cependant, les fonctions de votre système d’exploitation vont être différentes des fonctions de Linux ou des fonctions de Windows. Vous décidez donc que vous devez donner à l’utilisateur une interface standard pour qu’il puisse écrire du code portable. Voici où QT entre en jeu.

    Comme vous le savez sans doute, QT contient un grand nombre de classes et de 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 la façon dont elles apparaissent à l’utilisateur, mais le code derrière les fonctions est différent pour chaque système d’exploitation. Par exemple, QApplication :: closeAllWindows () de QT appellerait 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 probablement CloseWindow (hwnd) alors que sur un système d’exploitation utilisant le système X Window, il pourrait appeler XDestroyWindow (affichage, fenêtre).

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

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

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

    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 un “appel système”, une instruction spéciale que le processeur peut exécuter. Ces instructions peuvent être écrites en assembleur ou peuvent être écrites en C ++ si le compilateur prend en charge leurs “insortingnsèques” (qui sont des fonctions que le compilateur gère “spécialement” en les convertissant en code spécial compréhensible par le processeur). 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 et appelle une autre fonction (etc.) jusqu’à ce que le pilote d’affichage soit invité à dessiner quelque chose à l’écran. À ce stade, le pilote d’affichage regarde 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 dans laquelle il est possible d’écrire comme s’il s’agissait de mémoire. Au lieu de cela, l’écriture dans cette plage d’adresses provoque l’interception du matériel graphique par la mémoire et dessine quelque chose à l’écran.
    L’écriture dans cette région de la mémoire est quelque chose qui pourrait être codé en C ++, car côté logiciel, il ne s’agit que d’un access mémoire normal. C’est juste que le matériel le gère différemment.
    Donc, c’est une explication très simple de la façon dont cela peut fonctionner.

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

    En rapport avec votre question catch-22, il y a la note historique selon laquelle «le premier compilateur C ++ a été écrit en C ++», bien qu’il s’agisse d’un compilateur C avec quelques notions C ++, assez pour comstackr la première version. .

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

    Comment je vois la question c’est en fait une question du compilateur.

    Regardez comme ça, vous écrivez un morceau de code dans Assembly (vous pouvez le faire dans n’importe quel langage) qui traduit votre nouveau langage que vous voulez appeler Z ++ dans Assembly, pour simplifier, appelons cela un compilateur (c’est un compilateur) .

    Maintenant, vous donnez à ce compilateur des fonctions de base, de sorte que vous puissiez écrire int, ssortingng, tableaux, etc. En fait, vous lui donnez suffisamment de capacités pour pouvoir écrire le compilateur lui-même en Z ++. et maintenant vous avez un compilateur pour Z ++ écrit en Z ++, plutôt bien.

    Ce qui est encore plus cool, c’est que maintenant vous pouvez append des capacités à ce compilateur en utilisant les capacités qu’il possède déjà, élargissant 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 en utilisant le Z ++ pour dessiner ce que vous voulez.

    Le matériel est ce qui permet que cela se produise. Vous pouvez voir la mémoire graphique comme un grand tableau (composé de chaque pixel à l’écran). Pour dessiner à l’écran, vous pouvez écrire dans cette mémoire en utilisant C ++ ou tout autre langage permettant d’accéder directement à cette mémoire. Cette mémoire se trouve juste être accessible par ou situé sur la carte graphique.

    Sur les systèmes modernes, l’access direct à la mémoire graphique nécessiterait l’écriture d’un pilote en raison de diverses ressortingctions, de sorte que vous utilisiez des moyens indirects. Bibliothèques qui créent une fenêtre (en réalité une image comme toute autre image), puis écrivent cette image dans la mémoire graphique que le GPU affiche ensuite à l’écran. Rien ne doit être ajouté à la langue, à l’exception de la possibilité d’écrire dans des emplacements de mémoire spécifiques, et c’est ce à quoi servent les pointeurs.