Den virtuella maskinen MrEd fungerar både som genomförandeplattform för DrScheme programmeringsmiljö, och som den underliggande Scheme-motorn för att utföra uttryck och program som ingår i DrSchemes läs-eval-print loop. Vi beskriver nyckelelementen i den virtuella maskinen MrEd för att bygga en programmeringsmiljö, och vi går igenom genomförandet av en miniatyrversion av DrScheme i Ed. Mer allmänt visar vi hur MrEd definierar ett operativsystem på hög nivå för grafiska program. med ett grafiskt användargränssnitt (GUI) t oo lb ox. MrEd fungerar både som genomförandeplattform för DrScheme och som underliggande systemmotor för att utföra uttryck och program som ingår i DrSchemes read-eval-print loop (REPL). Denna strategi följer en lång tradition av meta-cirkulärt genomförande som är praktiskt taget synonymt med Lisp, och allmänt förstås för högnivåspråk som helhet [20, 25, 281. Eftersom DrScheme exponerar MrEds språkkonstruktioner direkt till REPL, kan DrScheme enkelt köra program som använder hela MrEd språk, inklusive dess GTJI verktygslåda. Samtidigt måste DrScheme skydda sitt GUI mot störningar från de program som den utför, och det måste kunna stoppa ett program som har gått snett och *Denna forskning stöddes delvis av en Lodieska Stockbridge Vaughan Fellowship, NSF beviljar CCR-9619756, CDA-9713032, och CCR-9708957, och en Texas ATP bidrag. Att göra diaital- eller papperskopior av hela eller delar av detta verk för personligt bruk eller classroo&se beviljas utan avgift, förutsatt att kopiorna inte görs eller distribueras i vinstsyfte eller i kommersiellt syfte och att kopiorna är försedda med detta meddelande och det fullständiga angivandet på första sidan. För att kopiera på annat sätt, för att återpublicera, för att posta på servrar eller för att omfördela till listor, krävs tidigare specifik behörighet och/eller avgift. ICFP '99 9/99 Paris, Frankrike 0 1999 ACM l-581 13-l 1 l-9/99 0009... $5.00 återta programmets resurser-även om programmet och DrScheme delar en enda virtuell maskin. För att ta itu med detta problem tillhandahåller Ed en liten uppsättning nya språkkonstruktioner. Dessa konstruktioner gör det möjligt för ett programkörningsprogram, till exempel DrScheme, att köra inbäddade program direkt på den virtuella maskinen MrEd utan att offra kontrollen över de inhägnade programmen. Som ett resultat av detta kan DrScheme utföra en kopia av DrScheme som kör sin egen kopia av DrScheme (se figur 1). Den inre och mellersta DrSchemes kan inte störa driften av den yttre DrScheme, och den mellersta DrScheme kan inte störa den yttre DrSchemes kontroll över den inre DrScheme. I detta dokument beskriver vi de viktigaste delarna av den virtuella maskinen MrEd, och vi går igenom genomförandet av en miniatyrversion av DrScheme i Ed. Mer allmänt visar vi hur MrEd definierar ett operativsystem på hög nivå (OS) för grafiska program. Liksom i andra högnivå OS, säkerhet och säkerhet i:MrEd härrör från egenskaper hos det underliggande programmeringsspråket. Säkerheten ger dock varken den skyddsnivå mellan tillämpningarna eller den typ av processtyrning som konventionella operativsystem tillhandahåller. Sådant skydd och kontroll är avgörande för genomförandet av många typer av program, inklusive programmeringsmiljöer och skriptmotorer. Genom att beskriva hur vi implementerade DrScheme i MrEd visar vi hur vi kan få viktiga OS-anläggningar genom små tillägg till ett programmeringsspråk på hög nivå. Resten av tidningen är organiserad enligt följande. Avsnitt 2 skisserar en miniatyr DrScheme, kallad SchemeEsq, och förklarar mer i detalj genomförandeutmaningarna för att skapa en grafisk programmeringsmiljö. I avsnitt 3 ges en kort översikt över Ed. Avsnitt 4 steg genom genomförandet av SchemeEsq som ett MrEd-program. I avsnitt 5 förklaras hur MrEd fungerar som ett operativsystem på hög nivå. I avsnitt 6 diskuteras vissa problem för det framtida arbetet och i avsnitt 7 jämförs Ed med relaterade system. MrEds gör-objekt förfarande tar en klass och returnerar en instans av klassen. Extra argument för att göra-objekt fungerar som initiering argument för objektet, liknande argument som ges med nya i Java. För ram% klass, initiering argument anger ramens titel, dess överliggande fönster (#f om ingen), och dess ursprungliga storlek. Ovanstående ram benämns Exempel, har ingen förälder, och är 400 pixlar bred och 200 pixlar lång. De inbyggda klasserna tillhandahåller olika mekanismer för att hantera GUI-händelser, som Ed skickar automatiskt. Till exempel, när du direktierar knappen% klass, programmeraren tillhandahåller en händelse callback förfarande som ska åberopas när användaren klickar på knappen. Följande exempel skapar en knapp Stäng som döljer ramen när användaren klickar på knappen: För enkla kontroller, t.ex. knappar, är kontrollens åtgärd normalt instansspecifik, så åtgärden anges bäst som en callback i make-objekt vaser, händelsehantering är ofta vanligt för en klass av instanser, så metoden överskuggande ger en mer extensibel mekanism "Genom konvention, klassnamn slutar med ett procenttecken (%) i MrEd. Källkoden i detta dokument finns i MrEd version 100. MrEds GUI-klasser hanterar också den grafiska layouten av fönster. Vår exempelram visar en enkel layout; ramens element är uppradade uppifrån och ner. I allmänhet anger en programmerare l,ayout i ett fönster genom att tilldela varje element i det grafiska gränssnittet till en överliggande behållare. En vertikal behållare, till exempel en ram, ordnar sina barn i en kolumn, och en horisontell behållare ordnar sina barn i rad. En behållare kan vara ett barn till en annan behållare; till exempel för att placera två knappar sida vid sida i en ram, en programmerare skapar en horisontell panel för knapparna: (define panel (make-object horisontal-pane% ram)) (make-object knapp% "vänster" panel. ..) (göra-objekt knappen% "rätt" punkt. ..) En programmerare kan justera minsta bredd, lägsta höjd, horisontell töjbarhet och vertikal töjbarhet för varje element i det grafiska gränssnittet. Med hjälp av dessa inställningar väljer MrEd en initial storlek för varje ram, och den ändrar inställningar när användaren ändrar storlek på en ram. Containrar, Experience och Rationale: lådor ger en mängd olika mekanismer för geometriska hanterare med godtycklig layout strategier, vi implementerade DrScheme med endast vertikala och horisontella behållare. Förutom de grundläggande byggstenarna i det grafiska gränssnittet tillhandahåller MrEd en samling klasser som stöder ett brett spektrum av editorprogram, från ordprocessorer till ritprogram. Editorn ramverket behandlar ett brett spektrum av verkliga frågor för en editor-inklusive cut-and-paste, extensible filformat, och lager text stilar-samtidigt stödja en hög nivå av extensibilitet genom klasssystemet. och Rationale: Eds verktygslåda för redaktörer ger en grund för två vanliga typer av tillämpningar: 1. program som innehåller en sofistikerad textredigerare: MrEds enkla textfältskontroll är otillräcklig för textintensiva tillämpningar. Många program behöver redaktörer som kan hantera flera teckensnitt och icke-textobjekt. MrEds verktygslåda för teckning ger en allmän ritning SWface för plottning av linjer och lådor, men många program behöver en interaktiv duk, där användning? kan dra och ändra storlek på enskilda objekt. Verktygslådans kraft och flexibilitet gör den ganska komplex, och med hjälp av verktygslådan krävs en gedigen förståelse för dess struktur och terminologi. Men tillräckligt många tillämpningar passar en (eller båda) av beskrivningarna ovan för att motivera djupet och komplexiteten i verktygslådan och den inlärningsinvestering som krävs för att använda den. Konventionella operativsystem stöder flera program genom en process abstraktion som ger varje program sitt eget kontrollflöde, I/O-miljö, och resurskontroller. En process utmärker sig främst genom sitt adressutrymme, där separata adressutrymmen fungerar både som en skyddsbarriär mellan program och som en mekanism för att definiera ett programs miljö; t.ex. innehåller stdout global variabel i ett Unix C-program ett processspecifikt värde. I MrEd är separata adressutrymmen onödiga för skydd mellan program, på grund av säkerhetsegenskaperna hos programmeringsspråket. Icke desto mindre kräver separata program separat kontrollflöde, I/O-miljöer och resurskontroller. I stället för att tillhandahålla en allomfattande processabstraction tillhandahåller Ed specifika mekanismer för att skapa trådar av kontroll, hantera grafisk I/O och hantera resurser. Herr Eds tråd primitiv konsumerar ett förfarande utan argument och åberopar det i en ny tråd. Följande exempel ger upphov till en tråd som skriver ut "tick" varje sekund: Varje tråd behåller sin egen samling av systeminställningar, såsom den aktuella katalogen och den aktuella utmatningsanpassningen. Dessa inställningar kallas parametrar.3 En parameter ifrågasätts och ändras genom ett parameterförfarande, t.ex. ström-directory eller ström-output-port. Till exempel returnerar (current-directory) sökvägen för den aktuella katalogen, medan (current-directory dir) ställer in den aktuella katalogen till dir. Att ändra en parameter ändrar endast dess värde i den aktuella tråden. Därför, genom att ställa in aktuell-utgång-port i den tick-loop tråden, kan vi omdirigera "tick" utskrifter utan att ändra tick-loop och utan att påverka utgången av någon annan tråd: (tick-loop))) En nyIy-skapad tråd ärver parametervärden för den skapande tråden. Således, om tick-loop skapar sina egna trådar, de producerar också utdata till "ticks" filen. Parameter arv ger en alternativ mekanism för att ställa in utgångsporten i "ticking" tråden. Istället för att uttryckligen ställa in porten i den tickande tråden, kan vi tillfälligt ställa in porten i huvudtråden medan du skapar den tickande tråden: (parameterisera ((current-output-port (open-output-file "ticks"))) (trådig tick-loop) En parameterize uttryck anger värdet av en parameter under den dynamiska omfattningen av dess kropp. I exemplet ovan återställer parameterize utgångsporten för huvudtråden efter att den tickande tråden har skapats, men den tickande tråden ärver "ticks" som sin nuvarande utgångsport. Eftersom utgångsporten är inställd innan tick-loop kallas, har den tickande tråden inget sätt att komma åt den ursprungliga utgångsporten. På så sätt möjliggör parametrarna en säker anpassning av miljön i ett bobaserat program (eller någon otillförlitlig tråd).  En händelserymd i MrEd är ett sammanhang för att bearbeta GUI-händelser på ett sekventiellt sätt. Varje händelserymd upprätthåller sin egen kö av händelser, sin egen samling av ramar och sin egen hanteringstråd. MrEd skickar händelser inom en händelserymd synkront i hanteringstråden, samtidigt som han sänder händelser från olika händelserymder asynkront i separata hanteringstrådar. Att skapa en händelserymd startar en hanteringstråd för händelserymden implicit. Endast hanteringstråden skickar händelser, men alla trådar som delar en händelserymd kan köa händelser, och alla trådar (oavsett händelserymd) kan manipulera ett tillgängligt GUI-objekt.4 När en tråd skapar ett fönster på toppnivå, tilldelar den fönstret till nuvarande händelserymd som bestäms av parametern för aktuell händelserymd. Utrustade med de MrEd konstruktioner som definieras i föregående avsnitt, kan vi genomföra SchemeEsq-programmet som beskrivs i avsnitt 2. För det första skapar vi SchemeEsqs grafiska gränssnitt med hjälp av verktygslådan MrEd. Sedan använder vi trådar, eventrymder och väktare för att implementera säker utvärdering för REPL-uttryck. Slutligen diskuterar vi hur enheter och mixins låter oss förlänga SchemeEsq för att genomföra hela DrScheme miljö. När en användare träffar returtangenten utvärderar SchemeEsq uttrycket efter den aktuella prompten. SchemeEsq utvärderar slutligen detta uttryck genom att kalla det inbyggda eval-förfarandet. Men innan vi låter SchemeEsq ringa eval måste vi se till att den kod som utvärderas i REPL inte kan störa själva SchemeEsq, eftersom både SchemeEsq och användarens kod kör tillsammans i MrEd. För att genomföra SchemeEsqs grafiska gränssnitt skapar vi först en ram: (definiera ramen (make-object frame% "SchemeEsq" #f 400 200)) och göra den synlig: Naturligtvis får användarkoden inte få direkt tillgång till ramen eller redigeraren av SchemeEsq, eftersom det kan kalla metoder för objekten olämpligt. Vi kan dölja SchemeEsqs implementering från användarens program genom att sätta in den i en modul och göra alla definitioner privata. För närvarande fortsätter vi att definiera SchemeEsq genom definitioner på toppnivå, men bilagan visar det slutliga SchemeEsq-programmet inkapslat i en modul. (skicka bildspel #t) Nästa, vi skapar återställningsknappen för att visas högst upp i ramen: De återstående problemen gäller samspelet mellan kontrollflödet i användarens program och i SchemeEsq. Trådar med parametrar, händelseutrymmen och väktare ger exakt de mekanismer som behövs för att lösa dessa problem. (definiera återställningsknapp 4.2.1 Trådar i SchemeEsq (make-object-knappen% "Reset" ram (lambda (b e) (reset-program)))) Uppringningsförfarandet för återställningsknappen ignorerar dess argument och samtal återställning-program, som vi definierar senare. Slutligen skapar vi ett visningsområde för REPL, implementerat som en editor duk: (definiera repl-display-canvas En obegränsad beräkning i användarens program får inte fördröja SchemeEsqs grafiska gränssnitt. Annars skulle programmet hindra användaren från att klicka på SchemeEsqs återställningsknapp. För att undvika blockering av SchemeEsq på en REPL beräkning, utvärderar vi användaruttryck i en separat tråd. Följande är ett första försök att definiera utvärderingsförfarandet för att utvärdera användaruttryck? (make- object editor- canvas% ram)) Vid denna punkt, vår SchemeEsq GUl redan liknar Figur 2, men REPL är ännu inte aktiv. Den faktiska REPL implementeras som en textredigerare som visas av canvasen.7 Den grundläggande funktionaliteten som behövs i SchemeEsqs REPL inklusive hantering av tangentbordshändelser, rullning, och klippa och klistra in operationer-restes i MrEds text% editor klass. Esq-text% klass, som definieras i bilagan, anpassar texten% klass till behoven i REPL genom övergripande metoder för att specialisera redaktörens beteende. Till exempel, när redaktören får en Enter/Return-nyckelpress, kallas utvärderingsförfarandet (som vi definierar senare). Utöver hantering av indata definierar klassen esq-text% en metod för utskrift av utdata från användarens program till REPL-editorn. Eftersom användarens program kan skapa många trådar, utdatametoden behöver en speciell omslag för att konvertera multi-trådade utdata samtal till enkelgängad utdata. Den kö-output omslag utför denna omvandling genom att ändra en metod anrop till en köad, lågprioriterad GUI-händelse: (definiera (värdera ezpr-str) (tråd (lambda ()) (med-handlers ((ext? (lambda (ezn) (display (exn-message ezn)))) (skriv (eval (read (open-input-string ezpr-str)))) (ny linje) (skicka repl-editor new-prompt))))) Efter att ha skapat en tråd för att representera användarprocessen, måste vi konfigurera processens miljö. För enkelhetens skull definierar vi konfigurationen som att omdirigera utdata från användarens program (via visning eller skriva} till REPL-editorn. För att omdirigera utdata för användarens program, ställer vi in utdataporten i utvärderingstråden: (define (queue-output proc) (parameterize ((current-even&pace esq-eventspace)) (queue-callback proc #f))) (current-output-port user-output-port) ; 1 t add 1 (with-handlers Med den nya esq-text% klassen skapar vi en editor instans och installera den i displayen duk: (define repl-redigerare (make-object esq-text%)) (send rep/display-canvas set-editor repl-editor) SchemeEsq GUI är nu färdig, men vi har ännu inte implementerat utvärdera (används i esg-text%) och återställningsprogram (används av återställningsknappens callback). 7MrEd skiljer mellan en display och dess redaktör på samma sätt som Emacs skiljer mellan ett fönster och dess buffert. (display (exn-message ezn)))) (skriv (eval (read (open-input-string ezpr-str))))) (newline) (skicka repl-editor new-prompt))))) Ovanstående förutsätter att användar-output-portport fungerar som ett rör till REPL-editorn. Vi kan definiera användar-output-port med 143 make-output-port, en MrEd-procedur som skapar en port från godtycklig sträng-printing och port-stängande förfaranden: (definiera användar-output-port I denna användning av make-output-port, sträng-printing procedur skickar strängen till REPL-editorn, och port-stängning förfarande gör ingenting. (current-output-port user-output-port) (med-handlers) ((ext? (lambda (em) (display (exn-message ezn)))) (skriv (eval (read (open-input-string ezpr-str))))) (ny linje) (skicka repl-editor new-prompt)))))) Vi fullbordar SchemeEsq genom att genomföra återställningsknappens åtgärd med en depåhållare. Vi definierar user-contodian och skapar användarens händelserymd under hantering av usercustodian: (define user-contodian (make-contodian)) (define user-eventspace (parameterize (( current-custodian user-custodian))) (make-eventspace))) (define user-eventspace)) (define user-eventspace)) (define user-eventtspace) (define-e-eventspace)) För att genomföra återställningsprogrammet procedur för återställningsknappen, utfärdar vi ett avstängningskommando på oss 'w-custodian anal sedan återställa REPL-editorn: (define (reset-program) (contodian-shut-all user-custodian) (parameterize ((current-custodian user-custodian))) (set! user-eventspaclf! (make-eventspace)) (send repl-editor återställning) Varje återställning förstör användar-eve&space (genom att utfärda en avstängning. kommando till användarens förvar), vilket gör händelserymden oanvändbar. Därför skapar återställningsprogram ett nytt händelseutrymme efter varje återställning. Modularitet och extensibilitet i ScherneEsq Appendixet monterar de bitar som vi har utvecklat till ett fullständigt genomförande av SchemeEsq. Den mest, slående aspekten av SchemeEsq genomförande-förutsätter det faktum att det passar på en sida - är att hälften av koden finns för att köra REPL redaktören. I den verkliga DrScheme miljön är REPL betydligt mer komplex, och dess genomförande upptar en motsvarande stor del av DrScheme övergripande kod. Vid genomförandet av DrScheme tämjade vi komplexiteten i det grafiska gränssnittet genom att i stor utsträckning använda enheter och mixins. Till exempel, parentes-highlighting förlängning för en editor implementeras som en mixin i sin egen enhet, och det interaktiva sökgränssnittet är en annan mixin i en separat enhet. Med hjälp av enheter och mixins på detta sätt, den genomförandestrategi som vi har visat för SchemeEsq skalor till det mer detaljerade genomförandet av DrScheme. DrScheme utnyttjar även enheter för inbäddning av programliknande komponenter. Till exempel, DrSchemes hjälpsystem körs antingen som en fristående applikation eller inbäddad i DrScheme programmeringsmiljö. Hjälpsystemets enhet importerar en klass som definierar en grundläggande ram för hjälpfönstret. I fristående läge implementerar klassen en ram med en generisk menyrad, men när hjälpsystemet är inbäddat i DrScheme implementerar den importerade klassen en menyrad med DrScheme-specifik :menus. Utvecklingen av SchemeEsq i Ed visar hur några noggrant definierade utbyggnader kan omvandla ett programmeringsspråk på hög nivå till ett operativsystem på hög nivå. En hög nivå OS tillåter flexibel och effektiv kommunikation mellan program genom gemensamma l.anguage mekanismer, såsom förfaranden och metoder. Den garanterar också typ- och minnessäkerhet för alla program genom språkmekanismer, vilket eliminerar behovet av separata processadressutrymmen och datamarsering. Denna flexibilitet ökar potentialen för extensible och interoperativa program. Säkerheten ger dock varken den skyddsnivå mellan tillämpningarna eller den typ av processtyrning som konventionella operativsystem tillhandahåller. Som ett exempel visar SchemeEsq hur en grafisk programmeringsmiljö måste skydda sitt grafiska gränssnitt från störningar från ett program som kör i miljön. Även om språkbaserad säkerhet kan förhindra ett program från att trampa på miljöns datastrukturer, kan det inte hindra ett program från att svälta miljöprocessen eller från att läcka resurser. Ed kombinerar programmeringsflexibilitet för ett högnivåoperativt system med konventionella processkontroller av ett konventionellt operativsystem. Som vi har visat gör tre viktiga förlängningar denna 144 kombination möjlig: trådar med parametrar, händelserymder och väktare. Vår strategi för att bygga en hög nivå OS på toppen av programmet bör tillämpas lika bra på andra språk, såsom ML eller Java. Till exempel skickar användarens program utdata till SchemeEsq genom att köa en GUI-händelse. Eftersom den inbyggda köverksamheten är atomär och icke-blockerande, finns det ingen risk för att användarens program kommer att bryta en kommunikation invariant genom att döda sin egen tråd. Mer sofistikerade kommunikationsprotokoll kräver starkare skydd under genomförandet av protokollet. Faktum är att bara lägga till en gräns för storleken på utmatningskön i SchemeEsq (så att användarens trådblock när kön är full) kräver ett sådant skydd. En allmän lösning på skyddsproblemet är att skapa en ny trådägd av Schemeqs förmyndare för varje kommunikation. Denna teknik löser problemet eftersom trådskapande är en atomisk operation, och den nyskapade tråden kan exekvera godtyckligt många instruktioner utan att riskera att dödas av användarens program. Tyvärr är trådskapande en dyr operation i Ed jämfört med proceduranrop, som i många system. För att minska denna kostnad för gemensamma skyddsidiomer tillhandahåller Ed ett kall-in-nested-thread-förfarande som skapar en barntråd, och blockerar sedan föräldratråden tills barnet slutar. Genom att utnyttja den ömsesidiga uteslutningen mellan föräldra- och barntrådar kan MrEd eliminera en stor del av de trådskapande och trådswappande överkanterna för skyddsimpulser. Med hjälp av en liknande teknik förbättrade Ford och Lepreau [17] Mach RPC:s prestanda. Icke desto mindre återstår ett betydande omkostnader. [7], Oberon [33] och JavaOS [31]. Alla dessa system förenklar genomförandet av samarbetande grafiska program genom ett språk på hög nivå. Även om de flesta av dessa system stöder flera processer, är det bara MrEd som tillhandahåller den typ av processkontroller som krävs för att genomföra en SchemeEsq-liknande programmeringsmiljö.g Andra relaterade arbeten syftar till att replikera säkerhet, säkerhet och resurskontroll av konventionella operativsystem inom ett enda adressutrymme. Arkitekturer som Alta [2], SPIN [5], J-Kernel [22] och Nemesis [24] betonar skydd inom ett enda adressutrymme, men på bekostnad av programintegration genom indirekta och ineffektiva samtal. Till exempel, J-Kernel bygger på explicita kapaciteter, och "På Lisp Machine, tillåter programmerare att mixtra med OS on-the-fly ansågs vara en fördel [7, sidan 441. därför offrar bekvämligheten av direkta procedursamtal och direkt datadelning. Tillbaka och Hsieh [l] p rekommenderar en detaljerad förklaring av skillnaden mellan processtyrning och enbart säkerhet i ett Javabaserat operativsystem. De betonar vikten av den "röda linjen" som separerar användarkod och kärnkod i ett konventionellt operativsystem. Denna röda linje existerar i MrEd, som separerar lågnivåinbyggda primitiver från resten av systemet. Till exempel, kö-callback är effektivt en atomisk operation till den anropande tråden. MrEd går ett steg längre och ger program möjlighet att definiera nya lager av röda linjer. I synnerhet, SchemeEsq definierar en röd linje mellan sig själv och de program som den utför. Inferno [8] uppnår många av samma mål som MrEd, men på ett mindre språk som är inriktat på kommunikationsprogramvara snarare än genomförande av allmänna riktlinjer. Balfanz och Gong [3] utforska tillägg till Java för att stödja flera processer, särskilt flera processer som ägs av olika användare inom en enda JVM. De härleder några av samma konstruktioner som definieras av MrEd, särskilt händelserymder. Kålgrönsaker (utom rötter och späda bladgrödor av Brassica)
REF visar hur viktiga OS-anläggningar erhålls genom tre viktiga tillägg -trådar med parametrar, händelserymder och väktare - till ett programmeringsspråk på hög nivå.
Programming languages as operating systems (or revenge of the son of the lisp machine)
{'venue': "ICFP '99", 'journal': None, 'mag_field_of_study': ['Computer Science']}