Dlaczego systemy operacyjne robią rzeczy niskiego poziomu w C i C ++? Dlaczego nie tylko C ++?

20

Na stronie Wikipedii dla systemu Windows napisano, że system Windows został napisany w asemblerze dla programu ładującego i przełącznika zadań oraz w C i C ++ dla procedur jądra.

IIRC, możesz wywoływać funkcje C ++ z extern "C"bloku d. Mogę używać C do funkcji jądra, więc aplikacje w czystym C mogą z nich korzystać (jak printfi takie), ale jeśli można je po prostu owinąć w extern "C "blok, to po co kodować w C?

Cole Johnson
źródło
10
Czy widziałeś różne „Po co używać C, gdy jest C ++?” pytania tutaj? Niekoniecznie jest to duplikat żadnego z nich, ale są one powiązane.
1
„możesz wywoływać funkcje C ++ z zewnętrznego bloku„ C ”d jako C ++„ Czy masz na myśli, że możesz wywoływać funkcje C ...?
Code-Guru,
@ Code-Guru nie, ponieważ jedyną różnicą między eksportowanymi funkcjami C i C ++ jest dekoracja nazwy, aw C ++ dodanie thiszmiennej
Cole Johnson
2
Rzuć wyjątek w ISR i zobacz, co się stanie
James
Jeszcze jedno pytanie C vs. C ++.
Machado

Odpowiedzi:

31

To głównie z powodów historycznych. Niektóre części jądra systemu Windows zostały pierwotnie napisane w języku C, ponieważ w 1983 r., Ponad trzy dekady temu, kiedy uruchomiono system Windows 1.0 , C ++ zostało ledwie wydane. Teraz te biblioteki C pozostaną tam „na zawsze”, ponieważ Microsoft uczynił kompatybilność wsteczną punktem sprzedaży, a przepisanie wersji części C kompatybilnych z błędami w C ++ wymaga ogromnego wysiłku, bez żadnej skutecznej korzyści.

back2dos
źródło
+1, wydaje mi się, że jest to najbardziej realistyczna odpowiedź (poza tym, że mogą istnieć niektórzy deweloperzy jądra systemu Windows, którzy po prostu nie lubią C ++ lub nie ufają kompilatorom C ++ w przypadku takich rzeczy niskiego poziomu). Spójrz na przykład tutaj stackoverflow.com/questions/520068/… , dlaczego jądro Linuksa jest napisane w C.
Doc Brown
@ back2dos - Chociaż ich kod C nie zostanie wyrzucony, nie oznacza to, że zostanie użyty lub nie zaktualizowany. Gwarantuję ci, że istnieje co najmniej jedna metoda, która robi coś, co zostało pierwotnie zapisane i zawarte w bibliotece C, która została przeniesiona do biblioteki C ++ w systemie Windows 8
Ramhound
8
Trudno mi uwierzyć, że w najnowszej wersji systemu Windows jest jakiś kod systemu Windows 1.0. Windows ME był ostatnią wersją systemu Windows, która nie była oparta na bazie kodu Windows NT. I nawet to zostało w dużej mierze zastąpione przez nowe jądro RT (które, jak rozumiem, nie obiecuje zbytniej kompatybilności wstecznej).
TMN,
@TMN: argument jest również prawdopodobnie poprawny - jestem prawie pewien, że na drodze od Win 1.0 do teraz było wiele bibliotek napisanych w C, które nadal są częścią obecnej bazy kodu Win8, i nikt w MS nie widzi żadnej skorzystaj z przepisania ich w C ++.
Dok. Brown
1
Zresztą nie ma prawie żadnego kodu, który mógłby komunikować się z jądrem systemu Windows. Zasadniczo robią to tylko kierowcy. Aplikacje zwykle rozmawiają z interfejsem API Win32 lub API POSIX.
MSalters,
24

Jak większość ludzi zauważyła, przyczyny są zdecydowanie historyczne, ale jest coś jeszcze, o czym nikt nie wspomina i uważam, że to jest powód, dla którego ludzie nadal piszą kod C dla niskiego poziomu.

