• Nie Znaleziono Wyników

Emulacja systemów komputerowych

3. Metodyka symulacji bł˛edów w emulowanym ´srodowisku

3.2. Emulacja systemów komputerowych

Emulatory systemów komputerowych s ˛a bardzo popularnymi narz˛edziami w przemy´sle informatycznym. Na rynku dost˛epnych jest wiele rozwi ˛aza´n, które ró˙zni ˛a si˛e posiadanymi funkcjami i s ˛a przeznaczone do ró˙znych zada´n: testowanie oprogramowania [98], izolacja

´srodowiska wykonania oprogramowania [20], czy zarz ˛adzanie wirtualnymi komputerami1. W niniejszych rozwa˙zaniach emulator systemu komputerowego jest zdefiniowany nast˛epuj ˛aco:

Definicja 3.2.1. Emulatorem systemu komputerowego A jest oprogramowanie działaj ˛ace na systemie komputerowym B, które umo˙zliwia działanie niezmodyfikowanego oprogramowania X przeznaczonego na system komputerowy A.

Definicja 3.2.1 okre´sla główne wymagania sformułowane dla emulatorów – oprogramowanie X nie mo˙ze by´c zmodyfikowane w ˙zaden sposób oraz musi realizowa´c programowo ´srodowisko systemu komputerowego (patrz 2.1.1). Niemniej emulatory mog ˛a pracowa´c na ró˙znych poziomach abstrakcji. Warto wyró˙zni´c trzy poziomy abstrakcji:

— emulacja układów cyfrowych,

— emulacja logicznego funkcjonowania urz ˛adze´n,

— emulacja systemu operacyjnego.

Emulacja na poziome układów cyfrowych oznacza modelowanie systemu komputerowego A poprzez niskopoziomow ˛a specyfikacj˛e sprz˛etu – np. definiowanie procesorów, pami˛eci i innych urz ˛adze´n w j˛ezyku VHDL. Przykłady bada´n ukierunkowanych na przeprowadzanie eksperymentów na modelach mikrokontrolerów mo˙zna znale´z´c w [43, 44, 47] . Warto zaznaczy´c, ˙ze ten sposób emulacji jest rozwi ˛azaniem bardzo kosztownym obliczeniowo przy zastosowaniu dla wszystkich komponentów systemu komputerowego, co czyni to rozwi ˛azanie niepraktycznym.

Emulacja na poziomie logicznego funkcjonowania urz ˛adze´n pozwala na modelowanie sprz˛etu poprzez implementacj˛e funkcji, które s ˛a przez niego realizowane. Przykładem

1 http://www.vmware.com/pdf/virtualization.pdf

jest emulacja dysku twardego systemu komputerowego A poprzez moduł programowy, który wykorzystuje plik w systemie B do realizacji funkcji zapisu i odczytu danych.

Zadaniem modułu programowego jest wtedy implementacja protokołu wymiany danych mi˛edzy urz ˛adzeniem, a pozostałymi komponentami emulowanego systemu – np. SATA. Podej´scie takie pozwala na emulacj˛e sprz˛etu bez znajomo´sci jego wewn˛etrznej struktury. Dodatkowo jest ono wydajniejsze od emulacji układów cyfrowych z uwagi na mo˙zliwo´s´c wykorzystania mechanizmów dost˛epnych w systemie B bez narzutu na replikacj˛e wewn˛etrznych stanów emulowanych urz ˛adze´n.

Emulacja na poziomie systemu operacyjnego zwi ˛azana jest z ograniczeniem oprogramowania X tylko do aplikacji u˙zytkownika. Aplikacje te komunikuj ˛a si˛e z systemem operacyjnym poprzez wywołania systemowe (patrz 2.1.2). Koncepcja emulacji na poziomie systemu operacyjnego polega na przechwytywaniu wywoła´n systemowych pochodz ˛acych z oprogramowania X i obsługi ich przez oprogramowanie emulatora, który w razie potrzeby wywołuje system operacyjny działaj ˛acy na systemie B. Dzi˛eki tej metodzie na potrzeby oprogramowania X emulowany jest jedynie procesor i pami˛e´c systemu A, natomiast dost˛ep do dodatkowych urz ˛adze´n takich jak dyski twarde, czy interfejs sieciowy realizowany jest przez system B.

