Je maîtrise la programmation et j'ai rencontré des langages tels que BASIC, FORTRAN, COBOL, LISP, LOGO, Java, C++, C, MATLAB, Mathematica, Python, Ruby, Perl, JavaScript, Assembly et ainsi de suite. Je ne comprends pas comment les gens créent des langages de programmation et conçoivent des compilateurs pour cela. Je ne pouvais pas non plus comprendre comment les gens créent des OS comme Windows, Mac, UNIX, DOS et ainsi de suite. L'autre chose qui est mystérieuse pour moi est la façon dont les gens créent des bibliothèques comme OpenGL, OpenCL, OpenCV, Cocoa, MFC et ainsi de suite. La dernière chose que je ne peux pas comprendre, c'est comment les scientifiques conçoivent un langage d'assemblage et un assembleur pour un microprocesseur. J'aimerais vraiment apprendre toutes ces choses et j'ai 15 ans. J'ai toujours voulu être informaticien, comme Babbage, Turing, Shannon ou Dennis Ritchie.
J'ai déjà lu Aho's Compiler Design et le livre de concepts de Tanenbaum OS et ils ne parlent tous que de concepts et de code à un niveau élevé. Ils n'entrent pas dans les détails et les nuances et comment concevoir un compilateur ou un système d'exploitation. Je veux une compréhension concrète afin de pouvoir en créer une moi-même et pas seulement une compréhension de ce qu'est un fil, un sémaphore, un processus ou une analyse. J'ai demandé à mon frère tout cela. Il est un étudiant SB dans EECS à MIT et n'a pas la moindre idée de la façon de créer réellement toutes ces choses dans le monde réel. Tout ce qu'il sait, c'est juste une compréhension de la conception du compilateur et du système d'exploitation. des concepts comme ceux que vous avez mentionnés (c.-à-d. comme thread, synchronisation, concurrence, gestion de la mémoire, analyse lexicale, génération de code intermédiaire, etc.)
Fondamentalement, votre question est "comment les puces informatiques, les jeux d'instructions, les systèmes d'exploitation, les langages, les bibliothèques et les applications sont-ils conçus et mis en œuvre?" C'est une industrie mondiale de plusieurs milliards de dollars qui emploie des millions de personnes, dont beaucoup sont des spécialistes. Vous voudrez peut-être concentrer votre question un peu plus.
Cela dit, je peux essayer de:
Je ne comprends pas comment les gens créent des langages de programmation et conçoivent des compilateurs pour cela.
C'est surprenant pour moi, mais beaucoup de gens considèrent les langages de programmation comme magiques. Quand je rencontre des gens lors de fêtes ou quoi que ce soit, s'ils me demandent ce que je fais, je leur dis que je conçois des langages de programmation et implémente les compilateurs et les outils, et c'est surprenant le nombre de fois - des programmeurs professionnels, pensez-vous - disent "wow, je n'y ai jamais pensé, mais oui, quelqu'un doit concevoir ces choses". C'est comme s'ils pensaient que les langues surgissaient juste entièrement formées avec des infrastructures d'outils autour d'elles déjà.
Ils n'apparaissent pas seulement. Les langues sont conçues comme tout autre produit: en faisant soigneusement une série de compromis entre les possibilités concurrentes. Les compilateurs et les outils sont construits comme n'importe quel autre logiciel professionnel: en décomposant le problème, en écrivant une ligne de code à la fois, puis en testant le diable du programme résultant.
La conception de la langue est un énorme sujet. Si vous êtes intéressé par la conception d'une langue, un bon point de départ est de réfléchir aux lacunes d'une langue que vous connaissez déjà. Les décisions de conception découlent souvent de l'examen d'un défaut de conception dans un autre produit.
Vous pouvez également envisager un domaine qui vous intéresse, puis concevoir un langage spécifique au domaine (DSL) qui spécifie les solutions aux problèmes dans ce domaine. Vous avez mentionné LOGO; c'est un excellent exemple de DSL pour le domaine "dessin au trait". Les expressions régulières sont une DSL pour le domaine "trouver un modèle dans une chaîne". LINQ en C #/VB est un DSL pour le domaine "filtrer, joindre, trier et projeter les données". HTML est une DSL pour le domaine "décrire la mise en page du texte sur une page", etc. Il existe de nombreux domaines qui se prêtent à des solutions basées sur la langue. Un de mes favoris est Inform7, qui est une DSL pour le domaine "jeu d'aventure basé sur le texte"; c'est probablement le langage de programmation sérieux de plus haut niveau que j'ai jamais vu. Choisissez un domaine sur lequel vous savez quelque chose et réfléchissez à la façon d'utiliser le langage pour décrire les problèmes et les solutions dans ce domaine.
Une fois que vous avez esquissé à quoi vous voulez que votre langue ressemble, essayez d'écrire précisément quelles sont les règles pour déterminer ce qu'est un programme légal et illégal. En règle générale, vous souhaiterez le faire à trois niveaux:
Notez ces règles aussi précisément que possible. Si vous faites un bon travail, vous pouvez l'utiliser comme base pour écrire un compilateur ou un interpréteur. Jetez un œil à la spécification C # ou à la spécification ECMAScript pour voir ce que je veux dire; ils regorgent de règles très précises qui décrivent ce qui fait un programme juridique et comment comprendre ce que l'on fait.
L'une des meilleures façons de commencer à écrire un compilateur est d'écrire un compilateur de langage de haut niveau vers un langage de haut niveau . Écrivez un compilateur qui prend des chaînes dans votre langue et crache des chaînes en C # ou JavaScript ou dans la langue que vous connaissez; laissez le compilateur pour ce langage s'occuper de la lourde tâche de le transformer en code exécutable.
J'écris un blog sur la conception de C #, VB, VBScript, JavaScript et d'autres langages et outils; si ce sujet vous intéresse, consultez-le. http://blogs.msdn.com/ericlippert (historique) et http://ericlippert.com (actuel)
En particulier, vous pourriez trouver cet article intéressant; ici, je liste la plupart des tâches que le compilateur C # effectue pour vous lors de son analyse sémantique. Comme vous pouvez le voir, il y a beaucoup d'étapes. Nous décomposons le gros problème d'analyse en une série de problèmes que nous pouvons résoudre individuellement.
http://blogs.msdn.com/b/ericlippert/archive/2010/02/04/how-many-passes.aspx
Enfin, si vous cherchez un travail pour faire ce genre de choses lorsque vous êtes plus âgé, envisagez de venir chez Microsoft en tant que stagiaire universitaire et d'essayer d'entrer dans la division des développeurs. C'est comme ça que j'ai fini avec mon travail aujourd'hui!
Vous pourriez trouver Lets Build a Compiler par Jack Crenshaw une introduction intéressante à l'écriture de compilateurs et de langage d'assemblage.
L'auteur est resté très simple et s'est concentré sur la création de fonctionnalités réelles.
"Je voudrais vraiment aimer apprendre ce genre de choses". Si vous êtes sérieux à long terme:
Aller à l'université, se spécialiser en génie logiciel. Prenez toutes les classes de compilation que vous pouvez obtenir. Les personnes qui dispensent les cours sont mieux éduquées et plus expérimentées que vous; il est bon d'avoir leurs points de vue d'experts utilisés pour vous présenter les informations d'une manière que vous n'obtiendrez jamais en lisant du code.
Tenez-vous aux cours de mathématiques jusqu'au lycée et continuez au collège pendant les 4 années. Focus sur les mathématiques non standard: logique, théorie des groupes, méta-mathématiques. Cela vous obligera à penser de manière abstraite. Il vous permettra de lire les articles de théorie avancés sur la compilation et de comprendre pourquoi ces théories sont intéressantes et utiles. Vous pouvez ignorer ces théories avancées, si vous voulez toujours être derrière l'état de l'art.
Collectez/lisez les textes standard du compilateur: Aho/Ullman, etc. Ils contiennent ce que la communauté reconnaît généralement comme étant fondamental. Vous pourriez ne pas utiliser tout ce qui se trouve dans ces livres, mais vous devez savoir qu'il existe et savoir pourquoi vous ne l'utilisez pas. Je pensais que Muchnick était génial, mais c'est pour des sujets assez avancés.
Construisez un compilateur. Commencez MAINTENANT en construisant un pourri. Cela vous apprendra quelques problèmes. Construisez-en un deuxième. Répéter. Cette expérience crée une énorme synergie avec votre apprentissage du livre.
Un très bon point de départ est de se renseigner sur la BNF (Backus Naur Form), les analyseurs et les générateurs d'analyseurs. BNF est effectivement universellement utilisé dans le compilateur, et vous ne pouvez pas parler de manière réaliste à vos collègues compilateurs si vous ne le connaissez pas.
Si vous voulez une excellente première introduction à la compilation et la valeur directe de BNF non seulement pour la documentation mais comme un métalangage pouvant être traité par un outil, voyez ceci tutoriel (pas le mien) sur la construction de "méta" compilateurs (compilateurs qui construisent des compilateurs) basé sur un papier de 1964 (oui, vous avez bien lu) ["META II un langage d'écriture de compilateur orienté syntaxe" par Val Schorre. (http://doi.acm.org/10.1145/800257.808896)] Cette IMHO est l'un des meilleurs papiers comp-sci jamais écrits: il vous apprend à construire des compilateurs-compilateurs en 10 pages. J'ai d'abord appris de cet article.
Ce que j'ai écrit ci-dessus est beaucoup d'expérience personnelle, et je pense que cela m'a plutôt bien servi. YMMV, mais à mon humble avis, pas de beaucoup.
Voici un livre/cours en ligne que vous pouvez suivre appelé Les éléments des systèmes informatiques: construire un ordinateur moderne à partir des premiers principes .
À l'aide de simulateurs, vous construisez un système informatique complet à partir de zéro. Alors que de nombreux commentateurs ont déclaré que votre question est trop large, ce livre y répond en réalité tout en restant très gérable. Lorsque vous avez terminé, vous aurez écrit un jeu dans un langage de haut niveau (que vous avez conçu), qui utilise les fonctionnalités de votre propre système d'exploitation, qui est compilé dans une langue VM (que vous avez conçu) par votre compilateur, qui est traduit dans un langage d'assemblage (que vous avez conçu) par votre VM traducteur, qui est assemblé en code machine (que vous avez conçu) par votre assembleur, qui s'exécute sur votre système informatique que vous avez assemblé à partir de puces que vous avez conçues en utilisant une logique booléenne et un langage de description matérielle simple.
Les chapitres:
Plus de plaisir à parcourir
Prendre du recul. Un compilateur est simplement un programme qui traduit un document dans une langue en un document dans une autre langue. Les deux langues doivent être bien définies et spécifiques.
Les langages ne doivent pas nécessairement être des langages de programmation. Ils peuvent être toute langue dont les règles peuvent être écrites. Vous avez probablement vu Google Translate ; c'est un compilateur car il peut traduire une langue (disons l'allemand) dans une autre (le japonais, peut-être).
Un autre exemple de compilateur est un moteur de rendu HTML. Son entrée est un fichier HTML et la sortie est une série d'instructions pour dessiner les pixels sur l'écran.
Lorsque la plupart des gens parlent d'un compilateur, ils font généralement référence à un programme qui traduit un langage de programmation de haut niveau (tel que Java, C, Prolog) en un langage de bas niveau (code d'assemblage ou de machine). Cela peut être intimidant. Mais ce n'est pas si mal quand vous prenez l'avis d'un généraliste qu'un compilateur est un programme qui traduit une langue dans une autre.
Pouvez-vous écrire un programme qui inverse chaque mot d'une chaîne? Par exemple:
When the cat's away, the mice will play.
devient
nehW eht s'tac yawa, eht ecim lliw yalp.
Ce n'est pas un programme difficile à écrire, mais vous devez penser à certaines choses:
Les réponses à ces questions aident à bien définir la langue. Maintenant, allez-y et écrivez le programme. Félicitations, vous venez d'écrire un compilateur.
Que diriez-vous de ceci: pouvez-vous écrire un programme qui prend une série d'instructions de dessin et génère un fichier PNG (ou JPEG)? Peut-être quelque chose comme ça:
image 100 100
background black
color red
line 20 55 93 105
color green
box 0 0 99 99
Encore une fois, vous devrez réfléchir à la définition de la langue:
Bien sûr, il y a plus de questions à répondre mais si vous pouvez les clouer, vous avez défini un langage. Le programme que vous écrivez pour faire la traduction est, vous le devinez, un compilateur.
Vous voyez, écrire un compilateur n'est pas si difficile. Les compilateurs que vous avez utilisés dans Java ou C ne sont que des versions plus grandes de ces deux exemples. Alors allez-y! Définissez un langage simple et écrivez un programme pour que ce langage fasse quelque chose. Tôt ou plus tard, vous voudrez étendre votre langage. Par exemple, vous voudrez peut-être ajouter des variables ou des expressions arithmétiques. Votre compilateur deviendra plus complexe mais vous en comprendrez chaque élément parce que vous l'avez écrit vous-même. C'est comment les langues et les compilateurs se produisent.
Si vous êtes intéressé par la conception de compilateurs, consultez le Dragon Book (titre officiel: Compilateurs: principes, techniques et outils). Il est largement considéré comme un livre classique sur ce sujet.
Ne croyez pas qu'il y ait quelque chose de magique dans un compilateur ou un OS: il n'y en a pas. Vous vous souvenez des programmes que vous avez écrits pour compter toutes les voyelles dans une chaîne ou additionner les nombres dans un tableau? Un compilateur n'est pas différent dans son concept; c'est juste beaucoup plus grand.
Chaque programme comporte trois phases:
Pensez-y: qu'est-ce qui est entré dans le compilateur? Une chaîne de caractères d'un fichier source.
Quelle est la sortie du compilateur? Une chaîne d'octets qui représente les instructions de l'ordinateur à l'ordinateur cible.
Quelle est donc la phase "processus" du compilateur? Que fait cette phase?
Si vous considérez que le compilateur - comme tout autre programme - a pour inclure ces trois phases, vous aurez une bonne idée de la construction d'un compilateur.
"Construisons un compilateur" était déjà suggéré. Il existe une version "modernisée" utilisant Haskell au lieu de Turbo Pascal: http://alephnullplex.appspot.com/blog/view/2010/01/12/lbach-1-introduction
Fidèle à Haskell, il existe un interpréteur de Schéma très instructif qui pourrait donner d'autres idées: Ecrivez-vous un schéma en 48 heures
Je ne suis pas un expert, mais voici mon coup de couteau:
Vous ne semblez pas demander d'écrire un compilateur, juste un assembleur. Ce n'est pas vraiment magique.
Voler quelqu'un d'autre réponse de SO ( https://stackoverflow.com/questions/3826692/how-do-i-translate-Assembly-to-binary ), L'assemblage ressemble à ceci:
label: LDA #$00
JMP label
Ensuite, vous l'exécutez via un assembleur et vous vous transformez en quelque chose comme ceci:
$A9 $00
$4C $10 $00
Seulement, tout est écrasé, comme ceci:
$A9 $00 $4C $10 $00
Ce n'est vraiment pas magique.
Vous ne pouvez pas écrire cela dans le bloc-notes, car le bloc-notes utilise ASCII (pas hex). Vous utiliseriez un éditeur hexadécimal, ou écrivez simplement les octets par programme. Vous écrivez cet hex dans un fichier , nommez-le "a.exe" ou "a.out", puis dites au système d'exploitation de l'exécuter.
Bien sûr, les processeurs et systèmes d'exploitation modernes sont vraiment assez compliqués, mais c'est l'idée de base.
Si vous souhaitez écrire un nouveau compilateur, voici comment procéder:
1) Écrivez un langage interprété en utilisant quelque chose comme l'exemple de calculatrice dans le pyparsing (ou tout autre bon cadre d'analyse). Cela vous permettra de vous familiariser avec les bases de l'analyse.
2) Écrivez un traducteur. Traduisez votre langue en, disons, Javascript. Maintenant, votre langue s'exécutera dans un navigateur.
3) Écrivez un traducteur à un niveau inférieur, comme LLVM, C ou Assembly.
Vous pouvez vous arrêter ici, c'est un compilateur. Ce n'est pas un compilateur d'optimisation, mais ce n'était pas la question. Vous devrez peut-être également envisager d'écrire un éditeur de liens et un assembleur, mais le voulez-vous vraiment?
4) (Insane) Écrivez un optimiseur. De grandes équipes y travaillent depuis des décennies.
4) (Sane) Impliquez-vous dans une communauté existante. GCC, LLVM, PyPy, l'équipe principale travaillant sur n'importe quel interprète.
Plusieurs autres ont donné d'excellentes réponses. Je vais juste ajouter quelques suggestions supplémentaires. Tout d'abord, un bon livre pour ce que vous essayez de faire est les textes de mise en œuvre du compilateur moderne d'Appel (faites votre choix parmi C , Java =, ou ML standard ). Ce livre vous guide à travers une implémentation complète d'un compilateur pour un langage simple, Tiger, vers MIPS Assembly qui peut être exécuté dans un émulateur, avec une bibliothèque de support d'exécution minimale. Pour un seul passage à travers tout le nécessaire pour faire fonctionner une langue compilée, c'est un très bon livre1.
Appel vous expliquera comment compiler un langage pré-conçu, mais ne passe pas beaucoup de temps sur ce que signifient les différentes fonctionnalités du langage ou comment les considérer en termes de mérites relatifs pour concevoir le vôtre. Pour cet aspect, Langages de programmation: concepts et constructions est décent. Concepts, techniques et modèles de programmation informatique est également un bon livre pour réfléchir profondément à la conception d'un langage, bien qu'il le fasse dans le contexte d'un langage unique ( Oz ).
Enfin, j'ai mentionné qu'Appel a son texte en C, Java et ML standard - si vous êtes sérieux au sujet de la construction du compilateur et des langages de programmation, je recommande d'apprendre ML et d'utiliser cette version d'Appel. Les langages de la famille ML ont des systèmes de types forts qui sont principalement fonctionnels - des fonctionnalités qui seront différentes de nombreuses autres langues, donc les apprendre si vous ne connaissez pas déjà un langage fonctionnel affinera votre métier de langage. De plus, leurs mentalités d'appariement de motifs et fonctionnelles sont extrêmement bien adaptées aux types de manipulations que vous devez souvent effectuer dans un compilateur, de sorte que les compilateurs écrits dans des langages basés sur ML sont généralement beaucoup plus courts et plus faciles à comprendre que les compilateurs écrits en C, Java ou langages similaires. le livre de Harper sur Standard ML est un très bon guide pour vous aider à démarrer; ce travail devrait vous préparer à vous attaquer au livre d'implémentation du compilateur Standard ML d'Appel. Si vous apprenez Standard ML, il sera également assez facile de choisir OCaml pour un travail ultérieur; IMO, il a un meilleur outillage pour le programmeur qui travaille (s'intègre plus proprement à l'environnement OS environnant, produit facilement des programmes exécutables et possède des outils de construction de compilateurs spectaculaires comme ulex et Menhir).
1Pour une référence à long terme, je préfère le Dragon Book, car il contient plus de détails sur les choses auxquelles je vais probablement faire référence, comme le fonctionnement interne des algorithmes d'analyse syntaxique et a une couverture plus large des différentes approches, mais le livre d'Appel est très bon pour un premier passage. Fondamentalement, Appel vous enseigne une façon de faire les choses tout au long du compilateur et vous guide à travers celui-ci. Le Dragon Book couvre plus en détail différentes alternatives de conception, mais fournit beaucoup moins de conseils sur la façon de faire fonctionner quelque chose.
Modifié : remplacez la référence Aho incorrecte par Sethi, mentionnez CTMCP.
J'ai dû créer un compilateur pour les cours au collège.
Les bases de cette opération ne sont pas aussi compliquées que vous ne le pensez. La première étape consiste à créer votre grammaire. Pensez à la grammaire de la langue anglaise. De la même manière, vous pouvez analyser une phrase si elle a un sujet et un prédicat. Pour en savoir plus à ce sujet, lisez Grammaires sans contexte .
Une fois que vous avez la grammaire (les règles de votre langue), écrire un compilateur est aussi simple que de simplement suivre ces règles. Les compilateurs se traduisent généralement en code machine, mais à moins que vous ne vouliez apprendre x86, je vous suggère de regarder MIPS ou de créer votre propre machine virtuelle.
Les compilateurs ont généralement deux parties, un scanner et un analyseur. Fondamentalement, le scanner lit le code et le sépare en jetons. L'analyseur analyse la structure de ces jetons. Ensuite, le compilateur passe en revue et suit quelques règles assez simples pour le convertir dans le code dans lequel vous en avez besoin (Assembly, code intermédiaire comme bytecode, etc.). Si vous le décomposez en morceaux de plus en plus petits, cela n'est finalement pas intimidant du tout.
Bonne chance!
Le livre de Petzold Code est une excellente introduction aux non-techniciens et aux techniciens à partir des premiers principes. Il est très lisible et vaste dans sa portée sans trop s'enliser.
Maintenant que j'ai écrit cela, je vais devoir le relire.
Il y a d'excellentes réponses dans ce fil, mais je voulais juste ajouter la mienne car j'avais moi aussi une fois posé la même question. (Je voudrais également souligner que le livre proposé par Joe-Internet est une excellente ressource.)
La première est la question de savoir comment fonctionne un ordinateur? Voici comment: Entrée -> Calcul -> Sortie.
Considérons d'abord la partie "Calculer". Nous verrons plus tard comment fonctionnent les entrées et les sorties.
Un ordinateur se compose essentiellement d'un processeur (ou CPU) et de mémoire (ou RAM). La mémoire est une collection d'emplacements dont chacun peut stocker un nombre fini de bits, et chacun de ces emplacements mémoire peut lui-même être référencé par un nombre, c'est ce qu'on appelle l'adresse de l'emplacement mémoire. Le processeur est un gadget qui peut extraire des données à partir de la mémoire, effectuer certaines opérations sur la base des données et réécrire certaines données dans la mémoire. Comment le processeur sait-il quoi lire et quoi faire après avoir lu les données de la mémoire?
Pour répondre à cela, nous devons comprendre la structure d'un processeur. Ce qui suit est une vue assez simple. Un processeur se compose essentiellement de deux parties. L'un est un ensemble d'emplacements de mémoire construits à l'intérieur du processeur qui servent de mémoire de travail. Ils sont appelés "registres". Le second est un ensemble de machines électroniques construites pour effectuer certaines opérations en utilisant les données des registres. Il y a deux registres spéciaux appelés le "compteur de programmes" ou le pc et le "registre d'instructions" ou l'ir. Le processeur considère que la mémoire est partitionnée en trois parties. La première partie est la "mémoire programme", qui stocke le programme informatique en cours d'exécution. Le second est la "mémoire de données". Le troisième est utilisé à des fins spéciales, nous en parlerons plus tard. Le compteur de programmes contient l'emplacement de la prochaine instruction à lire dans la mémoire de programmes. Le compteur d'instructions contient un nombre qui fait référence à l'opération en cours. Chaque opération qu'un processeur peut effectuer est désignée par un numéro appelé l'opcode de l'opération. Le fonctionnement d'un ordinateur consiste essentiellement à lire l'emplacement de mémoire référencé par le compteur de programmes dans le registre d'instructions (et à incrémenter le compteur de programmes de sorte qu'il pointe vers l'emplacement de mémoire de l'instruction suivante). Ensuite, il lit le registre d'instructions et effectue l'opération souhaitée. Par exemple, l'instruction pourrait être de lire un emplacement de mémoire spécifique dans un registre, ou d'écrire dans un registre ou d'effectuer une opération en utilisant les valeurs de deux registres et d'écrire la sortie dans un troisième registre.
Maintenant, comment l'ordinateur effectue-t-il les entrées/sorties? Je vais fournir une réponse très simplifiée. Voir http://en.wikipedia.org/wiki/Input/output et http://en.wikipedia.org/wiki/Interrupt . pour plus. Il utilise deux choses, cette troisième partie de la mémoire et quelque chose appelé Interruptions. Chaque appareil connecté à un ordinateur doit pouvoir échanger des données avec le processeur. Il le fait en utilisant la troisième partie de la mémoire mentionnée précédemment. Le processeur alloue une tranche de mémoire à chaque périphérique et le périphérique et le processeur communiquent via cette tranche de mémoire. Mais comment le processeur sait-il quel emplacement se réfère à quel appareil et quand un appareil doit-il échanger des données? C’est là que les interruptions entrent en jeu. Une interruption est essentiellement un signal au processeur pour suspendre ce qu’il est actuellement et sauvegarder tous ses registres à un emplacement connu, puis commencer à faire autre chose. Il y a de nombreuses interruptions, chacune étant identifiée par un numéro unique. Pour chaque interruption, un programme spécial lui est associé. Lorsque l'interruption se produit, le processeur exécute le programme correspondant à l'interruption. Maintenant, selon le bios et la façon dont les périphériques matériels sont connectés à la carte mère de l'ordinateur, chaque périphérique reçoit une interruption unique et une tranche de mémoire. Lors du démarrage du système d'exploitation à l'aide du bios, détermine l'interruption et l'emplacement de la mémoire de chaque périphérique et configure les programmes spéciaux pour l'interruption afin de gérer correctement les périphériques. Ainsi, lorsqu'un appareil a besoin de données ou souhaite envoyer des données, il signale une interruption. Le processeur suspend ce qu'il fait, gère l'interruption, puis revient à ce qu'il fait. Il existe de nombreux types d'interruptions, par exemple pour le disque dur, le clavier, etc. Le temporisateur système, qui appelle une interruption à intervalles réguliers, est important. Il existe également des opcodes qui peuvent déclencher des interruptions, appelées interruptions logicielles.
Maintenant, nous pouvons presque comprendre comment fonctionne un système d'exploitation. Lorsqu'il démarre, le système d'exploitation configure une interruption de la minuterie, de sorte qu'il donne le contrôle du système d'exploitation à intervalles réguliers. Il configure également d'autres interruptions pour gérer d'autres périphériques, etc. Maintenant, lorsque l'ordinateur exécute un tas de programmes et que l'interruption du minuteur se produit, le système d'exploitation prend le contrôle et effectue des tâches importantes telles que la gestion des processus, la gestion de la mémoire, etc. une manière abstraite pour les programmes d'accéder aux périphériques matériels, plutôt que de les laisser accéder directement aux périphériques. Lorsqu'un programme souhaite accéder à un périphérique, il appelle du code fourni par le système d'exploitation qui communique ensuite avec le périphérique. Il y a beaucoup de théorie impliquée dans ceux-ci qui traite de la concurrence, des threads, des verrous, de la gestion de la mémoire, etc.
Maintenant, on peut en théorie écrire un programme directement en utilisant des opcodes. C'est ce qu'on appelle le code machine. C'est évidemment très douloureux. Désormais, un langage d'assemblage pour le processeur n'est rien d'autre que des mnémoniques pour ces opcodes, ce qui facilite l'écriture de programmes. Un assembleur simple est un programme qui prend un programme écrit en Assembly et remplace les mnémoniques par les opcodes appropriés.
Comment procéder pour concevoir un processeur et un langage d'assemblage. Pour savoir qu'il faut lire quelques livres sur l'architecture informatique. (voir les chapitres 1-7 du livre référencé par joe-internet). Cela implique d'apprendre l'algèbre booléenne, comment construire des circuits combinatoires simples pour ajouter, multiplier, etc., comment construire de la mémoire et des circuits séquentiels, comment construire un microprocesseur et ainsi de suite.
Maintenant, comment écrit-on des langages informatiques. On pourrait commencer par écrire un simple assembleur en code machine. Utilisez ensuite cet assembleur pour écrire un compilateur pour un simple sous-ensemble de C. Utilisez ensuite ce sous-ensemble de C pour écrire une version plus complète de C. Enfin, utilisez C pour écrire un langage plus compliqué tel que python = ou C++. Bien sûr, pour écrire un langage, vous devez d'abord le concevoir (de la même manière que vous décrivez un processeur). Encore une fois, regardez quelques manuels à ce sujet.
Et comment écrire un os. Vous ciblez d'abord une plate-forme telle que x86. Ensuite, vous déterminez comment il démarre et quand votre système d'exploitation sera-t-il invoqué. Un PC typique démarre de cette façon. Il démarre et le bios effectue quelques tests. Ensuite, le bios lit le premier secteur du disque dur et charge le contenu à un emplacement spécifique dans la mémoire. Ensuite, il configure le processeur pour commencer à exécuter ces données chargées. C'est le point où vous êtes invoqué. Un os typique à ce stade charge le reste de sa mémoire. Ensuite, il initialise les appareils et configure d'autres choses et enfin il vous accueille avec l'écran de connexion.
Donc, pour écrire un système d'exploitation, vous devez écrire le "chargeur de démarrage". Ensuite, vous devez écrire du code pour gérer les interruptions et les périphériques. Ensuite, vous devez écrire tout le code pour la gestion des processus, la gestion des périphériques, etc. Ensuite, vous devez écrire une API qui permet aux programmes exécutés dans votre système d'exploitation d'accéder aux périphériques et autres ressources. Et enfin, vous devez écrire du code qui lit un programme à partir du disque, le configure comme un processus et commence à l'exécuter.
Bien sûr, ma réponse est ouvertement simplifiée et probablement de peu d’utilité pratique. Pour ma défense, je suis maintenant un étudiant diplômé en théorie, donc j'ai oublié beaucoup de ces choses. Mais vous pouvez google beaucoup de ces trucs et en savoir plus.
Vous voudrez peut-être vérifier cette excellente question (et ses réponses) sur StackOverflow: Apprendre à écrire un compilateur . Il contient une large liste de ressources.
Je me souviens d'un moment de ma carrière en programmation où j'étais dans un état de confusion similaire au vôtre: j'avais beaucoup lu la théorie, le livre Dragon, le livre Tiger (rouge), mais je n'avais toujours pas grand-chose de un indice sur la façon de tout assembler.
Ce qui a fait le lien, c'est de trouver un projet concret pour faire (puis de découvrir que je n'avais besoin que d'un petit sous-ensemble de toute la théorie).
Le Java VM m'a fourni un bon point de départ: c'est conceptuellement un "processeur" mais il est très abstrait des détails désordonnés des processeurs réels. Il offre également une partie importante et souvent négligée du processus d'apprentissage: démonter les choses avant de les remonter (comme les enfants avaient l'habitude de faire avec les postes de radio dans le temps).
Jouez avec un décompilateur et la classe Hello, World en Java. Lisez la spécification JVM et essayez de comprendre ce qui se passe. Cela vous donnera un aperçu concret de ce que le compilateur est faire.
Ensuite, jouez avec du code qui crée la classe Hello, World. (En fait, vous créez un compilateur spécifique à l'application, pour un langage hautement spécialisé dans lequel vous ne pouvez dire que Bonjour, World.)
Essayez d'écrire du code qui pourra lire dans Hello, World écrit dans une autre langue, et afficher la même classe. Faites en sorte que vous puissiez changer la chaîne de "Hello, World" en quelque chose d'autre.
Essayez maintenant de compiler (en Java) une classe qui calcule une expression arithmétique, comme "2 * (3 + 4)". Démontez ce cours, écrivez un "compilateur de jouets" qui pourra le reconstituer.
1) Grandes conférences vidéo de l'Université de Washington:
Construction du compilateur CSE P 501 - Automne 2009 www.cs.washington.edu/education/courses/csep501/09au/lectures/video.html *
2) SICP http://groups.csail.mit.edu/mac/classes/6.001/abelson-sussman-lectures/ Et le livre du même nom. C'est en fait une obligation pour tout ingénieur logiciel.
3) En outre, sur la programmation fonctionnelle, Haskell, le calcul lambda, la sémantique (y compris la dénotation) et la mise en œuvre du compilateur pour les langages fonctionnels. Vous pouvez commencer à partir de 2005-SS-FP.V10.2005-05-24.HDV si vous connaissez déjà Haskell. Les vidéos Uxx sont des réponses. Veuillez suivre les vidéos Vxx en premier.
http://video.s-inf.de/#FP.2005-SS-Giesl. (COt) .HD_Videoaufzeichnung
(les vidéos sont en anglais, mais les autres cours sont en allemand.)
ANTLR est un bon point de départ. C'est un framework de génération de langage, similaire à Lex et Yacc. Il y a un GUI appelé ANTLRWorks qui simplifie le processus.
Dans le monde .NET, il y a Dynamic Language Runtime qui peut être utilisé pour générer du code dans le monde .NET. J'ai écrit un langage d'expression appelé Zentrum qui génère du code à l'aide du DLR. Il vous montrera comment analyser et exécuter des expressions typées de manière statique et dynamique.
Si tout ce que vous dites est vrai, vous avez le profil d'un chercheur prometteur et une compréhension concrète ne peut être obtenue que d'une seule façon: étudier. Et je ne dis pas " Lisez tous ces livres d'informatique de haut nivea (spécialement ceux-ci ) écrits par ce génie !"; Je veux dire: il faut être avec des gens de haut niveau pour être un informaticien comme Charles Babbage, Alan Turing, Claude Shannon ou Dennis Ritchie. Je ne méprise pas les gens autodidactes (je suis l'un d'eux) mais il n'y a pas beaucoup de gens comme vous là-bas. Je recommande sérieusement Symbolic Systems Program (SSP) at Stanford University . Comme le dit leur site Web:
Le programme de systèmes symboliques (SSP) de l'Université de Stanford se concentre sur les ordinateurs et les esprits: les systèmes artificiels et naturels qui utilisent des symboles pour représenter l'information. SSP rassemble des étudiants et des professeurs intéressés par différents aspects de la relation homme-ordinateur, y compris ...
- sciences cognitives : étude de l'intelligence humaine, des langues naturelles et du cerveau comme processus de calcul;
- intelligence artificielle : doter les ordinateurs d'un comportement et d'une compréhension de type humain; et
- interaction homme-machine : conception de logiciels et d'interfaces qui fonctionnent bien avec les utilisateurs humains.
Je vais suggérer quelque chose un peu en dehors du champ de gauche: apprendre Python (ou peut-être Ruby, mais j'ai beaucoup plus d'expérience en Python donc c'est ce que je vais discuter). Et pas seulement se plonger dedans, mais vraiment apprendre à le connaître à un niveau profond.
Il y a plusieurs raisons pour lesquelles je suggère ceci:
Python est un langage exceptionnellement bien conçu. Bien qu'il ait quelques verrues, il a moins de mon humble avis que de nombreuses autres langues. Si vous êtes un concepteur de langues en herbe, il est bon de vous exposer à autant de bonnes langues que possible.
L'implémentation standard de Python (CPython) est open-source et bien documentée, ce qui facilite la compréhension du fonctionnement du langage sous le capot.
Python est compilé en un code d'octet simple qui est plus facile à comprendre que Assembly et qui fonctionne de la même manière sur toutes les plates-formes Python fonctionne. Vous apprendrez donc sur la compilation (puisque Python compile votre code source en code octet) et interprétation (comme ce code octet est interprété dans la machine virtuelle Python).
Python a beaucoup de nouvelles fonctionnalités proposées, documentées dans des PEP numérotés (Python Enhancement Proposals). PEP intéressant à lire pour voir comment les concepteurs de langage ont envisagé d'implémenter une fonctionnalité avant de choisir la façon dont ils l'ont réellement fait. (Les PPE qui sont encore à l'étude sont particulièrement intéressants à cet égard.)
Python a un mélange de fonctionnalités de divers paradigmes de programmation, vous apprendrez donc comment aborder la résolution de problèmes et aurez un plus large éventail d'outils à envisager, y compris dans votre propre langage.
Python facilite l'extension du langage de différentes manières avec des décorateurs, des métaclasses, des hooks d'importation, etc. afin que vous puissiez jouer avec les nouvelles fonctionnalités du langage dans une certaine mesure sans vraiment quitter le langage. (En passant: les blocs de code sont des objets de première classe dans Ruby, donc vous pouvez réellement écrire de nouvelles structures de contrôle telles que des boucles! J'ai l'impression que Ruby ne considèrent pas nécessairement que l'extension de la langue, c'est juste comment vous programmez en Ruby. Mais c'est plutôt cool.)
En Python, vous pouvez réellement désassembler le bytecode généré par le compilateur, ou même écrire le vôtre à partir de zéro et demander à l'interpréteur de l'exécuter (je l'ai fait moi-même, et c'était époustouflant mais amusant).
Python possède de bonnes bibliothèques pour l'analyse. Vous pouvez analyser Python code dans une arborescence de syntaxe abstraite, puis le manipuler à l'aide du module AST. Le module PyParsing est utile pour analyser des langages arbitraires, tels que ceux Vous pouvez en théorie écrire votre premier compilateur de langage en Python si vous le vouliez (et il pourrait générer C, Assembly, ou même Python)).
Cette approche d'investigation pourrait bien aller avec une approche plus formelle, car vous commencerez à reconnaître les concepts que vous avez étudiés dans la langue avec laquelle vous travaillez, et vice versa.
S'amuser!
Pour une introduction simple sur la façon dont les compilateurs fonctionnent et comment créer votre propre langage de programmation, je recommanderais le nouveau livre http://createyourproglang.com qui se concentre davantage sur la théorie de la conception de langage sans avoir à connaître les composants internes du système d'exploitation/du processeur, c'est-à-dire les lexers, les analyseurs, les interprètes, etc.
Il utilise les mêmes outils que ceux utilisés pour créer les langages de programmation Coffee Script et Fancy récemment populaires.
Voir le livre de Kenneth Louden, "Compiler Construction"
http://www.cs.sjsu.edu/~louden/cmptext/
Il offre une meilleure approche pratique du développement du compilateur.
Les gens apprennent en faisant. Seul un petit nombre peut voir des symboles griffonnés sur le tableau et passer immédiatement de la théorie à la pratique. Malheureusement, ces gens sont souvent dogmatiques, fondamentalistes et les plus bruyants à ce sujet.
Eh bien, je pense que votre question pourrait être réécrite pour être: "Quels sont les concepts pratiques de base d'un diplôme en informatique", et la réponse totale est, bien sûr, d'obtenir votre propre baccalauréat en informatique.
Fondamentalement, vous créez votre propre compilateur de langage de programmation en lisant un fichier texte, en en extrayant des informations et en effectuant des transformations sur le texte en fonction des informations que vous en avez lues, jusqu'à ce que vous l'ayez transformé en octets pouvant être lus par le chargeur (cf, Linkers and Loaders by Levine). Un compilateur trivial est un projet assez rigoureux lorsqu'il est terminé pour la première fois.
Le cœur d'un système d'exploitation est le noyau, qui gère les ressources (par exemple, l'allocation/la désallocation de mémoire) et bascule entre les tâches/processus/programmes.
Un assembleur est une transformation texte-> octet.
Si vous êtes intéressé par ce genre de choses, je suggère d'écrire un assembleur X86, sous Linux, qui prend en charge un sous-ensemble de l'assemblage X86 standard. Ce sera un point d'entrée assez simple et vous présenterez ces problèmes. Ce n'est pas un projet de bébé et vous apprendra beaucoup de choses.
Je recommanderais de l'écrire en C; C est la lingua franca pour ce niveau de travail.
J'ai eu la chance d'être exposé au PDP-8 comme première langue de l'Assemblée. Le PDP-8 n'avait que six instructions, si simples qu'il était facile d'imaginer qu'elles étaient mises en œuvre par quelques composants discrets, ce qu'elles étaient en fait. Cela a vraiment supprimé la "magie" des ordinateurs.
Une autre passerelle vers la même révélation est le langage d'assemblage "mixte" que Knuth utilise dans ses exemples. "Mix" semble archaïque aujourd'hui, mais il a toujours cet effet mystifiant DE.
Les compilateurs et les langages de programmation (et tout, y compris dans la construction d'un - comme la définition d'une grammaire finie et la conversion en assembleur) est une tâche très complexe qui nécessite une grande compréhension des systèmes dans leur ensemble. Ce type de cours est généralement proposé en classe Comp Sci de 3e/4e année à l'université.
Je vous recommande fortement de commencer par mieux comprendre les systèmes d'exploitation en général et comment les langages existants sont compilés/exécutés (c'est-à-dire nativement (C/C++), dans un VM (Java) ou par un interprète (Python/Javascript)).
Je crois que nous avons utilisé le livre Operating System Concepts par Abraham Silberschatz, Peter B. Galvin, Greg Gagne dans mon cours sur les systèmes d'exploitation (en 2e année). C'était un excellent livre qui a donné une description complète de chaque composant d'un système d'exploitation - un peu cher mais en vaut la peine et les copies plus anciennes/utilisées devraient flotter.
C'est un gros sujet mais plutôt que de vous balayer avec un pompeux "allez lire un livre, gamin" à la place, je vous donnerai volontiers des conseils pour vous aider à envelopper votre tête autour de lui.
La plupart des compilateurs et/ou interprètes fonctionnent comme ceci:
Tokenize : Scannez le texte du code et divisez-le en une liste de jetons.
Cette étape peut être délicate car vous ne pouvez pas simplement diviser la chaîne sur des espaces, vous devez reconnaître que if (bar) foo += "a string";
est une liste de 8 jetons: Word, OPEN_PAREN, Word, CLOSE_PAREN, Word, ASIGNMENT_ADD, STRING_LITERAL, TERMINATOR. Comme vous pouvez le voir, le simple fractionnement du code source sur des espaces ne fonctionnera pas, vous devez lire chaque caractère comme une séquence, donc si vous rencontrez un caractère alphanumérique, vous continuez à lire des caractères jusqu'à ce que vous frappiez un caractère non alphanum et cette chaîne que vous il suffit de lire est un mot à classer plus tard. Vous pouvez décider vous-même de la granularité de votre tokenizer: s'il avale "a string"
Comme un token appelé STRING_LITERAL pour être analysé plus tard, ou s'il voit "a string"
Comme OPEN_QUOTE, UNPARSED_TEXT, CLOSE_QUOTE, ou autre , ce n'est qu'un des nombreux choix que vous devez décider par vous-même pendant que vous le codez.
Lex : Alors maintenant, vous avez une liste de jetons. Vous avez probablement marqué certains jetons avec une classification ambiguë comme Word, car lors de la première passe, vous ne consacrez pas trop d'efforts à essayer de comprendre le contexte de chaque chaîne de caractères. Alors, lisez à nouveau votre liste de jetons source et reclassifiez chacun des jetons ambigus avec un type de jeton plus spécifique en fonction des mots-clés de votre langue. Vous avez donc un mot tel que "si" et "si" est dans votre liste de mots-clés spéciaux appelés symbole IF, vous changez donc le type de symbole de ce jeton de Word en IF, et tout mot qui ne figure pas dans votre liste de mots-clés spéciaux , comme Word foo, est un IDENTIFICATEUR.
Parse : Alors maintenant, vous avez transformé if (bar) foo += "a string";
une liste de jetons lexés qui ressemble à ceci: IF OPEN_PAREN IDENTIFER CLOSE_PAREN IDENTIFIER ASIGN_ADD STRING_LITERAL TERMINATOR. L'étape consiste à reconnaître les séquences de jetons comme des instructions. C'est l'analyse. Pour ce faire, vous utilisez une grammaire telle que:
DÉCLARATION: = ASIGN_EXPRESSION | IF_STATEMENT
IF_STATEMENT: = IF, PAREN_EXPRESSION, STATEMENT
ASIGN_EXPRESSION: = IDENTIFIER, ASIGN_OP, VALUE
PAREN_EXPRESSSION: = OPEN_PAREN, VALUE, CLOSE_PAREN
VALEUR: = IDENTIFICATEUR | STRING_LITERAL | PAREN_EXPRESSION
ASIGN_OP: = EQUAL | ASIGN_ADD | ASIGN_SUBTRACT | ASIGN_MULT
Les productions qui utilisent "|" entre les termes signifie "correspondre à l'un d'eux", s'il y a des virgules entre les termes, cela signifie "correspondre à cette séquence de termes"
Comment l'utilisez-vous? En commençant par le premier jeton, essayez de faire correspondre votre séquence de jetons avec ces productions. Donc, d'abord, vous essayez de faire correspondre votre liste de jetons avec STATEMENT, vous lisez donc la règle pour STATEMENT et elle dit "un STATEMENT est soit un ASIGN_EXPRESSION ou un IF_STATEMENT" donc vous essayez de faire correspondre ASIGN_EXPRESSION d'abord, donc vous recherchez la règle de grammaire pour ASIGN_EXPRESSION et il dit "ASIGN_EXPRESSION est un IDENTIFICATEUR suivi d'un ASIGN_OP suivi d'une VALEUR, donc vous recherchez la règle de grammaire pour IDENTIFIER et vous voyez qu'il n'y a pas de rupture de grammaire pour IDENTIFIER, ce qui signifie IDENTIFIER un" terminal "ce qui signifie qu'il n'a pas besoin de plus analyse pour le faire correspondre afin que vous puissiez essayer de le faire correspondre directement avec votre jeton. Mais votre premier jeton source est un IF, et si ce n'est pas la même chose qu'un IDENTIFICATEUR, la correspondance a échoué. Et maintenant? Revenez à la règle STATEMENT et essayez pour faire correspondre le terme suivant: IF_STATEMENT. Vous recherchez IF_STATEMENT, il commence par IF, recherchez IF, IF est un terminal, comparez le terminal avec votre premier jeton, les correspondances de jetons IF, continuez génial, le terme suivant est PAREN_EXPRESSION, recherchez PAREN_EXPRESSION, ce n'est pas un terminal, quel est son premier terme, PAREN_EXPRESSION commence par OPEN_PAREN, recherchez OPEN_PAREN, c'est un terminal, faites correspondre OPEN_PAREN à votre prochain jeton, il correspond, .... et ainsi de suite.
La façon la plus simple d'aborder cette étape est d'avoir une fonction appelée parse () avec laquelle vous lui passez le jeton de code source que vous essayez de faire correspondre et le terme de grammaire avec lequel vous essayez de le faire correspondre. Si le terme de grammaire n'est pas un terminal, vous récursivement: vous appelez parse () en lui passant à nouveau le même jeton source et le premier terme de cette règle de grammaire. C'est pourquoi il s'agit d'un "analyseur de descente récursif". La fonction parse () renvoie (ou modifie) votre position actuelle en lisant les jetons source, elle renvoie essentiellement le dernier jeton de la séquence correspondante, et vous continuez le prochain appel à analyser () à partir de là.
Chaque fois que parse () correspond à une production comme ASIGN_EXPRESSION, vous créez une structure représentant ce morceau de code. Cette structure contient des références aux jetons source d'origine. Vous commencez à construire une liste de ces structures. Nous appellerons cette structure entière l'arbre de syntaxe abstraite (AST)
Compiler et/ou exécuter : pour certaines productions de votre grammaire, vous avez créé des fonctions de gestionnaire qui, si elles sont dotées d'une structure AST il compilerait ou exécuterait ce morceau d'AST.
Examinons donc la partie de votre AST qui a le type ASIGN_ADD. Donc, en tant qu'interprète, vous avez une fonction ASIGN_ADD_execute (). Cette fonction est transmise comme partie de la AST qui correspond à l'arbre d'analyse pour foo += "a string"
, donc cette fonction examine cette structure et sait que le premier terme de la structure doit être un IDENTIFICATEUR, et le deuxième terme est la VALEUR, donc ASIGN_ADD_execute () transmet le terme VALUE à une fonction VALUE_eval () qui renvoie un objet représentant la valeur évaluée en mémoire, puis ASIGN_ADD_execute () effectue une recherche de "foo" dans votre table de variables et stocke une référence à tout ce qui a été renvoyé par la valeur eval_value () une fonction.
Voilà un interprète. Un compilateur aurait à la place des fonctions de gestionnaire traduisant le AST en code octet ou code machine au lieu de l'exécuter.
Les étapes 1 à 3 et 4 peuvent être simplifiées à l'aide d'outils tels que Flex et Bison. (aka. Lex et Yacc), mais écrire un interprète à partir de zéro est probablement l'exercice le plus stimulant qu'un programmeur puisse réaliser. Tous les autres défis de programmation semblent triviaux après avoir atteint celui-ci.
Mon conseil est de commencer petit: une langue minuscule, avec une grammaire minuscule, et essayez d'analyser et d'exécuter quelques instructions simples, puis évoluez à partir de là.
Lisez-les et bonne chance!
http://www.iro.umontreal.ca/~felipe/IFT2030-Automne2002/Complements/tinyc.c
Le domaine informatique n'est compliqué que parce qu'il a eu le temps d'évoluer dans de nombreuses directions. En son cœur, il s'agit uniquement de machines qui calculent.
Mon ordinateur très basique préféré est l'ordinateur relais de Harry Porter . Il donne un aperçu du fonctionnement d'un ordinateur au niveau de base. Ensuite, vous pouvez commencer à comprendre pourquoi des choses comme les langues et les systèmes d'exploitation sont nécessaires.
Le truc, c'est qu'il est difficile de comprendre quoi que ce soit sans comprendre ce qui en a besoin . Bonne chance, et ne vous contentez pas de lire des trucs. Faites des trucs.