C jest małym językiem w tym sensie, że specyfikacja jest (względnie) krótka. C ++ jest ogromny i to mało powiedziane. Może to nie mieć większego znaczenia dla programisty (choć myślę, że tak), ale jest niezwykle ważne, jeśli chcesz przeprowadzić formalną weryfikację . Ponadto istnieją ustalone narzędzia do analizy kodu C, które mogą pomóc w zapobieganiu błędom itp.

Jest to bardzo ważne w oprogramowaniu wbudowanym, w którym koszt zgłoszonego błędu jest niezwykle wysoki w porównaniu z resztą branży (porównaj sieć Web, w której możesz natychmiast zastosować łatę dla wszystkich użytkowników). Nie wspominając o kluczowym oprogramowaniu i sprzęcie medycznym.

Podjęto próby wyparcia C z jego dominującego miejsca w programowaniu niskiego poziomu z językami, które są w tym nawet lepsze, jak BitC, ale jak dotąd nie odniosły sukcesu.

K.Steff
źródło
4
+1 Za wzmiankę o kluczowym oprogramowaniu w czystym C (np. Oprogramowanie lotnicze). Jednak w oprogramowaniu medycznym stosuje się również C ++ (zamiast formalnej weryfikacji stosuje się szeroko zakrojone testy, co byłoby bardzo trudne w C ++).
Giorgio,
1
Ponadto C ma dość udany bezpieczny podzbiór MISRA-C. Istnieją odpowiedniki dla C ++, ale nie są one de facto standardem branżowym (jeszcze). Trend programowania krytycznego dla bezpieczeństwa polega na tym, że biblioteki i kompilatory będą również zmuszone do korzystania z bezpiecznego podzbioru, takiego jak MISRA. Ponowne napisanie kompatybilnej wersji MISRA-C ++ całej standardowej biblioteki C ++ najprawdopodobniej będzie koszmarem.
2
@Lundin Wytyczne MISRA nie są bezpiecznym podzbiorem - wciąż masz surowe wskaźniki i większość innych funkcji, które czynią C niebezpiecznym - skupiają się głównie na nieużywaniu lub dokumentowaniu specyficznych dla implementacji zachowań.
Pete Kirkham,
@PeteKirkham MISRA-C wyłapie wszystkie najbardziej klasyczne błędy wskaźnika i wymusi analizę statyczną. Branżowe normy bezpieczeństwa (IEC 61508 i in.) Najwyraźniej zatwierdzają MISRA-C jako bezpieczny podzbiór C. Nie ma wielu innych przydatnych alternatyw dla oprogramowania o znaczeniu krytycznym, chyba że wybierzesz SPARK Ada, język, który niewielu zna, z ograniczonym narzędziem wsparcie.
2
To powinno być wybrane jako najlepsza odpowiedź, ponieważ jest poprawna. Inni, którzy sugerują, że C jest używany tylko z powodów historycznych, sam w sobie jest histeryczny.
Rob
15
  1. Środowisko wykonawcze C jest znacznie mniejsze.
  2. Tłumaczenie C ++ na konstrukcje niższego poziomu jest mniej przejrzyste niż w C. (Zobacz dwa odnośniki i vtables, dwa krótkie przykłady)
  3. C zwykle ma stabilny ABI. C ++ zwykle nie. Oznacza to, że interfejs systemu musi być w stylu C. Ponadto, jeśli chcesz mieć jakieś dynamiczne moduły, posiadanie spójnego ABI bardzo pomaga.
wnoise
źródło
+1 za (2) i (3). Nie jestem przekonany do (1).
Thomas Eding,
7
@Thomas Eding: Środowisko wykonawcze C ++ składa się ze środowiska wykonawczego C oraz funkcji C ++, takich jak wyjątki, RTTI i inny alokator pamięci. YMMV, czy liczy się to o wiele więcej. (A potem jest standardowa biblioteka ...)
wnoise
Ach, zapomniałem o wyjątkach i nowych / usuwanych pulach. RTTI Nigdy jednak nie używam: D
Thomas Eding,
11