Ciekaw ˛a technik ˛a cz˛esto wykorzystywan ˛a w emulacji jest równie˙z mo˙zliwo´s´c współdzielenia urz ˛adze´n wyst˛epuj ˛acych w systemie A z systemem B – rozwi ˛azanie takie jest mo˙zliwe przy wsparciu systemu operacyjnego maszyny emuluj ˛acej. Zasada działania jest nast˛epuj ˛aca: dane wysyłane do urz ˛adzenia s ˛a przechwytywane z emulowanej magistrali do systemu operacyjnego rzeczywistego komputera, który nast˛epnie wysyła je do urz ˛adzenia (z zastosowaniem rzeczywistej magistrali). Analogicznie system operacyjny rzeczywistego komputera przekazuje (ewentualnie po wst˛epnej obróbce) dane otrzymane z urz ˛adzenia do oprogramowania emulatora, po czym przekazywane s ˛a one do emulowanego systemu za po´srednictwem emulowanej magistrali. W [73] przedstawiono mechanizm udost˛epniania rzeczywistych urz ˛adze´n USB dla emulowanego systemu w sposób imituj ˛acy bezpo´srednie podł ˛aczenie tych urz ˛adze´n. Niemniej podobne rozwi ˛azania s ˛a dost˛epne w wielu komercyjnych rozwi ˛azaniach (VMware, VirtualBox, Virtual PC) – w szczególno´sci mo˙zliwe jest wykorzystanie przez emulowany system rzeczywistego dysku twardego, urz ˛adze´n USB, czy kart graficznych.

W implementacjach emulatorów mog ˛a by´c stosowane rozwi ˛azania hybrydowe, gdzie emulacja poszczególnych urz ˛adze´n realizowana jest na ró˙znych poziomach abstrakcji.

Przykładowo procesor systemu A mo˙ze by´c emulowany na poziomie układów cyfrowych, pami˛e´c RAM jako plik rezyduj ˛acy w systemie B, a pozostałe urz ˛adzenia s ˛a dost˛epne za po´srednictwem systemu operacyjnego działaj ˛acego na systemie B.

W dalszej cz˛e´sci rozdziału opisane s ˛a wył ˛acznie zagadnienia zwi ˛azane z emulacj ˛a na poziomie logicznego funkcjonowania urz ˛adze´n. Emulacja na poziomie układów cyfrowych została wykluczona z powodu niedost˛epno´sci wewn˛etrznej specyfikacji poszczególnych urz ˛adze´n – stanowi ˛a one własno´s´c intelektualn ˛a producentów i cz˛esto nie s ˛a udost˛epniane publicznie. Natomiast emulacja na poziomie systemu operacyjnego ograniczyłaby badania do oprogramowania aplikacji u˙zytkownika, co stoi w sprzeczno´sci z jednym z celów rozprawy, jakim jest ewaluacja oprogramowania systemu operacyjnego.

Przy emulacji na poziomie logicznego funkcjonowania urz ˛adze´n implementacja modelu pami˛eci oraz urz ˛adze´n wej´scia/wyj´scia mo˙ze by´c zbli˙zona mi˛edzy ró˙znymi emulatorami.

Bardzo istotnym zagadnieniem jest sposób emulacji jednostek przetwarzaj ˛acych, który ma decyduj ˛acy wpływ na wydajno´s´c. Nale˙zy wyró˙zni´c trzy główne sposoby rozwi ˛azania tego problemu:

— interpretacja,

— translacja binarna,

— wirtualizacja.

Wszystkie z wymienionych technik maj ˛a za zadanie obsług˛e zestawu instrukcji architektury (ISA2) emulowanego procesora, jednak ka˙zda z nich ma swoje zalety oraz ograniczenia. Poni˙zej zamieszczona jest charakterystyka poszczególnych rozwi ˛aza´n.

Interpretacja

Technika interpretowania polega na obsłudze emulowanych instrukcji procesora pojedynczo przez program emulatora. Emulator pobiera instrukcj˛e programu emulowanego, dekoduje j ˛a przy pomocy zestawu instrukcji warunkowych if lub switch, a nast˛epnie pobiera argumenty instrukcji i wykonuje przypisane jej działanie, modyfikuj ˛ac struktur˛e danych reprezentuj ˛ac ˛a stan emulowanego procesora.

Interpretacja jest bardzo prost ˛a technik ˛a emulacji procesora, jednak nie jest ona wydajna.

Spowodowane jest to tym, ˙ze współczesne procesory s ˛a budowane w oparciu o potoki instrukcji.

