Klasyczne zarządzanie pamięcią w systemie Mac OS - Classic Mac OS memory management

Okno „O tym komputerze” Mac OS 9.1 pokazujące zużycie pamięci przez każdą otwartą aplikację i samo oprogramowanie systemowe.

Historycznie rzecz biorąc, klasyczny Mac OS wykorzystywał formę zarządzania pamięcią , która wypadła z łask w nowoczesnych systemach. Krytyka tego podejścia był jednym z kluczowych obszarów skierowana przez zmianę systemu Mac OS X .

Pierwotnym problemem dla inżynierów Macintosha było optymalne wykorzystanie 128 KB pamięci RAM, w którą była wyposażona maszyna, na sprzęcie komputerowym opartym na Motoroli 68000 , który nie obsługiwał pamięci wirtualnej . Ponieważ w tym czasie maszyna mogła uruchamiać tylko jeden program użytkowy na raz i nie było stałej dodatkowej pamięci masowej , inżynierowie zaimplementowali prosty schemat, który działał dobrze z tymi konkretnymi ograniczeniami. Ten wybór projektowy nie był dobrze skalowany wraz z rozwojem maszyny, stwarzając różne trudności zarówno dla programistów, jak i użytkowników.

Podział

Wydaje się, że głównym zmartwieniem pierwotnych inżynierów była fragmentacja - to znaczy powtarzające się przydzielanie i zwalnianie pamięci za pomocą wskaźników prowadzących do wielu małych izolowanych obszarów pamięci, których nie można użyć, ponieważ są zbyt małe, nawet jeśli całkowita wolna pamięć może być wystarczające, aby spełnić określone żądanie pamięci. Aby rozwiązać ten problem, inżynierowie Apple zastosowali koncepcję przemieszczalnego uchwytu , odniesienia do pamięci, które umożliwiło przeniesienie rzeczywistych danych bez unieważniania uchwytu. Schemat Apple był prosty - uchwyt był po prostu wskaźnikiem do (nie relokowalnej) tabeli dalszych wskaźników, które z kolei wskazywały na dane. Jeśli żądanie pamięci wymagało zagęszczenia pamięci, zostało to zrobione, a tabela, zwana głównym blokiem wskaźnika, została zaktualizowana. Maszyna sama zaimplementowała dwa obszary pamięci dostępnej dla tego schematu - stertę systemową (używaną dla systemu operacyjnego) i stertę aplikacji. Dopóki była uruchomiona tylko jedna aplikacja naraz, system działał dobrze. Ponieważ cała sterta aplikacji została rozwiązana po zamknięciu aplikacji, fragmentacja została zminimalizowana.

System zarządzania pamięcią miał słabe punkty; sterta systemu nie była chroniona przed błędnymi aplikacjami, co byłoby możliwe, gdyby architektura systemu obsługiwała ochronę pamięci , a to często było przyczyną problemów i awarii systemu. Ponadto podejście oparte na uchwytach otworzyło również źródło błędów programistycznych, w przypadku których nie można zagwarantować, że wskaźniki do danych w takich relokowalnych blokach pozostaną ważne dla wywołań, które mogą powodować przemieszczanie się pamięci. To był prawdziwy problem dla prawie każdego istniejącego systemowego API . Ze względu na przejrzystość struktur danych należących do systemu w tamtym czasie interfejsy API niewiele mogły zrobić, aby rozwiązać ten problem. W związku z tym na programiście spoczywał ciężar, aby nie tworzyć takich wskaźników lub przynajmniej bardzo ostrożnie nimi zarządzać, usuwając wszystkie uchwyty po każdym takim wywołaniu API. Ponieważ wielu programistów ogólnie nie było zaznajomionych z tym podejściem, wczesne programy dla komputerów Mac często cierpiały z powodu wynikających z tego błędów.