Przyczyny nie są techniczne. Trochę montażu jest nieuniknione, ale nie są zmuszeni do korzystania z okazjonalnego C, chcą . Moja firma używa własnego zastrzeżonego jądra, napisanego prawie całkowicie w C ++, ale nie musimy obsługiwać interfejsu C do jądra, jak większość innych, ponieważ nasze osadzone jądro jest monolitycznie kompilowane z naszymi aplikacjami C ++. Kiedy masz interfejs C, często łatwiej jest napisać kod interfejsu w C, nawet jeśli można go użyć extern "C"w C ++.

Nawet my mamy wiele plików C, głównie z powodu kodu innej firmy. Kod niskiego poziomu innej firmy jest prawie zawsze dostarczany w języku C, ponieważ o wiele łatwiej jest włączyć kod C do aplikacji C ++ niż na odwrót.

Karl Bielefeldt
źródło
6

Programiści jądra są często ludźmi, którzy czują się szczęśliwsi, kiedy od razu widać ze źródła, co faktycznie robi kod.

C ++ ma wiele innych funkcji, które ukrywają to, co kod robi więcej niż zwykły kod C: ukrywa to: przeciążenia, metody wirtualne, szablony, referencje, rzuty ... C ++ ma także znacznie więcej składni, którą musisz opanować, aby nawet zrozumieć C ++ kod używający go.

Myślę, że moc C ++ to bardzo potężne narzędzia do tworzenia bibliotek i frameworków, które sprawiają, że tworzenie aplikacji jest bardzo proste. Bardzo często twórca aplikacji C ++ byłby całkowicie zagubiony we wnętrzach biblioteki wypełnionych szablonami, nawet jeśli byłby bardzo kompetentny w tworzeniu aplikacji przy użyciu tej biblioteki. A napisanie odpowiedniej biblioteki C ++ jest bardzo trudnym zadaniem programistycznym i zostało wykonane tylko w celu zapewnienia świetnych ram dla korzyści twórcy aplikacji. Biblioteki C ++ nie są wewnętrznie proste, są (lub mogą być ...) po prostu potężne, ale proste z punktu widzenia programistów aplikacji.

Ale API jądra nie może być API C ++, musi to być API niezależne od języka, więc większość dobrych rzeczy w C ++ nie będzie bezpośrednio wykorzystywana w tym interfejsie. Co więcej, jądro nie jest tak naprawdę podzielone na części „biblioteczne” i „aplikacyjne” opracowane niezależnie, z większym wysiłkiem logicznie przechodząc do jednej biblioteki, aby ułatwić tworzenie masy aplikacji.

Ponadto bezpieczeństwo i stabilność są bardziej krytyczne w jądrze, a metody wirtualne są znacznie bardziej dynamiczne, a zatem trudniejsze do wyodrębnienia i weryfikacji, niż zwykłe wywołania zwrotne lub inne mechanizmy podobne do C.

Krótko mówiąc, chociaż można oczywiście napisać dowolny program C, w tym jądro jako C ++, większość mocy C ++ nie jest dobrze używana w jądrze. I wielu twierdzi, że narzędzia programistyczne powinny powstrzymywać cię od robienia rzeczy, których nie powinieneś robić. C ++ nie.

hyde
źródło
+1. Jako programista jądra moją „ogólną zasadą” jest to, że jeśli nie możesz łatwo oszacować „dotkniętych linii pamięci podręcznej”, to używany język wyrządza więcej szkody niż pożytku.
Brendan
Wyjaśnienie: W przypadku jąder należy założyć, że procesor spędza najwięcej czasu w przestrzeni użytkownika, a kod jądra jest używany sporadycznie (np. Gdy przestrzeń użytkownika wywołuje API jądra lub występuje przerwanie); co oznacza, że ​​musisz założyć „zimną pamięć podręczną”. W przypadku współczesnych procesorów (gdzie pamięć RAM jest wolna w stosunku do szybkości procesora) pamięć podręczna i brak TLB są kosztowne, dlatego (w połączeniu z oczekiwaniami „zimnej pamięci podręcznej”) wskaźnik „dotknięte linie pamięci podręcznej” staje się niezwykle ważnym wskaźnikiem wydajności i / lub skalowalności.
Brendan
5

