Demistyfikowane dzięki eksploracji bez żargonu.
Często słyszymy, uczymy się, a nawet używamy terminów lub zwrotów, których nie do końca rozumiemy. Uważam, że jest to dość powszechne w społeczności programistów, czy jest to RESTful Web APIs, metodologia Agile, Machine Learning, czy jakiś inny termin. To nie jest koniecznie zła rzecz, ale ważne jest, aby zrozumieć, kiedy naprawdę coś wiesz, a kiedy po prostu znasz nazwę dla tego.
Dla mnie, Programowanie Systemów jest jednym z takich terminów. Chciałbym spróbować wyjaśnić, używając prostego języka, co to oznacza.
Zanim zrozumiemy, co pociąga za sobą Programowanie Systemów, musimy najpierw zrozumieć, czym jest System. Oprogramowanie ma tendencję do wpadania do jednego z dwóch obozów, oprogramowania systemowego i oprogramowania aplikacyjnego.
Oprogramowanie systemowe jest oprogramowaniem komputerowym zaprojektowanym w celu zapewnienia platformy dla innego oprogramowania. Przykłady oprogramowania systemowego obejmują systemy operacyjne, oprogramowanie do nauk obliczeniowych, silniki gier, automatykę przemysłową i oprogramowanie jako aplikacje usługowe.
… Takie oprogramowanie nie jest uważane za oprogramowanie systemowe, jeśli może być odinstalowane zazwyczaj bez wpływu na funkcjonowanie innego oprogramowania.
– wikipedia.orgOprogramowanie systemowe to platforma składająca się z programów i usług systemu operacyjnego (OS), w tym ustawień i preferencji, bibliotek plików i funkcji używanych przez aplikacje systemowe. Oprogramowanie systemowe obejmuje również sterowniki urządzeń, które uruchamiają podstawowy sprzęt komputerowy i urządzenia peryferyjne.
– techopedia.comOprogramowanie systemowe odnosi się do plików i programów, które tworzą system operacyjny komputera. Pliki systemowe obejmują biblioteki funkcji, usługi systemowe, sterowniki do drukarek i innego sprzętu, preferencje systemowe i inne pliki konfiguracyjne. Programy, które są częścią oprogramowania systemowego, obejmują asemblery, kompilatory, narzędzia do zarządzania plikami, narzędzia systemowe i debuggery.
– techterms.com
Definicja Wikipedii jest bardzo niejasna w kwestii tego, co jest uważane za oprogramowanie systemowe, o ile świadczy ono usługi dla innych aplikacji. Jednak pozostałe dwie definicje skupiają się wyłącznie na systemie operacyjnym – sterownikach, jądrach, bibliotekach i funkcjach (pomyśl o plikach nagłówkowych jądra/libc i obiektach współdzielonych). Sugeruje to bliski związek ze sprzętem. Jeśli spojrzymy na inny artykuł Wikipedii na temat programowania systemowego, zobaczymy:
Programowanie systemowe wymaga dużego stopnia świadomości sprzętowej.
Artykuł sugeruje, że podstawową częścią programowania systemowego jest potrzeba, aby rzeczy były bardzo szybkie. To ma sens, dlaczego musielibyśmy wiedzieć dużo o sprzęcie. Ma to również sens, że prędkość (wydajność) byłaby kluczową częścią programowania systemów, jeśli jest to platforma dla innego oprogramowania.
Jeśli najbardziej centralna część aplikacji (oprogramowanie systemowe „platforma”) jest powolna, to cała aplikacja jest powolna. Dla wielu aplikacji, zwłaszcza w skali, byłby to deal-breaker.
System Software in a Nutshell
Powyższe cytaty i inne zasoby doprowadziły mnie do następujących kryteriów definiowania oprogramowania systemowego:
- Zapewnia platformę dla innego oprogramowania, na której można budować.
- Bezpośrednio lub ściśle współdziała ze sprzętem komputerowym w celu uzyskania niezbędnej wydajności i odsłonięcia abstrakcji (jako część platformy).
Co jest, a co nie jest oprogramowaniem systemowym
Przykłady tego, co jest oprogramowaniem systemowym:
- Jądra systemu operacyjnego
- Drivery
- Hiperwizory typu bare metal (np.g. Hyper-V i VM Ware ESXi)
- Kompilatory (które produkują natywne binarki) i Debuggery
Przykłady tego, co nie jest oprogramowaniem systemowym:
- Aplikacja czatu GUI (Slack, Discord, etc)
- Web-based JavaScript Application
- Web Service API
Zauważysz, że podczas gdy Web Service API’s dostarczają usługi do innego oprogramowania, nie wchodzą (typowo) w interakcję ze sprzętem w celu wystawienia abstrakcji nad nim. Istnieją jednak aplikacje, które mieszczą się w środkowej szarej strefie. Te, które przychodzą na myśl, to aplikacje obliczeniowe o wysokiej wydajności i oprogramowanie wbudowane.
Wysokowydajne aplikacje obliczeniowe (HPC), takie jak handel w czasie rzeczywistym na giełdach, zazwyczaj nie eksponują platformy, ale często zdarza się, że piszą kod, który bezpośrednio łączy się ze sprzętem. Przykładem może być ominięcie stosu sieciowego oferowanego przez jądro i zaimplementowanie własnego stosu sieciowego rozmawiającego bezpośrednio z NIC(s). W ten sposób możemy zobaczyć, jak oprogramowanie HPC dzieli wiele podobieństw z oprogramowaniem systemowym, poprzez bezpośrednią interakcję ze sprzętem w celu zapewnienia potrzebnych przyrostów wydajności.
Rozwój oprogramowania wbudowanego również dzieli wiele podobieństw z oprogramowaniem systemowym w tym, że kod jest napisany do bezpośredniego interfejsu ze sprzętem. Jednak wszelkie dostarczane abstrakcje są zazwyczaj konsumowane przez to samo oprogramowanie i nie mogą być uznane za platformę.
Ważne jest, aby zwrócić uwagę na aplikacje, które dzielą podobieństwa z naszą definicją oprogramowania systemowego, ponieważ prawdopodobnie zobaczysz te aplikacje/prace opisane w tych terminach (oprogramowanie systemowe, inżynierowie systemów, itp.)
Programowanie Systemów (+ Języki)
Zdefiniowawszy Systemy, możemy teraz zdefiniować Programowanie Systemów jako akt budowania Oprogramowania Systemów przy użyciu Języków Programowania Systemów. Wystarczająco proste, prawda?
Cóż, jest jedna rzecz, którą pominęliśmy, języki. Ludzie często mówią o językach programowania systemowego w sposób taki jak „X jest świetny, jest szybki, skompilowany i jest językiem programowania systemowego”. Ale czy wszyscy są na tej samej stronie, co do tego, czym jest język programowania systemowego?
Podając nasze definicje systemów, zdefiniowałbym kryteria języka programowania systemowego jako:
- Kompilowany do natywnej binarki
- Możliwość budowania bez zależności od innego oprogramowania (w tym jądra)
- Charakterystyka wydajności podobna do innych języków programowania systemowego
Zastrzeżenie: To jest moja definicja. Ponieważ nie ma ustalonych kryteriów, wyprowadzam definicję z tego, co ma dla mnie sens, biorąc pod uwagę kontekst, w którym zdefiniowałem oprogramowanie systemowe.
Kompilacja do Native Binary
Jeśli język nie może skompilować się do pliku wykonywalnego, który jest bezpośrednio interpretowalny przez CPU, to z definicji działa na platformie (np. JVM, Ruby VM, Python VM, itp.). Może być kilka argumentów do zrobienia tutaj, ale dla uproszczenia myślę, że jest to odpowiednie kryterium.
Brak zależności
Ten argument jest podobny do kompilacji do natywnej binarki. Jeśli język zawsze wymaga obecności jakiegoś innego oprogramowania do wykonania, to jest on uruchomiony na platformie. Przykładem tego jest Go i jego dołączona biblioteka standardowa. Wymaga ona wsparcia ze strony systemu operacyjnego do wykonania podstawowych czynności, takich jak alokacja pamięci, tworzenie wątków (dla goroutines do uruchomienia), dla wbudowanego pollera sieci i innych działań. Podczas gdy możliwe jest ponowne zaimplementowanie tych podstawowych funkcji, tworzy to barierę do użycia w tym kontekście i łatwo sobie wyobrazić, dlaczego nie wszystkie języki, nawet te, które kompilują się do statycznych binarek, są przeznaczone jako języki programowania systemowego.
Podobne charakterystyki wydajności
Ten jest trochę na wyrost. Jednak chodzi o to, aby powiedzieć, że w systemie języków typowo klasyfikowanych jako języki programowania systemowego, nie powinno być dużych (rząd wielkości) różnic w charakterystyce wydajności. Przez charakterystykę wyraźnie odnoszę się do prędkości wykonania i wydajności pamięci.
Złotym standardem dla porównania jest C i/lub C++, jak to jest często reprezentowane w porównawczych benchmarkach, które mierzą prędkość wykonania w ilu rzędach wielkości języki są wolniejsze niż C/C++.
Wymieniając kilka
Języki, które przychodzą na myśl natychmiast, biorąc pod uwagę powyższą definicję to C i C++. Ale są też nowsze języki, takie jak Rust i Nim, które również wypełniają tę niszę. W rzeczywistości, istnieje już system operacyjny napisany w całości w Rust (RedoxOS) i jądro w Nim (nimkernel).
Porozmawiajmy o Go
Wcześniej wspomniałem o tym, że Go może nie należeć do rodziny „języków programowania systemowego”. Jednak, tak jak nie wszystkie aplikacje pasują do oprogramowania użytkowego i systemowego, tak samo nie pasują do języków.
Często ludzie nazywają Go językiem programowania systemowego i nawet golang.org jest cytowany jako:
Go jest językiem ogólnego przeznaczenia zaprojektowanym z myślą o programowaniu systemowym.
Jednakże, nawet to nie jest jawne twierdzenie, że Go jest językiem programowania systemowego, po prostu, że jest zaprojektowany z myślą o tym. Ja uważam, że raczej siedzi pośrodku.
Choć Go kompiluje się do natywnych binarek, zawiera użyteczne koncepcje niskiego poziomu (raw/unsafe pointers, natywne typy takie jak bytes i int32, oraz wsparcie dla montażu inline), i jest stosunkowo wydajny; wciąż ma pewne wyzwania do pokonania. Go dostarczane jest z runtime i garbage collector.
A runtime oznacza, że bootstrapping/overriding runtime będzie wymagany do działania w środowiskach bez jądra. To wkracza bardziej w wewnętrzną implementację języka, która może się zmienić w przyszłych wydaniach. Zmiany wymagają dodatkowej pracy nad bootstrapowaniem w miarę rozwoju języka.
Garbage collector (GC) albo oznacza, że Go jest ograniczone w jakich domenach aplikacji może być używane, albo że GC musi być wyłączone i zastąpione ręcznym zarządzaniem pamięcią. W przypadku, gdy GC nie może zostać zastąpiony, domena czasu rzeczywistego (zdefiniowana przez operacje, które muszą zostać zakończone w określonych granicach czasowych i/lub wydajność jest mierzona w nanosekundach) nie byłaby w stanie zaryzykować niedeterministycznych czasów wstrzymania GC.
Oprogramowanie systemów rozproszonych
Przy rosnącej rozmowie o systemach rozproszonych i aplikacjach takich jak Kubernetes, które stają się bardzo popularne, słyszymy mnóstwo nowego słownictwa, które (jeśli jesteśmy szczerzy) większość z nas nie w pełni rozumie.
Do tej pory widziałem terminy programowanie systemowe i inżynierowie systemów używane w kontekstach, w których tak naprawdę chodziło o programowanie systemów rozproszonych i inżynierów systemów rozproszonych.
Zdefiniowaliśmy oprogramowanie systemowe, języki systemowe i programowanie systemowe w tym poście. Jednakże, gdy mówimy o systemach rozproszonych, znaczenie systemu się zmienia. I chociaż nie zamierzam tutaj zagłębiać się w konkretne różnice (głównie dlatego, że sam muszę je jeszcze lepiej zrozumieć), ważne jest, abyśmy dokonywali tych mentalnych rozróżnień i używali dokładniejszej mowy, kiedy tylko możemy, aby uniknąć zamieszania wśród tych, którzy wciąż uczą się tej przestrzeni.