Prezentacja User-Mode LinuxKatarzyna Rempoła, Jakub Tomiczek, Adam Warski |
User-Mode Linux to system operacyjny Linux zagnieżdżony w zewnętrznym systemie operacyjnym (Linux). Koncepcja jego stworzenia zrodziła się na początku 1999 roku, a jej autorem jest Jeff Dike. UML zaczął powstawać jako łatka do jądra 2.0. Oryginalnie był projektowany jako narzędzie develpoerów, które miało przyspieszyć rozwijanie oprogramowania i zredukować wpływ wymagań sprzętowych. Pierwsza wersja ukazała się wiosną 2000 roku. Od tego czasu UML jest w ciągłym rozwoju i do każdej nowej wersji jądra Linuxa powstaje odpowiednia łatka. |
Idea zagnieżdżania systemów operacyjnych bardzo się rozpowszechniła i obecnie User-Mode Linux nie jest jedynym projektem tego typu. Konkurencyjne projekty to między innymi: Linux on Linux, Windows on Linux, Linux on Windows.
UML nie jest najszybszym do uruchomienia i najłatwiejszym w obsłudze spośród nich. Ma jednak wiele istotnych przewag, które wynikają z towarzyszącej mu i ściśle przestrzeganej ideologii.
W przeciwieństwie do normalnego jądra systemu operacyjnego Linux, UML nie komunikuje się ze sprzętem. Jest od niego oddzielony warstwą macierzystego systemu - hosta. Zamiast z interfejsu hardware'u, korzysta z interfejsu funkcji systemowych udostępnianych przez hosta. Z punktu widzenia hosta jest więc zwykłym procesem działającym w przestrzeni użytkownika: nie ma dostępu do pamięci systemu macierzystego. Za to z punktu widzenia uruchomionych w nim aplikacji jest jądrem: z własną pamięcią wirtualną, schedulerem (planistą) oraz dostępem do zasobów. Procesy nie mają dostępu do jego przestrzeni adresowej.
User-Mode Linux umożliwia uruchomienie wielu wirtualnych komputerów pracujących pod kontrolą systemu Linux, które są odizolowane zarówno od siebie, jak i od systemu macierzystego (wraz z kontrolowanym przez niego sprzętem).
W odróżnieniu od konkurencyjnych projektów, twórcy UML-a dbają o to, aby w jak największym stopniu pozostał on zwykłym programem. Wynikają z tego następujące zalety:
Niektóre zastosowania UML-a zaskoczyły samego autora, Jeffa Dike'a. Oto niektóre z nich:
Najłatwiej uzyskać User-Mode Linux ściągając RPM-a, gdzie będzie skompilowane jądro, dokumentacja i
narzędzia (uml_moo, mkcow, mconsole...). Dodatkowo trzeba ściągnąć specjalnie przygotowany pod UML-a
system plików.
Po zainstalowaniu za pomocą wywołania
[host]$ rpm -ivh nazwa_pakietu
można już uruchomić UML-a wywołując
[host]$ /usr/bin/linux (lub linux)
Przebiega jak zwykła kompilacja jądra.
[host]$ mkdir ~/uml
[host]$ cd ~/uml
[host]$ tar -xjvf linux-2.4.27.tar.bz2
[host]$ cd ~/uml/linux
[host]$ bzcat uml-patch-2.4.27.bz2 | patch -p1
[host]$ make menuconfig ARCH=um
Uwaga: to działa tylko na systemach z podziałem przestrzeni adresowej 3G/1G; dla tych skonfigurowanych na 2G/2G trzeba pobrać specjalną łatkę.[host]$ make linux ARCH=um
W wyniku powstanie plik linux w katalogu, w którym były źródła. UML jest już gotowy do uruchomienia.Uwaga: rozmiar jądra, które powstało może być niepokojąco duży. To cecha charakterystyczna UML-a i modułów dla UML-a, że powstające binaria zajmują więcej miejsca niż analogiczne binaria hosta. W rzeczywistości są to w dużej części informacje debugowe, a faktyczna wielkość kodu wykonywalnego jest normalna. Rezczywisty rozmiar można zobaczyć robiąc na hoście strip linux
Uwaga: nie budować UML-a w /usr/source/linux ze względu na dowiązania symboliczne istniejące w katalogu /usr/include
Uwaga: bootowanie na większości systemów plików wymaga devfs (wirtualnego systemu plików) wkompilowanego do jądra i montowanego w czasie bootowania. Wyjątkiem jest system plików tomsrtbt. Można też zmodyfikować system plików, aby nie wymagał devfs. Aby to osiągnąć, należy:
for i in 0 1 2 3 4 5 6 7; do mknod ubd$i b 98 $[ $i * 16 ]; done
Uwaga: ubd0 jest domyślnym urządzeniem filesystemu UML-a. ubd reprezentuje skonfigurowane urządzenia blokowe. Jest to oznaczenie przyjęte w UML-u dla hd, fd, cdrom, scd, itd. Urządzenia ubd są bardzo elastyczne. Mogą być mapowane na partycje, katalogi, volume managers, jak również fizyczne urządzenia. Urządzenia mogą być ładowane i usuwane z uruchomionego UML-a za pomocą konsoli zarządzania (management console, mconsole).
Ta procedura wygląda analogicznie jak ze zwykłym jądrem, z dokładnością do tego, że trzeba, jak zwykle, dopisać do polecenia "ARCH=um":
[host]$ make modules ARCH=um
Moduły, jakich ma używać uml-kernel muszą być zbudowane w tym samym katalogu, gdzie kompilowaliśmy kernel ("user-mode pool"); moduły z macierzystego jądra nie działają dla UML-a.
Uwaga: znowu binaria są większe niż można by przypuszczać, ale wiadomo, o co chodzi.
Istnieje kilka sposobów, aby zainstalować moduły:
Gdy UML jest wyłączony (jest to konieczne, aby nie modyfikować jego systemu plików w czasie, gdy on to robi), montujemy jego system plików na hoście np. w /mnt/root_fs z opcją -o loop:
[host]$ mount /opt/uml/root_fs /mnt/root_fs -o loop
i w katalogu, w którym budowaliśmy moduły uruchamiamy
[host]$ make modules_install INSTALL_MOD_PATH=/mnt/root_fs ARCH=um
Następnie odmontowujemy fs
[host]$ umount /mnt/root_fs
[uml]$ mount -t hostfs none jakis_katalog -o sciezka_do_katalogu_ze_zrodlami
// wejść do zamontowanego katalogu
[uml]$ cd jakis_katalog
[uml]$ make modules_install // zainstalować moduły - bez dopisku ARCH=um!
Funkcje obsługujące ładowanie modułów są standardowe: depmod może zgłaszać problemy, ale dośwadczenie wskazuje, że nie należy się tym przejmować, ponieważ insmod, modprobe działają poprawnie.
Standardowym problemem przy kompilowaniu własnych modułów dla UML-a na hoście jest dobór właściwych
flag kompilatora (CFLAGS). Gdy są one niepoprawne, moduł może nie działać, nawet jeśli się skompiluje.
Bezpieczne jest ustawienie dokładnie tych CFLAGS, których są użyte w Makefile do budowania jądra. Aby je
poznać, można do tego użyć tego właśnie pliku Makefile i jego reguły script:
Po wejściu do katalogu źródeł UML-a, następujące polecenie powinno wypisać CFLAGS:
make script 'SCRIPT=@echo $(CFLAGS)' ARCH=um
We własnym skrypcie można zdefiniować zmimenną CFLAGS następującym poleceniem:
CFLAGS=`cd katalog_ze_źródłami; make script 'SCRIPT=@echo $(CFLAGS)' ARCH=um
We własnym Makefile można zdefiniować zmienną CFLAGS następująco:
CFLAGS=$(shell cd katalog_ze_źródłami; make script 'SCRIPT=@echo $$(CFLAGS)' ARCH=um)
Realizacja niektórych funkcji jądra jest wspomagana przez programy przestrzeni użytkownika. Odpowiednie programy znajdują się w pakiecie uml_utilities, rozprowadzanym niezależnie od łatki na jądro. Istnieje jednak ścisła zależność między wersją łatki a wersją pakietu.
Kompilacja pakietu uml_utilities (w katalogu ze źródłami na hoście)
[host]$ make && make install
Przykładowe narzędzia
User-Mode Linux uruchamia się poleceniem linux. Jeśli nie podamy parametru ubd0, UML będzie próbował zamontować plik o nazwie root_fs z bieżącego katalogu jako swój root filesystem. Jeśli chcemy wskazać inny filesystem, uruchamiamy:
[uml]$ linux ubd0=nasz_filesystem
UML uruchamia się i prosi o zalogowanie. UML-owe systemy plików początkowo zawierają 2 konta: root z hasłem root i user z hasłem user. Podajemy wybrany login i hasło i już jesteśmy zalogowani na wirtualną maszynę.
Możliwe jest podłączenie UML-a do wielu typów kanałów wejścia/ wyjścia komputera bazowego. Umożliwia to między innymi:
Ogólny format - w linii poleceń dajemy jedną lub więcej opcji postaci: urządzenie=kanał Jeżeli chcemy osobno okreslić kanał wejściowy i wyjściowy, należy użyć opcji: urządzenie=kanał_wejściowy,kanał_wyjściowy |
Dostępne urządzenia są dwa: konsola - con oraz łącze szeregowe - ssl. Użycie jako nazwy urządzenia po prostu "con" albo "ssl" oznacza przypisanie kanału wszystkim urządzeniom. Jeżeli chcemy podłączyć kanał do konkrentego urządzenia, po nazwie urządzenia dodajemy numer, np. "con5", "ssl9". Ustawienia dla konkretnych urządzeń przesłaniają ustawienia ogólne.
urządzenie=pts lub urządzenie=pty
UML przydzieli urządzeniu wolny pseudo-terminal na komputerze bazowym. Jaki konkretnie terminal został przydzielony można sprawdzić w logach startowych sytemu (np. poleceniem dmesg). Następnie z komputera bazowego można uzyskać dostęp do przydzielonego w ten sposób terminala poleceniem screen (lub: minicom, kermit). Np. screen /dev/pts/x; screen /dev/ttyxx.
urządzenie=tty:urządzenie_tty
Podłączy urządzenie do niewykorzystywanego terminala komputera bazowego, urządzenie_tty to np. /dev/tty5.
urządzenie=xterm
Spowoduje utworzenie nowego xterm-a i przyłączenie do niego podanego urządzenia.
urządzenie=fd:a,fd:b
Podłączenie wejścia i wyjścia urządzenia do istniejących (otwartych) deskryptorów a i b. Np. polecenie con0=fd:0,fd:1 spowoduje podłączenie głównej konsoli do konsoli, z której został UML uruchomiony. Można też podać na przykład wcześniej przygotowane deskryptory.
urządzenie=port:numer
Spowoduje podłączenie danego urządzenia do podanego portu na komputerze bazowym. Dostęp do UML-a można wtedy uzyskać poprzez telnet. Podanie jako urządzenia ssl spowoduje, że każdy klient dostanie jedno łącze szeregowe, aż do ich wyczerpania (nowi klienci będą usypiani). Umożliwia to zalogowanie się do UML-a bez znajomości konkretnych urządzeń, do jakich zostały podłączone kanały - jak to było w przypadku pseudo-terminali. W ten sposób też można udostępnić logowanie do UML-a przez sieć, bez konieczności konfigurowania obsługi sieci w maszynie wirtualnej.
urządzenie=none
Urządzenie nie jest widoczne w systemie, tzn. nie można go otworzyć.
urządzenie=null
Urządzenie jest widoczne, można je otworzyć, ale próba odczytu spowoduje zablokuje proces, a wszystkie zapisy idą w "czarną dziurę".
Np., aby pozbyć się dwóch dodatkowych konsol otwierających się podczas startu systemu, należy dodać do linii poleceń opcje:
con=pty con0=fd:0,fd:1
Na początku musimy utworzyć plik, w którym będziemy przechowywać nowy system plików. Robimy to korzystając z polecenia dd, którym tworzymy plik rzadki (tzn. plik który fizyczne zajmuje mniej miejsca niż wynosi jego rozmiar):
[host]$ dd if=/dev/zero of=nowy_fs seek=100M count=1 bs=1
W ten sposób plik "nowy_fs" będzie miał wielkość 100MB, ale na dysku zajmie dużo mniej (można to sprawdzić poleceniem ls -ls - plik zajmuje 12 bloków).
Następnie uruchamiamy UML-a, dodając w linii poleceń opcję:
ubdx=nowy_fs
gdzie x jest numerem wolnego urządzenia blokowego.
Można utworzyć tylko system plików, którego obsługa jest wbudowana w jądro lub załadowana jako
moduł jądra. Aby utworzyć system plików wydajemy polecenie:
[uml]$ mke2fs /dev/ubd/x
gdzie x jest poprzednio wybranym numerem. Polecenie to stworzy system plików ext2, ale można też
tworzyć inne, poleceniami: mkreiserfs, mkdosfs itd.. Jakie systemy plików możemy stworzyć zależy od tego,
jakie systemy plików
obsługuje jądro (ich lista znajduje się w /proc/filesystems) oraz od zainstalowanych narzędzi
w systemie. W przypadku ich braku, możemy ściągnąć je z sieci lub bazowego komputera.
Na koniec trzeba zamontować system plików:
[uml]$ mount /dev/ubd/x /mnt/nowy_fs_katalog
Często systemy plików ściagnięte z sieci jako nie mają prawie wolnego miejsca, przez co praca z nimi jest bardzo utrudniona. Jednak w łatwy sposób można istniejący system plików powiększyć. W tym celu trzeba wyjść z UML-a i wydać polecenia (dla systemu plików ext2):
[host]$ e2fsck -f nazwa_pliku
[host]$ dd if=/dev/zero of=nazwa_pliku bs=1 count=1 seek=nowy_rozmiar conv=notrunc
[host]$ resize2fs -p nazwa_pliku
[host]$ e2fsck -f nazwa_pliku
Po kolei: sprawdzamy system plików, zmieniamy rozmiar pliku (jako nowy_rozmiar możemy podać np. 100M lub 2GB), zmieniamy rozmiar systemu plików i na koniec dla pewności ponownie sprawdzamy czy cała operacja przebiegła pomyślnie.
Często zachodzi potrzeba skopiowania jakichś plików z komputera na którym jest uruchomiony UML do systemu plików UML-a. Można to zrobić na parę sposobów: zamontować system plików hosta przez nfs, wyłączyć UML-a, zamontować jego system plików na komputerze bazowym, poleceniem:
[host]$ mount system_plików katalog -o loop
albo skorzystać z wbudowanego w UML-a systemu plików hostfs. Aby z niego skorzystać, system plików musi być dostępny w systemie (wkompilowany w jądro albo załadowany jako moduł). Najprostszym sposobem na jego wykorzystanie jest wydanie polecenia:
[uml]$ mount none /mnt/host -t hostfs
W ten sposób przez katalog /mnt/host będziemy mieli dostęp do całego systemu plików hosta. Dodanie
opcji -o katalog spowoduje zamontowanie tylko podanego katalogu (np. -o /home).
Możliwe jest też uruchomienie UML-a z katalogu na komputerze bazowym, zamiast z zawartego w pliku
systemu plików. W tym celu najpierw musimy mieć odpowiedni katalog (jeżeli mamy system plików w pliku,
możemy to zrobić poleceniem mount system_plików katalog -o loop). Następnie w pliku etc/fstab
zamontowanego systemu plików zmieniamy rodzaj systemu plików dla katalogu głównego / na:
none / hostfs defaults 1 1
Po czym zmienimy właściciela wszystkich plików, których właścicielem był root na nas:
[host]$ find . -uid 0 -exec chown użytkownik {} \
Na koniec uruchamiamy UML-a z następującymi opcjami:
root=/dev/root rootflags=/ścieżka/do/katalogu/z/systemem/plików rootfstype=hostfs
Można też ograniczyć działanie hostfs-a przez podanie opcji w linii poleceń:
hostfs=katalog,[append]
gdzie katalog jest nazwą katalogu, który będzie można standardowo zamontować (przy podaniu np. /home niemożliwe jest uzystkanie dostępu do katalogu /etc; katalog ten można pominąć, jeżeli nie chcemy ograniczać dostępu). Opcja append oznacza, że nie będziemy mogli kasować plików z komputera bazowego (otwarcie pliku do zapisu spowoduje otwarcie go w trybie dopisywania).
Mogłoby się wydawać, że gdy chcemy naraz uruchomić więcej niż jednego UML-a, to dla każdego musimy mieć osobny plik z głównym systemem plików (root_fs). (Próba uruchomienia dwóch UML-i z tym samym plikiem jak root_fs skończy się uszkodzeniem systemu plików!) Jednak takie rozwiązanie jest bardzo pamięciożerne, gdyż systemy plików zajmują na ogół dużo miejsca. Dlatego UML oferuje mechanizm dostępu do systemów plików COW (copy-on-write, kopiowanie przy zapisie). Każda kopia UML-a posiada jedynie własny plik COW, w którym zapisywane są zmiany w stosunku do oryginalnego systemu plików. Uruchomienie UML-a z systemem plików COW jest bardzo proste, w linii poleceń przy uruchamianiu UML-a dodajemy opcję: ubd0=nazwa_pliku_cow,nazwa_root_fs |
Spowoduje to utworzenie pliku nazwa_pliku_cow, jeżeli jeszcze nie istniał, albo skorzystanie z już istniejącego. Gdy plik COW już istnieje, możemy uruchamiać UML-a z opcją:
ubd0=nazwa_pliku_cow
Rozmiar pliku COW będzie dużo większy niż faktyczna ilość zajętego na dysku miejsca. Dlatego aby
go sprawdzić należy wydać polecenie ls -ls. Gdy raz skorzystamy z pliku nazwa_root_fs jako pliku bazowego
dla pliku COW, nie należy go modyfikować, czyli uruchamiać UML-a bezpośrednio z niego. Spowoduje to, że
wszystkie pliki COW powiązane z tym systemem plików najprawdopodobniej przestaną działać.
Możliwe jest też scalenie pliku COW z systemem plików. W tym celu wydajemy polecenie:
[host]$ uml_moo nazwa_pliku_COW nowy_root_fs
Spowoduje to utworzenie nowego pliku z systemem plików, który będzie połączonym oryginalnym systemem plików z zmianami zawartymi w pliku COW. Można też dokonać scalenia bez tworzenia nowego pliku:
[host]$ uml_moo -d nazwa_pliku_COW
Jednak spowoduje to że inne pliki COW powiązane z oryginalnym systemem plików przestaną działać.
Aby utworzyć nowy plik COW bez uruchamiana UML-a, należy wydać polecenie:
[host]$ uml_mkcow [-f] plik_COW plik_root_fs
Opcja -f powoduje nadpisanie starego pliku COW. Narzędzie uml_moo i uml_mkcow są częścią pakietu UML Utilites.
Do tworzenia nowych bazowych systemów plików (root_fs) zostało stworzonych kilka narzędzi:
Więcej informacji o tych i innych narzędziach można znaleźć na stronie UML-a.
Konsolę zarządzania możemy pobrać z repozytorium CVS projektu UML: (dostepna w postaci pliku źródłowego), nastepnie kompilujemy ją dostarczonym makefile`m. Przy uruchamianiu UML-a wystarczy teraz tylko dopisać CONFIG_MCONSOLE, spowoduje to utworzenie w katalogu .uml w podkatalogu z nazwą maszyny wirtualnej instancji UML-a gniazda umożliwiającego komunikację konsoli zarządzania z UML-em. Nazwa katalogu maszyny wirtualnej jest dość losowa więc dla wygody uruchamiania konsoli możemy w linii poleceń przy startowaniu UML-a wpisać umid=nazwa
[root@localhost linux-2.4.26]$ ./linux ubd0=../root_fs_toms1.7.205 CONFIG_MCONSOLE umid=nazwa
Podczas uruchamiania UML-a powinniśmy zobaczyć coś podobnego do:
mconsole (version 2) initialized on /root/.uml/nazwa/mconsole
Teraz możemy uruchomić uml_mconsole w następujący sposób:
uml_mconsole nazwa
Pojawi się linia poleceń w której możemy wpisać następujące komendy1. version
Bezargumentowa, drukuje wersję UML-a:OK Linux tomsrtbt 2.4.26 #1 nie lis 28 03:09:16 CEST 2004 i686
Można użyć tej komendy np. w celu sprawdzenia czy UML pracuje.2. halt i reboot
Bezargumentowe, powoduje natychmiastowe zamknięcie systemu(UML-a).3. config
Dodaje do maszyny wirtualnej nowe urządzenie lub wyświetla informacje o istniejącym urządzeniu np:config ubd0
spowoduje wyświetlenie informacji o urządzeniu blokowym ubd0OK ../root_fs_toms1.7.205
Natomiastconfig ubd3=/root/user-mode/root_fs_slack8.1
dodanie filesystemu jako ubd3.4. remove
Usunięcie wybranego urządzenia z systemu np:remove ubd3
Urządzenie wybrane do usunięcia nie może być używane przez system5. help
Wyświetla spis możliwych poleceń i opis ich czynności.6. sysrq
Jest interfejsem do przekazywania jądru komend sysrq(normalnie są wywoływane wciśnieciem ALT+Sys Rq+sysrq znak_z_komenda
Przykładowo polecenie konsoli zarządzaniasysrq i
spowoduje wysłanie wszystkim procesom(poza initem) działającym na maszynie wirtualnej sygnału SIGKILL.7. cad
Wykonuje na procesie init czynność powiązaną z kombinacja klawiszy ALT+CTRL+DELETE (normalnie powoduje restart systemu)8. stop
Po wykonaniu stop UML wejdzie w pętle, w której może wykonywać wyłącznie polecenia wydawane z konsoli zarządzania aż do wydania polecenia go. Takie zatrzymanie systemu przydaje się przy tworzeniu backup`ów systemów plików UML`a. Możemy mianowicie zatrzymać system a następnie poleceniem konsoli sysrq -s zapisać wszystkie dane z buforów dyskowych na dysk. Teraz można zrobić kopię systemu plików i "uwolnić" system poleceniem go. Jeśli UML działa na architekturze wieloprocesorowej polecenie stop spowoduje zapętlenie tylko na jednym procesorze, ten bład, według twórców UML-a, ma zostać wkrótce naprawiony.9. go
Używane w kontekście wspomnianym wyżej, powoduje wyjście UML-a z pętli.10. log
W argumencie podajemy napis, na którym kernel wykona funkcję printk-czyli napis zostanie zapisany w message log-u.12. proc
Jako argument podajemy nazwę pliku z katalogu proc w UML-u np.proc version
Spowoduje to wypisanie na konsoli zarządzania zawartości pliku version.Konfiguracja TUN/TAP Przed rozpoczęciem konfiguracji musimy zainstalować uml_net (część pakietu uml_tools, do ściagnięcia ze strony projektu). W wierszu poleceń przy uruchamianiu UML-a wpisujemy:
[root@localhost linux-2.4.26]$ ./linux ubd0=../root_fs_toms1.7.205 eth0=tuntap,,,10.1.1.110
A nastepnie po zalogowaniu się(z uprawnieniami roota w UML-u):ifconfig eth0 10.1.1.1 up
Teraz dla UML-a host jest widoczny jako 10.1.1.110 a UML dla hosta jako 10.1.1.1. Jeśli nasza wirtualna maszyna UML ma widzieć elementy sieci hosta to musimy uruchomić routowanie na maszynie wirtualnej:Konfiguracja ethertap przebiega bardzo podobnie, z tym że nie trzeba używać do niej uml_net-a, można go zastąpić /dev/tap0. Dla jądra 2.4 nie zaleca się jej stosowania (na stronie projektu UML pisze obsolete).
[root@localhost linux-2.4.26]$ ./linux ubd0=../root_fs_toms1.7.205 eth0=ethertap,tap0,fe:fd:0:0:0:1,10.1.1.129
Konfiguracja multicast W linii poleceń podczas uruchamiania UML-a dopisujemy:
[root@localhost linux-2.4.26]$ ./linux ubd0=../root_fs_toms1.7.205 eth0=mcast
Przy założeniu, że host ma interfejs eth0 Teraz wpisujemy juz w linii poleceń UML-a:ifconfig eth0 10.1.1.1 up
Teraz możemy na hoście uruchomić kolejne UML-e i ta wirtualna maszyna będzie dla nich widoczna pod numerem 10.1.1.1export DISPLAY=host-ip:0
Teraz możemy uruchomić X-y.Ciekawe zastosowanie sieciowych mozliwości maszyn wirtualnych UML-a znajduje się w Virtual Network Laboratory - Christchurch Polytechnic Institute of Technology (CPIT) w Nowej Zelandii. W obawie o wpływ eksperymentów przeprowadzanych przez studentów na siec akademicką, a mając na uwadze duże koszty utworzenia odrębnej sieci do eksperymentów postanowiono wykorzystać UML-a tworząc sieć maszyn wirtualnych przedstawioną na poniższym rysunku. Całe laboratorium składa się z 20 wirtualnych maszyn umieszczonych na jednym hoście. Wirtualne maszyny są za pomocą jednego wirtualnego routera połączone z hostem i dalej do sieci akademickiej. Konstrukcja jest bardzo dokładnie opisana na stronie: "http://user-mode-linux.sourceforge.net/cpit.html"
W normalnym trybie, UML działa w następujący sposób:
Takie rozwiązanie ma wady: ponieważ jądro jest częścią przestrzeni adresowej każdego procesu, proces może do niego pisać. W ten sposób proces mógłby dostać się do komputera bazowego. Można też wykryć czy proces działa na UML-u, sprawdzając, czy można czytać kod jądra - jest to niepożądana m.in. w "honeypotach". Również wydajność jest niższa, ponieważ do komunikacji z jądrem UML-a tt używa sygnałów.
Rozwiązaniem tych i innych problemów jest łatka SKAS - Single Kernel Address Space. Łatka ta nakładana jest na jądro linuxa komputera bazowego (!) i umożliwia procesom przełączanie się między różnymi przestrzeniami adresowymi.
W ten sposób jądro UML-a posiada własną przestrzeń adresową i jest jest całkowicie odizolowane od procesów. Zamiast jednego procesu na komputerze bazowym dla każdego procesu UML-a, tworzone są tylko 4 procesy (jądra, procesów, sterownik ubd, emulacja pisania SIGIO), co znacznie przyśpiesza działanie. Nie jest też używana powolna komunikacja sygnałami.
Aby zainstalować SKAS, należy: ściągnąć odpowiednią łatkę i skompilować z nią jądro; w opcjach UML-a włączyć CONFIG_MODE_SKAS. Wzrost wydajności jest znaczny (nawet pięciokrotny).
Debugowanie jądra w tracing thread mode jest nieco bardziej skomplikowane, naprościej je ropocząć uruchamiając UML-a od razu pod kontrolą gdb(z opcją debug) np:
[root@localhost linux-2.4.26]$ ./linux ubd0=../root_fs_slack8.1 debug
W efekcie pojawi sie xterm z uruchomionym gdb a bootowanie UML-a zostanie zatrzymane(zatrzyma sie proces tt) na poczatku funkcji start_kernel(). Od tej chwili możemy juz wydawać polecenia debugera.Jeśli chcemy podłączyć gdb do już działającego jądra, możemy skorzystać z konsoli zarządzania wpisując w jej linii poleceń:
config gdb=xterm
Efektem będzie uruchomienie gdb w nowym xtermie.Jeszcze inny sposób(ogólniejszy bo możemy tak podłączyć do UML-a inne programy zawierające podproces gdb jak np. ddd i jedyny działający na moim komputerze(nie wyskakuje żaden a u xterm zwracał błąd)) Uruchamiamy UML z opcją
debug gdb-pid=
att 1
a następnie symbol-file ze ścieżką do UML-a(ładuje symbole jądra do gdb)symbol-file /root/uml/linux-2.4.26/linux
(gdb) b start_kernel
Breakpoint 1 at 0xa00023f7: file init/main.c, line 362.
(gdb) c
Continuing.
Breakpoint 1, start_kernel () at init/main.c:362
362 printk(linux_banner);
(gdb) b kernel_thread
Breakpoint 2 at 0xa000f46b: file fork.c, line 606.
(gdb) c
Continuing.
Breakpoint 2, kernel_thread (fn=0xa000b5d0
at fork.c:606
606 struct task_struct *task = current;
run /root/user-mode/2.4.26/linux ubd0=/root/user-mode/root_fs_slack8.1
Powinniśmy jeszcze tylko nakazać gdb ignorowanie sygnałów SIGSEGV i SUGUSR1 ponieważ są one uzywane wewnętrznie przez UML i powodowałyby ciągłe przerywanie debugowania.(gdb) handle SIGSEGV pass nostop noprint
(gdb) handle SIGUSR1 pass nostop noprint
Gdb wspiera debugowanie kodu który jest ładowany dynamicznie, umożliwia zatem w szczególności debugowanie modułów jądra UML. Proces ten jest jednak dosyć skomplikowany, po pierwsze musimy wskazać gdb który plik obiektowy ( *.o) został właśnie załadowany i gdzie się on znajduje w pamięci(założenie że moduł został skompilowany z opcją -g)
add-symbol-file modul.o adres_w_pamieci
Parametr adres_w pamieci jest domyślnie równy 0. Dzięki niemu gdb może zinterpretować adresy symboli zawartych w pliku modul.o. Parametr adres_w_pamieci można zdobyć z ze struktury module_list(lista załadowanych modułów) czynnośc ta jest skomplikowana i jej opis znajduje się: Wygodniejszym rozwiązaniem jest użycie gotowego skryptu umlgdb(autorstwa Chandana Kudige, do sciągnięcia z części utilities portalu sourceforge ) automatyzującego cały proces ładowania modułów. Skrypt trzeba najpierw przeglądnąć i ewentualnie pozmieniać zmienne w skrypcie tak żeby pasowały do naszych ustawień choć teoretycznie musimy jedynie poustawiać scieżki w poniższej liście tak żeby wskazywały na nasze moduły:set MODULE_PATHS { fat /usr/src/uml/linux-2.4.18/fs/fat/fat.o
isofs /usr/src/uml/linux-2.4.18/fs/isofs/isofs.o
minix /usr/src/uml/linux-2.4.18/fs/minix/minix.o }
20:18 09/12/2004