Bjarne Stroustrup w wywiadzie z lipca 1999 r . :

Żaden z tych języków nie był radykalnie inny ani dramatycznie lepszy niż inne współczesne języki. Byli jednak wystarczająco dobrzy i byli beneficjentami szczęścia i czynników społecznych

David
źródło
2
Witaj David. Podczas cytowania lub cytowania dobrze jest podać odniesienie (dodane!)
Andrew
3

C jest z założenia językiem bardzo niskiego poziomu. To tylko jeden krok od asemblera; znając chipset, na który celujesz, możesz przy odrobinie wiedzy ręcznie „skompilować” C w ASM. Ten rodzaj „zbliżonego do metalu” języka jest kluczem do wysokiego poziomu optymalizacji (wydajności, wydajności pamięci itp.). Ponieważ jednak jest tak blisko metalu, nie dostajesz za darmo tego języka; jest to język proceduralny, nie zorientowany obiektowo, dlatego też praca z takimi konstrukcjami wymaga dużej ilości kodu typu „plateplate” do tworzenia i konsumowania konstrukcji o wielu wartościach w pamięci.

C ++ to „C jeden lepszy”, dodając szereg łatwości użytkowania, takich jak dynamiczny przydział pamięci, wbudowane zestawianie struktur, duża biblioteka predefiniowanego kodu itp., Kosztem pewnych strat wydajności (wciąż znacznie lepszych niż środowiska zarządzanego środowiska wykonawczego). Dla przeciętnego programisty zalety znacznie przewyższają wady w obszarach bazy kodowej, które nie wymagają antywstrzymywalnej kontroli alokacji pamięci itp.

Połączenie tych dwóch jest dość tradycyjne; używasz C do pisania najbardziej krytycznych pod względem wydajności, wydajnych pod względem pamięci obszarów bazy kodu, z którymi możesz następnie pracować w bardziej abstrakcyjny sposób za pomocą wywołań metod z kodu C ++, które mogą być bardziej elegancko zorganizowane i zaprojektowane niż uber-performant , zoptymalizowany uber-oogly kod C.

KeithS
źródło
2
Jeśli chodzi o wydajność, powtórzę: (1) Ludzie z C ++ powiedzą, że to bzdury. (2) Mówię, że nie widzę powodu, dla którego powinno tak być, i chciałbym konkretnych przykładów. Nie przykłady tego, jak łatwo napisać mniej wydajny kod, ale przykłady tego, jak bycie tak wydajnym jak C wymaga nadmiernej brzydoty.
3
Zdefiniuj „brzydki”; Zarabiam na życie w C # i za każdym razem, gdy widzę przykłady kodu C ++ w StackOverflow, zastanawiam się, ile cruft uważa się za normalne w codziennym używaniu języka. Kiedy kodowałem w C ++ w przeszłości, często widziałem kod C i skurczyłem się; typy struktur pakowania ręcznego, obliczenia wskaźnika wykonania dla skoków ręcznych, osadzony ASM ... fuj. Niektórzy narzekają na utratę wiedzy niskiego poziomu wśród programistów Java / .NET; Uważam, że to ogromny dar dla wydajności.
KeithS,
1
Nie odpowiedziałeś na moje pytanie;) Przez „brzydki” mam na myśli „brzydki według standardów guru C ++”. Innymi słowy, przykłady, w których nie można używać „nowoczesnego C ++” i być tak wydajnym jak C.
2
To może być prawda (szczerze mówiąc nie wiem). Jednak w przypadku rozwoju jądra (i kilku innych dziedzin) nie mówimy o przeciętnych programistach.
3
Ludzie zapominają, że C -> C ++ jest kontinuum. Zbyt często argumenty te porównują dobry, nowoczesny C ++ z C. Możesz wziąć program C i kompilować go za pomocą kompilatora C ++ w stosunkowo krótkim czasie i będzie on działał dokładnie tak samo szybko. Jeśli nowoczesna funkcja C ++ jest „zbyt wolna”, nie używaj jej. Może to nawet obejmować takie rzeczy iostream. „Zbyt wolny” nigdy nie jest dobrym pretekstem do używania C zamiast C ++.
Gort the Robot
1

