Demystified met een jargonvrije verkenning.
Vaak horen we, leren we en gebruiken we zelfs termen of uitdrukkingen die we niet helemaal begrijpen. Ik merk dat dit vrij gebruikelijk is binnen de softwareontwikkelingsgemeenschap, of het nu gaat om RESTful Web API’s, Agile-methodologie, Machine Learning of een andere term. Dit is niet per se een slechte zaak, maar het is belangrijk om te begrijpen wanneer je iets echt kent en wanneer je alleen de naam ervoor kent.
Voor mij is Systems Programming zo’n term. Ik wil proberen om in eenvoudige taal uit te leggen wat dit betekent.
Voordat we kunnen begrijpen wat Systems Programming inhoudt, moeten we eerst begrijpen wat een Systeem is. Software heeft de neiging in een van de twee kampen te vallen, systeemsoftware en toepassingssoftware.
Systeemsoftware is computersoftware die is ontworpen om een platform te bieden voor andere software. Voorbeelden van systeemsoftware zijn besturingssystemen, software voor computerwetenschappen, game engines, industriële automatisering en software-as-a-service-toepassingen.
… Dergelijke software wordt niet als systeemsoftware beschouwd wanneer deze gewoonlijk kan worden verwijderd zonder de werking van andere software te beïnvloeden.
– wikipedia.orgSysteemsoftware is een platform dat bestaat uit programma’s en diensten van het besturingssysteem (OS), waaronder instellingen en voorkeuren, bestandsbibliotheken en functies die worden gebruikt voor systeemtoepassingen. Systeemsoftware omvat ook apparaatstuurprogramma’s die basiscomputerhardware en randapparatuur uitvoeren.
– techopedia.comSysteemsoftware verwijst naar de bestanden en programma’s waaruit het besturingssysteem van uw computer is opgebouwd. Systeembestanden omvatten bibliotheken van functies, systeemdiensten, stuurprogramma’s voor printers en andere hardware, systeemvoorkeuren en andere configuratiebestanden. De programma’s die deel uitmaken van de systeemsoftware omvatten assemblers, compilers, bestandsbeheertools, systeemhulpprogramma’s en debuggers.
– techterms.com
De Wikipedia-definitie is erg vaag over wat wordt beschouwd als systeemsoftware, zolang het maar diensten verleent aan andere toepassingen. De andere twee definities richten zich echter puur op het besturingssysteem – drivers, kernels, bibliotheken en functies (denk aan kernel/libc header files en shared objects). Dit impliceert een nauwe relatie met hardware. Als we een ander Wikipedia-artikel over systeemprogrammering bekijken, zien we:
Systeemprogrammering vereist een grote mate van hardwarebewustzijn.
Het artikel gaat verder met te impliceren dat een kernonderdeel van systeemprogrammering is dat dingen heel snel moeten zijn. Dit is een logische reden waarom we veel over de hardware zouden moeten weten. Het is ook logisch dat snelheid (performance) een kernonderdeel zou zijn van systeemprogrammering als het een platform is voor andere software.
Als het meest centrale deel van je applicatie (het systeem software “platform”) traag is, dan is de hele applicatie traag. Voor veel toepassingen, vooral op schaal, zou dit een deal-breaker zijn.
System Software in a Nutshell
De bovenstaande citaten en andere bronnen hebben mij geleid tot de volgende criteria om systeemsoftware te definiëren:
- Biedt een platform voor andere software om op te bouwen.
- Directe of nauwe interfaces met computerhardware om de noodzakelijke prestaties te verkrijgen en abstracties bloot te leggen (als onderdeel van het platform).
Wat is wel en wat is geen Systeemsoftware
Voorbeelden van wat systeemsoftware is:
- OS Kernels
- Drivers
- Bare Metal Hypervisors (bijv.g. Hyper-V en VM Ware ESXi)
- Compilers (die native binaries produceren) en Debuggers
Voorbeelden van wat geen systeemsoftware is:
- GUI Chat toepassing (Slack, Discord, etc)
- Web-gebaseerde JavaScript toepassing
- Web Service API
Je zult merken dat terwijl Web Service API’s een dienst leveren aan andere software, ze (typisch) geen interactie hebben met hardware om abstracties erover bloot te leggen. Er zijn echter toepassingen die in het midden van een grijs gebied vallen. De toepassingen die in dit verband in het oog springen zijn high performance computing toepassingen en embedded software.
High performance computing (HPC) toepassingen, zoals real-time trading op beurzen, laten gewoonlijk geen platform zien, maar het is gebruikelijk dat zij code schrijven die direct met hardware interfereert. Een voorbeeld hiervan is het omzeilen van de netwerk-stack die door de kernel wordt aangeboden en het implementeren van hun eigen netwerk-stack die rechtstreeks met de NIC(‘s) praat. Op deze manier kunnen we zien dat HPC-software veel overeenkomsten vertoont met systeemsoftware, door rechtstreeks te interageren met hardware om de nodige prestatiewinst te kunnen boeken.
Embedded software-ontwikkeling vertoont ook veel overeenkomsten met systeemsoftware, in die zin dat code wordt geschreven om rechtstreeks te interageren met hardware. De geboden abstracties worden echter doorgaans door dezelfde software gebruikt en kunnen niet als een platform worden beschouwd.
Het is belangrijk te wijzen op toepassingen die overeenkomsten vertonen met onze definitie van systeemsoftware, omdat u deze toepassingen/functies waarschijnlijk in deze termen zult zien beschreven (systeemsoftware, systeemingenieurs, enz.)
Systeemprogrammering (+ Talen)
Nu we systemen hebben gedefinieerd, kunnen we systeemprogrammering definiëren als de handeling van het bouwen van systeemsoftware met behulp van systeemprogrammeertalen. Eenvoudig genoeg, toch?
Wel, er is één ding dat we overgeslagen hebben, talen. Mensen praten vaak over systeemprogrammeertalen op een manier als “X is geweldig, het is snel, gecompileerd, en een systeemprogrammeertaal.” Maar is iedereen het er wel over eens wat een systeemprogrammeertaal is?
Gezien onze definities van Systems zou ik de criteria voor een systeemprogrammeertaal als volgt definiëren:
- Gecompileerd tot native binary
- Kan worden gebouwd zonder afhankelijk te zijn van andere software (inclusief een kernel)
- Prestatiekenmerken vergelijkbaar met andere systeemprogrammeertalen
Disclaimer: Dit is mijn definitie. Aangezien er geen vaste criteria zijn, leid ik een definitie af van wat voor mij logisch is gezien de context waarin ik systeemsoftware heb gedefinieerd.
Compile to Native Binary
Als een taal niet kan compileren naar een executable die direct interpreteerbaar is door de CPU, dan draait het per definitie op een platform (bijv. JVM, Ruby VM, Python VM, etc). Hier zijn wellicht argumenten voor aan te voeren, maar voor de eenvoud vind ik dit een geschikt criterium.
Geen afhankelijkheden
Het argument is vergelijkbaar met het compileren naar een native binary. Als de taal altijd vereist dat er andere software aanwezig is om uit te voeren, dan draait het op een platform. Een voorbeeld hiervan is Go en zijn standaard bibliotheek. Het vereist ondersteuning van het OS om basisacties uit te voeren zoals geheugen toewijzen, threads spawnen (voor goroutines om op te draaien), voor de ingebouwde netwerk poller, en andere acties. Hoewel het mogelijk is om deze kernfuncties opnieuw te implementeren, werpt dit een barrière op voor gebruik in deze context en het is gemakkelijk voor te stellen waarom niet alle talen, zelfs die welke compileren tot statische binaries, bedoeld zijn als systeemprogrammeertalen.
Gelijkaardige Prestatiekenmerken
Deze is een beetje een uitvlucht. Het wil echter zeggen dat binnen het systeem van talen die typisch worden geclassificeerd als systeem programmeertalen, er geen grote (orde van grootte) verschillen in prestatie karakteristieken zouden moeten zijn. Met kenmerken bedoel ik expliciet de uitvoeringssnelheid en de geheugenefficiëntie.
De gouden standaard voor vergelijking is C en/of C++, zoals vaak wordt weergegeven in vergelijkende benchmarks, die de uitvoeringssnelheid meten in hoeveel orden van grootte talen langzamer zijn dan C/C++.
Naming a Few
De talen die onmiddellijk in gedachten komen, gegeven de bovenstaande definitie, zijn C en C++. Maar er zijn ook nieuwere talen zoals Rust en Nim die ook deze niche vullen. In feite is er al een OS dat geheel in Rust is geschreven (RedoxOS) en een kernel in Nim (nimkernel).
Let’s Talk About Go
Eerder heb ik laten doorschemeren dat Go misschien niet binnen de familie van “systeem programmeertalen” valt. Maar net zoals niet alle toepassingen mooi in applicatiesoftware en systeemsoftware passen, geldt dat ook niet voor talen.
Vaak noemen mensen Go een systeemprogrammeertaal en zelfs golang.org wordt geciteerd als:
Go is een general-purpose taal, ontworpen met systeemprogrammering in gedachten.
Hoewel, zelfs dit is geen regelrechte bewering dat Go een systeemprogrammeertaal is, alleen maar dat het ontworpen is met dat in gedachten. Hoewel Go compileert naar native binaries, nuttige low-level concepten bevat (raw/unsafe pointers, native types zoals bytes en int32, en inline assembly ondersteuning), en relatief performant is, heeft het nog wat uitdagingen te overwinnen. Go wordt geleverd met een runtime en een garbage collector.
Een runtime betekent dat bootstrapping/overriding van de runtime nodig zal zijn om te draaien in omgevingen zonder kernels. Dit gaat meer in op de interne implementatie van de taal, die in toekomstige versies zou kunnen veranderen. Veranderingen vereisen extra bootstrapping-werk naarmate de taal evolueert.
Een garbage collector (GC) betekent ofwel dat Go beperkt is in welke toepassingsdomeinen het gebruikt kan worden, ofwel dat de GC uitgeschakeld moet worden en vervangen door handmatig geheugenbeheer. In het geval dat GC niet kan worden vervangen, zou het realtime domein (gedefinieerd door operaties die binnen bepaalde tijdsgrenzen moeten worden voltooid en/of de prestaties worden gemeten in nano-seconden) niet in staat zijn om niet-deterministische pauzetijden van een GC te riskeren.
Distributed Systems Software
Met het groeiende gepraat over gedistribueerde systemen, en toepassingen zoals Kubernetes die erg populair worden, krijgen we een stortvloed aan nieuwe woordenschat te horen die (als we eerlijk zijn) de meesten van ons niet volledig begrijpen.
Tot nu toe heb ik de termen systeemprogrammering en systeemingenieurs gebruikt zien worden in contexten waar ze eigenlijk gedistribueerde systeemprogrammering en gedistribueerde systeemingenieurs bedoelden.
We hebben systeemsoftware, systeemtalen en systeemprogrammering in deze post gedefinieerd. Maar wanneer we het over gedistribueerde systemen hebben, verandert de betekenis van systeem. En hoewel ik hier niet in de specifieke verschillen duik (vooral omdat ik ze zelf nog beter moet begrijpen), is het belangrijk dat we die mentale verschillen maken en wanneer we kunnen exactere taal gebruiken om verwarring te voorkomen bij degenen die de ruimte nog moeten leren.