Palm OS i 16-bitowy Windows używają podobnego schematu do zarządzania pamięcią, ale wersje Palm i Windows utrudniają błąd programisty. Na przykład w systemie Mac OS, aby przekonwertować uchwyt na wskaźnik, program po prostu usuwa odniesienia bezpośrednio do uchwytu, ale jeśli uchwyt nie jest zablokowany, wskaźnik może szybko stać się nieprawidłowy. Wezwania do zablokowania i odblokowania uchwytów nie są zrównoważone; dziesięć wezwań do HLock jest cofniętych przez jedno wezwanie do HUnlock . W systemie Palm OS i Windows uchwyty są nieprzezroczyste i należy je usunąć za pomocą MemHandleLock w systemie Palm OS lub Global/LocalLock Windows. Gdy aplikacja Palm lub Windows zakończy pracę z uchwytem, ​​wywołuje MemHandleUnlock lub Global/LocalUnlock . Palm OS i Windows utrzymują liczbę blokad dla bloków; po trzech wywołaniach MemHandleLock blok zostanie odblokowany dopiero po trzech wywołaniach MemHandleUnlock .

Rozwiązanie problemu zagnieżdżonych blokad i odblokowań może być proste (choć żmudne) dzięki zastosowaniu różnych metod, ale przeszkadzają one w czytelności powiązanego bloku kodu i wymagają świadomości i dyscypliny ze strony programisty.

Wycieki pamięci i nieaktualne odniesienia

Świadomość i dyscyplina są również konieczne, aby uniknąć „wycieków” pamięci (niepowodzenie w zwalnianiu alokacji w ramach alokacji) oraz aby uniknąć odniesień do przestarzałych uchwytów po wydaniu (co zwykle skutkowało twardą awarią - irytujące w systemie jednozadaniowym, potencjalnie katastrofalne, jeśli inne programy są uruchomione).

Przełącznik

Sytuacja pogorszyła się wraz z pojawieniem się Switchera , który był sposobem dla Maca z 512KB lub więcej pamięci do uruchamiania wielu aplikacji jednocześnie. Był to konieczny krok naprzód dla użytkowników, dla których podejście polegające na pojedynczej aplikacji było bardzo ograniczające. Ponieważ firma Apple była teraz zaangażowana w model zarządzania pamięcią, a także zgodność z istniejącymi aplikacjami, została zmuszona do przyjęcia schematu, w którym każdej aplikacji przydzielono własną stertę z dostępnej pamięci RAM. Ilość rzeczywistej pamięci RAM przydzielonej do każdej sterty została ustalona przez wartość zakodowaną w metadanych każdej aplikacji, ustawioną przez programistę. Czasami ta wartość nie wystarczała do określonych rodzajów pracy, więc ustawienie wartości musiało zostać ujawnione użytkownikowi, aby umożliwić mu dostosowanie rozmiaru sterty do własnych wymagań. Choć popularne wśród „ zaawansowanych użytkowników ”, to ujawnienie szczegółów technicznych implementacji było sprzeczne z filozofią użytkowników komputerów Mac. Oprócz narażania użytkowników na ezoteryczne szczegóły techniczne, było to nieefektywne, ponieważ aplikacja musiałaby przejąć całą przydzieloną pamięć RAM, nawet jeśli pozostawiłaby większość z niej później nieużywaną. Inna aplikacja może być pozbawiona pamięci, ale nie będzie w stanie wykorzystać wolnej pamięci „należącej” do innej aplikacji.

Chociaż aplikacja nie mogłaby pożytecznie wykorzystać sterty aplikacji siostrzanej, z pewnością mogłaby ją zniszczyć, zazwyczaj przez nieumyślne pisanie na nonsensowny adres. Aplikacja przypadkowo traktująca fragment tekstu lub obrazu lub nieprzypisaną lokalizację jako wskaźnik może łatwo nadpisać kod lub dane innych aplikacji, a nawet systemu operacyjnego, pozostawiając „lurkerów” nawet po zamknięciu programu. Takie problemy mogą być niezwykle trudne do przeanalizowania i skorygowania.

Switcher ewoluował do MultiFindera w Systemie 4.2, który stał się Menedżerem Procesów w Systemie 7 , a do tego czasu schemat był już dawno zakorzeniony. Apple podjął pewne próby obejścia oczywistych ograniczeń - pamięć tymczasowa była taką, w której aplikacja mogła „pożyczyć” wolną pamięć RAM, która leżała poza jej stosem na krótkie okresy, ale była to niepopularna wśród programistów, więc w dużej mierze nie rozwiązała problemów. Dodatek Apple System 7 Tune-up dodał „minimalny” rozmiar pamięci i „preferowany” rozmiar - jeśli preferowana ilość pamięci nie była dostępna, program mógł zostać uruchomiony na minimalnej przestrzeni, prawdopodobnie z ograniczoną funkcjonalnością. Zostało to włączone do standardowego systemu operacyjnego począwszy od Systemu 7.1, ale nadal nie rozwiązało problemu z rootem.