Może się zdarzyć, że w C spędzasz większość czasu na myśleniu o problemie i sposobie kodowania rozwiązania. W C ++ kończy się myślenie o C ++ i jego niezliczonych cechach, funkcjach i niejasnej składni.

Również w wielu sklepach C ++ twój kod jest monitorowany przez „policję mody”, która jest zafascynowana najnowszym zestawem wzorców projektowych lub najnowszymi niezrozumiałymi wypowiedziami wielkiego boga Stroustrupa. Ładny kod staje się bardziej ceniony niż działający kod, znalezienie sposobu wykorzystania najnowszego zestawu szablonów Boost jest bardziej podziwiane niż znalezienie działającego rozwiązania dla firmy.

Z mojego doświadczenia wynika, że ​​dla wszystkich sprytnych funkcji i czystości OO w C ++, kodowanie w zwykłym C przyspiesza i efektywniej wykonuje zadanie.

James Anderson
źródło
0

Możliwe jest, że części C nie są ładnie przenośne dla kompilatora C ++ używanego dla części C ++. Być może kod C jest niezręczny w kompilatorze C w sposób, który nie działa z kompilatorem C ++.

Jeśli masz wysokiej jakości kompilator C ++, prawie nie ma powodu mieszać C i C ++ w projekcie. Prawie.

Jednym z powodów byłoby to, że twój projekt współdzieli kod C z innymi projektami, kod nie kompiluje się jako C ++ i nie chcesz utrzymywać rozwidlenia C ++ tego kodu.

Kaz
źródło
-1

Myślę, że masz to wstecz - extern "C"blok zapewnia, że ​​konwencje wywoływania C są używane dla wszystkich funkcji w bloku. Możesz więc wywoływać funkcje czystego C z C ++, a nie C ++ z C. Bez względu na to, wyobrażam sobie, że ludzie używają zarówno C, jak i C ++, ponieważ wiele bibliotek niskiego poziomu jest napisanych przy użyciu C i łatwiej jest użyć czegoś, co już istnieje (i prawdopodobnie jest debugowany i zoptymalizowany) niż pisanie własnego. OTOH, C ++ oferuje wiele fajnych funkcji wysokiego poziomu, z którymi ludzie wolą pracować, więc używają ich do reszty.

TMN
źródło
-2

Istnieje wiele poziomów wbudowanych platform używających C jako języka programowania (oczywiście możesz swobodnie korzystać z języka asemblera w dowolnym momencie)

W przypadku „poziomu” mówię o poziomie zasobów wewnętrznej pamięci SRAM i pamięci ROM dla systemu.

Platformy te czasami są ograniczone zasobami (np. Niektóre platformy 8051 mają tylko 128 bajtów SRAM użytkownika).

Wspieranie dynamicznej alokacji pamięci przy tak małej ilości pamięci RAM jest bez znaczenia. (nowe / usuń), a nawet Malloc w C.

Jednym z głównych ulepszeń od C do C ++ jest paradygmat obiektowy. C ++ jest odpowiedni w oprogramowaniu o większej pojemności pamięci

ale nie we wbudowanym oprogramowaniu, które ma ograniczenie rozmiaru do 32 KB. (np. w 16-bitowej formie MCU)

Nie ma potrzeby posiadania kompilatora C ++, który jest na ogół bardziej skomplikowany niż kompilator C. (przynajmniej dostawcy SDK nie będą się tym zajmować).

W rzeczywistości nie mogę znaleźć kompilatora C ++ na 32-bitowej platformie ARM7.

To po prostu nie jest warte złożoności

W niektórych 8051 (8 bitów): 1 MB ROM, 128B RAM

