Demystifierad och utan jargong.
När vi har definierat system kan vi nu definiera systemprogrammering som den handling som består i att bygga upp systemprogramvara med hjälp av systemprogrammeringsspråk. Enkelt nog, eller hur?
Det finns en sak som vi hoppade över, språk. Folk talar ofta om systemprogrammeringsspråk på ett sätt som ”X är bra, det är snabbt, kompilerat och ett systemprogrammeringsspråk”. Men är alla överens om vad ett systemprogrammeringsspråk är?
Med tanke på våra definitioner av system skulle jag definiera kriterierna för ett systemprogrammeringsspråk som följande:
- Kompilerat till binärfil
- Kan byggas utan beroende av annan programvara (inklusive en kärna)
- Prestationsegenskaper som liknar andra systemprogrammeringsspråk
Disclaimer: Detta är min definition. Eftersom det inte finns några fastställda kriterier, härleder jag en definition från vad som är meningsfullt för mig med tanke på det sammanhang i vilket jag har definierat systemprogramvara.
Kompilera till native binär
Om ett språk inte kan kompilera till en körbar fil som är direkt tolkningsbar av CPU:n körs det per definition på en plattform (t.ex. JVM, Ruby VM, Python VM, etc.). Det kan finnas en del argument att ta upp här, men för enkelhetens skull anser jag att detta är ett lämpligt kriterium.
Inga beroenden
Argumentet liknar det att kompilera till en inhemsk binärfil. Om språket alltid kräver att någon annan programvara är närvarande för att kunna köras, så körs det på en plattform. Ett exempel på detta är Go och dess inkluderade standardbibliotek. Det kräver stöd från operativsystemet för att utföra grundläggande åtgärder, t.ex. allokering av minne, skapande av trådar (för goroutiner att köra på), för dess inbyggda nätverkspollare och andra åtgärder. Även om det är möjligt att återimplementera dessa kärnfunktioner skapar det ett hinder för användning i detta sammanhang och det är lätt att föreställa sig varför inte alla språk, även de som kompileras till statiska binärer, är avsedda som systemprogrammeringsspråk.
Samma prestandakaraktäristika
Detta är lite av en undanflykt. Det handlar dock om att säga att inom det system av språk som vanligtvis klassificeras som systemprogrammeringsspråk bör det inte finnas stora (storleksordning) skillnader i prestanda. Med egenskaper syftar jag uttryckligen på exekveringshastighet och minneseffektivitet.
Den gyllene standarden för jämförelser är C och/eller C++, som ofta representeras i jämförande benchmarks, som mäter exekveringshastigheten i hur många storleksordningar långsammare språken är än C/C++.
Namn på några få
De språk som omedelbart kommer att komma i åtanke, med tanke på ovanstående definition, är C och C++. Men det finns också nyare språk som Rust och Nim som också fyller denna nisch. Faktum är att det redan finns ett operativsystem som är skrivet helt och hållet i Rust (RedoxOS) och en kärna i Nim (nimkernel).
Låt oss prata om Go
Tidigare antydde jag att Go kanske inte faller inom familjen ”systemprogrammeringsspråk”. Men precis som att alla applikationer inte passar fint in i applikationsprogramvara och systemprogramvara så gör inte heller språk det.
Ofta kallar folk Go för ett systemprogrammeringsspråk och till och med golang.org citeras som:
Go är ett allmängiltigt språk som är utformat med systemprogrammering i åtanke.
Men inte ens detta är ett direkt påstående om att Go är ett systemprogrammeringsspråk, utan bara att det är utformat med det i åtanke. Jag tycker att det snarare ligger mitt emellan.
Och även om Go kompileras till inhemsk binärfil, innehåller användbara koncept på låg nivå (råa/osäkra pekare, inhemsk typ som bytes och int32, stöd för inline-assemblering) och är relativt kraftfullt, så har det fortfarande en del utmaningar att övervinna. Go levereras med en körtid och en skräpplockare.
En körtid innebär att bootstrapping/overriding av körtiden kommer att krävas för att kunna köras i miljöer utan kärnor. Detta berör mer den interna implementeringen av språket, vilket kan förändras i framtida versioner. Förändringar kräver ytterligare arbete med bootstrapping när språket utvecklas.
En garbage collector (GC) innebär antingen att Go är begränsat i vilka tillämpningsdomäner det kan användas eller att GC måste inaktiveras och ersättas med manuell minneshantering. I det fall GC inte kan ersättas skulle realtidsdomänen (definierad av operationer som måste slutföras inom givna tidsgränser och/eller prestanda mäts i nanosekunder) inte kunna riskera icke-deterministiska paustider för en GC.
Mjukvara för distribuerade system
Med det ökande talet om distribuerade system, och tillämpningar som Kubernetes som blir mycket populära, får vi höra en mängd nya ord som (om vi ska vara ärliga) de flesta av oss inte förstår helt och hållet.
Jag har hittills sett termerna systemprogrammering och systemingenjörer användas i sammanhang där de egentligen menade distribuerad systemprogrammering och distribuerade systemingenjörer.
Vi har definierat systemmjukvara, systemspråk och systemprogrammering i det här inlägget. Men när vi talar om distribuerade system ändras innebörden av system. Och även om jag inte tänker dyka ner i de specifika skillnaderna här (främst för att jag fortfarande behöver förstå dem bättre själv), är det viktigt att vi gör dessa mentala distinktioner och använder ett mer exakt språk när vi kan för att undvika förvirring hos dem som fortfarande lär sig utrymmet.