Schematy pamięci wirtualnej , które udostępniły więcej pamięci poprzez stronicowanie nieużywanych części pamięci na dysk, zostały udostępnione przez narzędzia innych firm, takie jak Connectix Virtual , a następnie przez Apple w System 7 . Zwiększyło to pojemność pamięci Macintosha kosztem wydajności, ale nie dodało pamięci chronionej ani nie zapobiegło kompaktowaniu sterty przez menedżera pamięci, co mogłoby unieważnić niektóre wskaźniki.

32-bitowy czysty

Pierwotnie Macintosh miał 128 kB pamięci RAM, z ograniczeniem 512 kB. Liczba ta została zwiększona do 4 MB po wprowadzeniu komputera Macintosh Plus . Te komputery Macintosh korzystały z procesora 68000 , 32-bitowego procesora, ale miały tylko 24 fizyczne linie adresowe. Te 24 linie pozwoliły procesorowi zaadresować do 16 MB pamięci (2 24 bajty), co było wówczas postrzegane jako wystarczająca ilość. Limit pamięci RAM w projekcie Macintosha wynosił 4 MB pamięci RAM i 4 MB pamięci ROM , ze względu na strukturę mapy pamięci. Zostało to naprawione poprzez zmianę mapy pamięci w komputerach Macintosh II i Macintosh Portable , umożliwiając do 8 MB pamięci RAM.

Ponieważ pamięć była rzadkim zasobem, autorzy Mac OS postanowili wykorzystać nieużywany bajt w każdym adresie. Oryginalny menedżer pamięci (aż do pojawienia się Systemu 7) umieszczał flagi w 8-mych wysokich bitach każdego 32-bitowego wskaźnika i uchwytu . Każdy adres zawierał flagi, takie jak „zablokowane”, „usuwalne” lub „zasób”, które były przechowywane w głównej tablicy wskaźników. Gdy używane jako rzeczywisty adres, flagi te były maskowane i ignorowane przez procesor.

Chociaż dobrze wykorzystano bardzo ograniczoną przestrzeń RAM, projekt ten spowodował problemy, gdy Apple wprowadził Macintosh II, który wykorzystywał 32-bitowy procesor Motorola 68020 . 68020 miał 32 fizyczne linie adresowe, które mogły adresować do 4 GB (2 32 bajty) pamięci. Flagi, które Menedżer pamięci zapisywał w wysokim bajcie każdego wskaźnika i uchwytu, były teraz znaczące i mogą prowadzić do błędów adresowania.

Teoretycznie architekci oprogramowania systemu Macintosh mieli swobodę zmiany schematu „flag w wysokim bajcie”, aby uniknąć tego problemu, i to zrobili. Na przykład na komputerach Macintosh IIci i nowszych, HLock() a inne interfejsy API zostały przepisane, aby zaimplementować blokowanie uchwytów w sposób inny niż oznaczanie wysokich bitów uchwytów. Jednak wielu programistów aplikacji dla komputerów Macintosh i znaczna część samego kodu oprogramowania systemu Macintosh uzyskiwało bezpośredni dostęp do flag, zamiast korzystać z interfejsów API, takich jak te HLock() , które zostały udostępnione do manipulowania nimi. W ten sposób sprawili, że ich aplikacje były niekompatybilne z prawdziwym 32-bitowym adresowaniem, co stało się znane jako brak „32-bitowej czystości”.