Rozwi ˛azanie to powoduje przy´spieszenie wykonania kodu, gdy trafnie przewidywane s ˛a adresy docelowe instrukcji skoków. Za ka˙zdym razem, gdy przewidywanie nie powiedzie si˛e, nast˛epuje uniewa˙znienie zawarto´sci potoku i konieczne jest ładowanie nowego zestawu instrukcji do wykonania. W przypadku interpretacji przewidywanie skoków jest zadaniem bardzo trudnym z uwagi na dekodowanie emulowanych instrukcji, gdzie konieczne jest wykonanie wielu skoków warunkowych w procesie wyboru odpowiedniej procedury obsługi.

Zagadnienie optymalizacji interpretacji jest szeroko opisane w literaturze [11, 13, 32, 67], jednak wiele z nich polega na manipulowaniu zestawem emulowanych instrukcji3,

2 Ang. Instruction Set Architecture.

3 Techniki te s ˛a stosowane np. w przypadku kompilowania j˛ezyka programowania do bytecode’u, który podlega interpretacji.

co nie jest mo˙zliwe w przypadku emulowania konkretnego ISA. Zdaniem autora jedn ˛a z najbardziej interesuj ˛acych technik, która pozwala na przy´spieszenie interpretacji dowolnego ISA jest context threading (patrz [13]). Technika ta jest dwuetapowa. Pierwszy etap to przygotowanie dynamicznego bufora wypełnionego instrukcjami wywoła´n procedur obsługi kolejnych emulowanych instrukcji. Drugi etap polega na wykonaniu instrukcji zawartych w tym buforze. Pozwala to na wyeliminowanie dekodowania emulowanej instrukcji podczas interpretacji. Pewnym problemem pozostaj ˛a instrukcje skoku, które musz ˛a by´c tłumaczone na zestawy instrukcji maszyny emuluj ˛acej i równie˙z umieszczone w buforze, co czyni to rozwi ˛azanie zbli˙zonym do translacji binarnej.

Translacja binarna

Celem translacji binarnej jest przetłumaczenie instrukcji emulowanego systemu na instrukcje emuluj ˛acego systemu komputerowego. Mo˙zna wyró˙zni´c dwa rodzaje translacji binarnej:

statyczna oraz dynamiczna.

Statyczna translacja binarna polega na przetłumaczeniu pliku wykonywalnego zawieraj ˛acego instrukcje maszyny emulowanej na plik wykonywalny maszyny emuluj ˛acej w celu pó´zniejszego uruchomienia. Technika ta jednak nie pozwala na uruchamianie wszystkich typów programów. W szczególno´sci nie jest mo˙zliwe uruchomienie programów wykorzystuj ˛acych samo-modyfikacj˛e (patrz [46]). Zmiany wprowadzone w uruchomionym programie zostan ˛a przetłumaczone na instrukcje maszyny emuluj ˛acej i spowoduje to w konsekwencji bł˛edne działanie. Dodatkowo próba emulowania całego systemu operacyjnego t ˛a technik ˛a byłaby bardzo niepraktyczna w zwi ˛azku z konieczno´sci ˛a translacji statycznej nie tylko systemu operacyjnego, ale równie˙z i programów u˙zytkownika. W efekcie statyczna translacja binarna jest rzadko stosowana w praktyce.

Technika dynamicznej translacji binarnej skupia si˛e na tłumaczeniu bloków instrukcji podlegaj ˛acych emulacji w trakcie działania programu. W momencie napotkania nieprzetłumaczonego kodu emulator tłumaczy instrukcje systemu emulowanego na reprezentacj˛e po´sredni ˛a. Reprezentacja po´srednia jest etapem translacji binarnej, pozwalaj ˛acym na wykonanie nast˛epuj ˛acych operacji:

— zapis instrukcji logicznych wykonywanych wewn ˛atrz bloku,

— wygenerowanie poprawnych adresów docelowych dla instrukcji skoków wewn ˛atrz tłumaczonego bloku instrukcji,

— wygenerowanie wywoła´n odpowiednich procedur dla skoków poza przestrze´n aktualnie przetwarzanego bloku,

— opcjonalna optymalizacja technikami takimi jak peephole optimization (patrz [3, 7]).

Rozwi ˛azanie to jest bardzo efektywne, poniewa˙z pozwala na zachowanie cz˛esto wykonywanych bloków i odwoływanie si˛e do nich bez konieczno´sci ponownego tłumaczenia. Opcjonalne zastosowanie peephole optimization polega na automatycznym skanowaniu ci ˛agłych