TI MSP430 (16 bitów): 32 KB ROM, 4KB RAM

ST Microelectronics ARM 32-bitowy rdzeń procesora Cortex ™ -M3 (STM32F103T4): 16 lub 32 KB pamięci Flash 6 lub 10 KB pamięci SRAM

ansonchau
źródło
2
To nie jest nic nowego w innych odpowiedziach tutaj już opublikowanych.
Martijn Pieters,
32-bitowy kompilator C ++ dla ARM? Jeśli chcesz, możesz samodzielnie skompilować LLVM ze źródła po kilku modyfikacjach, aby uzyskać kompilator C ++ dla iOS.
Cole Johnson
-4

Widzę kilka możliwych powodów:

  • C jest nieco bardziej wydajny w porównaniu do odpowiednika C ++.
  • Niektóre biblioteki, których używają, są napisane w C.
  • Używają niektórych części jądra Linux, które jest napisane w C.

Edytowane: Jak się okazuje, trzeci argument nie jest prawdziwy (patrz komentarze).

Pijusn
źródło
5
(1) Ludzie z C ++ zaczęliby się różnić. I obiektywnie nie widzę powodu, dla którego miałoby tak być. (2) C ++ potrafi dobrze wywoływać kod C (to jest cała kwestia zgodności wstecznej i extern "C").
1
Jeśli MS używa niektórych części jądra Linuksa, a jądrem Linuksa jest GPL, czy to nie znaczy, że Windows również musiałby być GPL?
TomJ
1
@TomJ właśnie dlatego zostali zmuszeni do przekazania kilku tysięcy linii na system Linux. + Jak myślisz, dlaczego wspierają rozwój Linuksa?
Pijusn,
2
@Pius Ma on na myśli (i ma rację AFAIK), gdyby kod GPL był połączony z jądrem systemu Windows, całe jądro musiałoby być objęte licencją GPL (pod warunkiem, że nie ma oddzielnej umowy z właścicielami praw autorskich).
@ delnan, nie jestem pewien szczegółów. Nie jestem prawnikiem, więc nie mogę tego komentować. Ale kilka lat temu był mały skandal. Nie jestem pewien, ale myślę, że mógłbym być modułem sieciowym, na którym polegały wszystkie szalone rzeczy.
Pijusn,
-4

Ponieważ C jest prawdopodobnie lepszym językiem niż C ++. A ponieważ część kodu została napisana zanim C ++ stał się popularny i ludzie nie mieli powodu, aby go zastąpić.

A ponieważ C ++ ma wiele funkcji, które mogą uszkodzić Twój kod, jeśli nie będziesz ostrożny podczas używania go w jądrze.

Minthos
źródło
C ++ oczekuje bibliotek dynamicznych? A czym jest pamięć dynamiczna?
4
-1 dla [stuff] that C++ expects. Dlaczego C ++ używa więcej sterty niż C? Dlaczego C ++ wymaga więcej bibliotek DLL niż C? Po prostu NIE PRAWDA
Thomas Eding,
1
Korekta: tracisz biblioteki napisane w C ++. Wróć do kwadratu 1, jeśli używasz C ++ lub C. Po co ograniczać się do funkcjonalności C, jeśli możesz używać szablonów, klas, destruktorów, const i wszelkiego rodzaju innych dobroci.
Thomas Eding,
6
Co? Szablony, wyjątki, obiekty (w tym konstruktory, destruktory, przeciążone operatory, teraz przenoszą konstrukcję i - praktycznie? - wszystko inne) itp. Działają dobrze bez względu na to, skąd pochodzi pamięć (stos, pamięć statyczna, pula niestandardowa itp.) . Standardowe kontenery domyślnie używają alokacji sterty, ale ich alokatory można dostosować, a ludzie jądra piszą własne kolekcje i zarządzanie pamięcią. -1
2
FYI: Usunąłem moją opinię, ponieważ nie sądzę, aby twoja odpowiedź była obecnie szkodliwa, ale nie uważam jej za szczególnie przydatną ani wnikliwą.