Aby zatrzymać ciągłe awarie systemu spowodowane tym problemem, system 6 i wcześniejszy działający na 68020 lub 68030 zmuszałby maszynę do przełączenia w tryb 24-bitowy i rozpoznawałby i rozwiązał tylko pierwsze 8 megabajtów pamięci RAM, co jest oczywistą wadą w maszyny, których sprzęt został podłączony tak, aby przyjmować do 128 MB pamięci RAM - i których literatura dotycząca produktów reklamowała tę możliwość. W przypadku Systemu 7 oprogramowanie systemu Mac zostało ostatecznie wyczyszczone w 32-bitach, ale nadal występował problem z brudnymi pamięciami ROM. Problem polegał na tym, że decyzja o zastosowaniu 24-bitowego lub 32-bitowego adresowania musiała zostać podjęta na bardzo wczesnym etapie procesu rozruchu, kiedy procedury ROM zainicjowały Menedżera pamięci w celu skonfigurowania podstawowego środowiska Mac, w którym ładowane są ROMy NuBus i sterowniki dysków. i stracony. Starsze ROM-y nie obsługiwały 32-bitowego programu Memory Manager, więc nie można było uruchomić w trybie 32-bitowym. Zaskakujące jest, że pierwsze rozwiązanie tej usterki zostało opublikowane przez firmę Connectix , której produkt z 1991 roku MODE32 ponownie zainicjował Menedżera pamięci i powtórzył wczesne części procesu rozruchu komputera Mac, umożliwiając uruchomienie systemu w trybie 32-bitowym i umożliwiając korzystanie z wszystkich pamięć RAM w komputerze. Apple uzyskało licencję na oprogramowanie od Connectix później w 1991 roku i rozprowadzało je bezpłatnie. Komputery Macintosh IIci i późniejsze komputery Macintosh oparte na Motoroli miały 32-bitowe czyste ROM.

Minęło sporo czasu, zanim aplikacje zostały zaktualizowane w celu usunięcia wszystkich 24-bitowych zależności, a System 7 zapewnił sposób przełączenia z powrotem do trybu 24-bitowego w przypadku wykrycia niezgodności aplikacji. Do czasu migracji do PowerPC i Systemu 7.1.2 czystość 32-bitowa była wymagana do tworzenia aplikacji natywnych, a nawet późniejsze komputery Mac oparte na Motoroli 68040 nie mogły obsługiwać trybu 24-bitowego.

Orientacja obiektu

Powstanie obiektowych języków programowania dla Mac - pierwszy Object Pascal , potem C ++ - także źródłem problemów dla modelu pamięci przyjęty. Na początku wydawałoby się naturalne, że obiekty byłyby implementowane za pomocą uchwytów, aby zyskać przewagę w postaci możliwości przemieszczania. Języki te, tak jak zostały pierwotnie zaprojektowane, używały wskaźników do obiektów, co prowadziło do problemów fragmentacji. Rozwiązaniem zaimplementowanym przez kompilatory THINK (później Symantec ) było użycie uchwytów wewnętrznie dla obiektów, ale dostęp do nich przy użyciu składni wskaźnika. Na początku wydawało się to dobrym pomysłem, ale wkrótce pojawiły się głębokie problemy, ponieważ programiści nie mogli powiedzieć, czy mają do czynienia z blokiem przenoszonym, czy stałym, i nie mieli więc sposobu, aby wiedzieć, czy podjąć się zadania blokowania obiektów, czy nie. Nie trzeba dodawać, że doprowadziło to do ogromnej liczby błędów i problemów z tymi wczesnymi implementacjami obiektów. Późniejsze kompilatory nie próbowały tego robić, ale używały rzeczywistych wskaźników, często implementując własne schematy alokacji pamięci, aby obejść model pamięci Mac OS.

Podczas gdy model pamięci Mac OS, ze wszystkimi jego nieodłącznymi problemami, pozostał taki sam aż do Mac OS 9 , ze względu na poważne ograniczenia kompatybilności aplikacji, rosnąca dostępność taniej pamięci RAM oznaczała, że ​​w zasadzie większość użytkowników mogła uaktualnić swoją drogę wyjścia z kąt. Pamięć nie była wykorzystywana efektywnie, ale była na tyle duża, że ​​problem nigdy nie stał się krytyczny. Jest to ironiczne, biorąc pod uwagę, że celem oryginalnego projektu było maksymalne wykorzystanie bardzo ograniczonej ilości pamięci. Mac OS X ostatecznie pozbył się całego schematu, wdrażając nowoczesny schemat rzadkiej pamięci wirtualnej . Podzbiór starszych interfejsów API modelu pamięci nadal istnieje w celu zapewnienia zgodności jako część Carbon , ale jest mapowany do nowoczesnego menedżera pamięci ( malloc implementacja bezpieczna wątkowo) poniżej. Apple zaleca, Mac OS X wykorzystanie kodu malloc i free „niemal wyłącznie”.

Bibliografia

Zewnętrzne linki