fragmentów kodu składaj ˛acych si˛e z od kilku do kilkunastu instrukcji (nazywanych oknem) w celu eliminacji zb˛ednych operacji lub zamianie instrukcji na ich szybsze odpowiedniki.

Przykłady takich optymalizacji to np. zast˛epowanie wyra˙ze´n, które mo˙zna obliczy´c w czasie kompilacji, wyra˙zeniami stałymi, czy zamiana operacji mno˙zenia przez liczb˛e b˛ed ˛ac ˛a pot˛eg ˛a liczby 2 na operacj˛e przesuni˛ecia bitowego z uwagi na szybsz ˛a realizacj˛e w czasie wykonania.

Peephole optimizationmo˙ze by´c stosowane zarówno na kodzie reprezentacji po´sredniej jak i na kodzie konkretnego ISA.

Technika dynamicznej translacji binarnej znana jest równie˙z pod nazw ˛a Just-in-time compilationi jest szeroko stosowana w popularnych ´srodowiskach Java (patrz [89]) oraz .Net (patrz [107]), gdzie tłumaczeniu na ISA maszyny emuluj ˛acej podlega bytecode. Bytecode jest typem reprezentacji po´sredniej neutralnej wzgl˛edem docelowego ISA – tzn. składa si˛e z podstawowego zestawu instrukcji, który mo˙ze by´c przetłumaczony na ka˙zd ˛a z obsługiwanych ISA. Kod bytecode z zało˙zenia nie wykorzystuje wszystkich mo˙zliwo´sci docelowej architektury, poniewa˙z mogłoby to uniemo˙zliwi´c przeno´sno´s´c na inne architektury.

Dopiero dzi˛eki zastosowaniu translacji binarnej tworzony jest kod wykonywalny przystosowany do ko´ncowej platformy.

Wirtualizacja

Szczególnym przypadkiem emulacji jest wirtualizacja. Technika ta wymaga spełnienia dodatkowego warunku zaw˛e˙zaj ˛acego definicj˛e 3.2.1: oprogramowanie X mo˙ze by´c uruchamiane bezpo´srednio na systemie komputerowym B. Warunek ten sprowadza si˛e do mo˙zliwo´sci emulowania jedynie oprogramowania skompilowanego na t˛e sam ˛a architektur˛e ISA, na której działa system emuluj ˛acy.

Zasada działania wirtualizacji to trap-and-emulate (patrz [14]) – instrukcje emulowanego systemu komputerowego s ˛a wykonywane bezpo´srednio przez procesor systemu emuluj ˛acego, a˙z napotkana zostanie instrukcja wymagaj ˛aca interakcji z urz ˛adzeniami wej´scia/wyj´scia (patrz [2]). Oprogramowanie emulatora przechwytuje takie ˙z ˛adania, emuluje działanie sprz˛etu, a nast˛epnie zwraca odpowiedni rezultat do systemu emulowanego sygnalizuj ˛ac gotowo´s´c rezultatu poprzez wygenerowanie emulowanego przerwania sprz˛etowego. Emulowane s ˛a równie˙z dodatkowe urz ˛adzenia takie jak zegary, czy kontroler pami˛eci MMU.

Wirtualizacja jest najszybsz ˛a technik ˛a emulacji, poniewa˙z nie wymaga ani interpretowania, ani tłumaczenia instrukcji systemu emulowanego. Niemniej warto zauwa˙zy´c, ˙ze pierwsze implementacje trap-and-emulate nie były szybsze od translacji binarnej (patrz [2]), co było spowodowane cz˛estymi zmianami kontekstu procesora mi˛edzy systemem emulowanym a emuluj ˛acym. Na dzie´n dzisiejszy wirtualizacja jest najpopularniejsz ˛a technik ˛a emulacji, wła´snie dzi˛eki swojej wydajno´sci.

Ka˙zda z przedstawionych technik emulacji mo˙ze by´c wykorzystywania w procesie wstrzykiwania bł˛edów, jednak wybrana metoda mo˙ze okaza´c si˛e nieodpowiednia do pewnych zastosowa´n w zwi ˛azku z powi ˛azanymi z ni ˛a ograniczeniami. Wybór emulatora na potrzeby niniejszej rozprawy wraz z uzasadnieniem został opisany w sekcji 3.4.1.