0 Linux – moduły jądra, implementacja prostego sterownika
Tomasz Zaworski edited this page 2020-11-15 02:28:24 +01:00
This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

  1. Przygotowanie środowiska pracy
  2. Moduły jądra informacje ogólne
  3. Zarządzanie modułami
  4. Skrócona lista komend
  5. Lista załadowanych modułów
  6. Załadowanie modułu
  7. Usunięcie załadowanego modułu
  8. Przykład 1 - Tworzenie prostego sterownika
  9. Dodanie parametrów do modułu
  10. Obsługa urządzeń znakowych
  11. Przykład 2 - urządzenie znakowe
  12. Opis kodu
  13. Zadania
  14. Zadania do wykonania na zajęciach
  15. Zadanie domowe
  16. Dodatkowa lektura

Przygotowanie środowiska pracy

Uwaga: Systemy Linux przygotowane na potrzeby laboratoriów z przedmiotu Systemy Operacyjne nie są w rzeczywistości całkowicie odseparowanymi maszynami. Mają one współdzielone jądro i nie ma możliwości ładowania własnych modułów do jądra. Z tego powodu wymagane jest przy wykonywaniu ćwiczeń pobranie odpowiednich obrazów systemu i uruchomienie wirtualnej maszyny za pomocą np. qemu.

Pobranie obrazu na dysk lokalny. W tym celu proszę wykonać następujące komendy:

mkdir -p /tmp/sop_images && cp /media/$(whoami)/resources/Wawrzyniak\ Wojciech/debian_sop.img /tmp/sop_images

Uruchomienie obrazu:

cd /tmp/sop_images
qemu-system-x86_64 -hda debian_sop.img -redir tcp:2222::22  -m 1024M -smp 1

Teraz można już się podłączyć do maszyny z innej konsoli poprzez ssh (lokalnie z linuksa na którym została właśnie uruchomiona wirtualna maszyna z obrazem systemu Linux):

 ssh -p 2222 sop@localhost

Dane do logowania:

  • login: sop
  • hasło: sop2017

lub

  • login: root
  • hasło: sop2017

Moduły jądra informacje ogólne

Przez dziesięciolecia sterowniki urządzeń systemu UNIX były statycznie łączone z jądrem, dzięk czemu były dostępne W pamięci po każdym uruchomieniu systemu. W środowisku, w którym rozwijały się ówczesne wersje Uniksa, kiedy minikomputery i wydajne stacje robocze zawierały stosunkowo niewielkie i stale zbiory urządzeń wejścia-wyjścia, wspomniany model sprawdzał się całkiem dobrze. Wystarczyło skompilować jądro ze sterownikami dla odpowiednich urządzeń wejścia-wyjścia. Jeśli rok później dana organizacja decydowała się na zakup nowego dysku, można było po prostu raz jeszcze skompilować jądro. Dla nikogo nie był to poważny problem.

Po wprowadzeniu systemu Linux dla platformy PC nagle wszystko uległo zmianie. Liczba urządzeń wejścia-wyjścia dostępnych dla komputerów PC o kilka rzędów wielkości przekraczała liczbę tego rodzaju urządzeń instalowanych w minikomputerach. Co więcej, mimo że większość użytkowników Linuksa dysponuje (lub może łatwo uzyskać) kompletny kod źródłowy tego systemu, prawdopodobnie dla niemal wszystkich operacja dodania nowego sterownika, aktualizacji wszystkich struktur danych opisujących sterowniki urządzeń, ponowna kompilacja jądra i instalacja go w formie uruchamialnego systemu (nie wspominając o rozwiązywaniu problemów związanych z usuwaniem usterek w razie problemów z rozruchem jądra) byłaby kłopotliwa.

W systemie Linux rozwiązano ten problem — wprowadzono tzw. ładowalne moduły (ang. loadable modules). To fragmenty kodu, które można ładować do jądra systemu operacyjnego już w czasie jego działania. Do najbardziej popularnych modułów tego typu należą sterowniki urządzeń znakowych i blokowych, jednak równie dobrze można instalować w ten sposób całe systemy plików, protokoły sieciowe, narzędzia monitorujące wydajność i dowolne inne mechanizmy.

W trakcie ładowania modułu należy wykonać szereg operacji. Po pierwsze moduł musi zostać przeniesiony do pamięci W trakcie ładowania. Po drugie system musi sprawdzić, czy zasoby wymagane przez dany sterownik (np. poziomy żądań przerwań) są dostępne, i — jeśli tak — oznaczyć je jako wykorzystywane. Po trzecie należy ustawić wszelkie niezbędne wektory przerwań. Po czwarte systemu musi zaktualizować tablicę sterowników, aby obsługiwała nowy typ urządzeń. Dopiero po wykonaniu tych kroków sterownik może przystąpić do inicjalizacji swojego urządzenia. Sterownik zostaje wówczas w pełni zainstalowany i dysponuje takimi samymi prawami jak sterowniki instalowane statycznie. Ładowalne moduły są obecnie obsługiwane także przez inne współczesne systemy UNIX.

Tanenbaum, A., & Bos, H. (2016). Moduły w systemie Linux. W Systemy operacyjne (pp. 776-777, wyd. 4). Helion.

W systemie Linux sterowniki urządzeń mogą być umieszczone:

  • wewnątrz jądra (np. te które są niezbędne do uruchomienia)
  • wydzielone w postaci osobnych modułów.

Zauważmy, że z uwagi na dużą liczbę dostępnych urządzeń wkompilowywanie wielu sterowników w jądro systemu Linux byłoby bardzo kłopotliwe. Dlatego wprowadzono tzw. ładowalne moduły (ang. loadable modules). Są to fragmenty kodu, które można załadować do jądra systemu już w trakcie jego działania.

Do najpopularniejszych typów takich modułów należą:

  • sterowniki urządzeń znakowych
  • sterowniki urządzeń blokowych (np. dyski).

Jednak w ten sposób mogą być tworzone również całe systemy plików, protokoły sieciowe, i wiele wiele innych.

Zarządzanie modułami

Skrócona lista komend

  • lsmod - wylistowanie zaladowanych modułów jądra
  • modprobe - pozwala dodać i usunąć moduły jądra z katalogu /lib/modules/$(uname -r). Dodatkowo pozwala załadować wszystkie moduły potrzebne do działania naszego modułu
  • insmod <nazwa_plik.ko> - ładuje moduł jądra o nazwie podanej jako parametr (ładowanie możliwe z lokalnego katalogu)
  • rmmod <nazwa modulu> - usuwa moduł jądra.

Lista załadowanych modułów

Większość "sterowników" urządzeń jest albo wkompilowana w jądro systemu (choćby te, które są niezbędne do uruchomienia), albo jako moduły ładowane dynamicznie, gdy dane urządzenie zostanie wykryte. Aby zobaczyć jakie moduły zostały załadowane wystarczy użyć polecenia lsmod (większość linii została wycięta, lista jest zwykle dłuższa).

$ lsmod
Module                  Size  Used by
xt_mac                 16384  1
xt_tcpudp              16384  1
dccp_diag              16384  0
snd_pcm               106496  0
snd_timer              32768  1 snd_pcm
snd                    86016  2 snd_timer,snd_pcm
soundcore              16384  1 snd

Z powyższego możemy odczytać: nazwę modułu, zajmowaną przez niego pamięć oraz jakie inne moduły są od niego zależne (np. snd jest używany przez snd_timer).

Załadowanie modułu

Aby załadować moduł wraz z zależnościami możemy wykorzystać polecenie modprobe nazwa_modułu. Jednak ważne by moduł był zainstalowany w odpowiednim katalogu (w naszym przypadku w /lib/modules/4.9.0-4-amd64). Przykładowa komenda ładująca moduł jądra.

$ modprobe arc4

Moduł jądra można również załadować za pmocą polecenia insmod. Np.:

insmod hello.ko

Jednak w tym przypadku plik hello.ko może znajdować się w bieżącym katalogu

Usunięcie załadowanego modułu

$ rmmod nazwa_modułu.

Usunięcie nie powiedzie się, jeżeli moduł jest używany.

Przykład 1 - Tworzenie prostego sterownika

W celu utworzenia prostego sterownika należy przygotować dwa pliki: Makefile oraz plik źródłowy. Ponadto ważne jest by w systemie był zainstalowany pakiet linux-headers-$(uname -r). Gdzie polecenie uname -r pozwala pobrać aktualny numer wersji jądra systemu. Ważne jest by posiadane pliki nagłówkowe były zgodne co do wersji aktualnym jądrem systemu.

Makefile:

obj-m += hello.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

plik hello.c:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

MODULE_AUTHOR("Systemy operacyjne 2017 UAM");
MODULE_DESCRIPTION("Prosty modul Hello World From Module");

static int __init hello_init(void)
{
    printk(KERN_INFO "Hello world z modulu Hello!\n");
    return 0;    // Wartosc rozna od 0 zwraca blad
}

static void __exit hello_cleanup(void)
{
    printk(KERN_INFO "Cleanup z moduly Hello!.\n");
}

module_init(hello_init);
module_exit(hello_cleanup);

Opis kodu:

  • Załączone pliki nagłówkowe linux/module.h, linux/kernel.h, linux/init.h są konieczne podczas kompiliacji każdego z modułów jądra
  • Funkcja hello_init zostanie wywołana podczas dogrywania modułu do jądra (np. poleceniem modproble hello). Zwraca ona wartość typu int i powinna być ona 0 jeżeli moduł został załadowany poprawnie.
  • Funkcja hello_cleanup zostanie wywołana podczas usuwania modułu z jądra. Zadaniem jej jest "posprzątanie" po tym co wcześniej zostało wykonane w module np. w funkcji "hello_init".
  • Linie module_init(hello_init) i analogiczna module_exit(hello_cleanup) służą do powiadomienia systemu od których funkcji powinno rozpocząć/zakończyć się działanie modułu.

Stałe uprawnień dla plików:

  • S_IRWXU - read, write, execute/search by owner
  • S_IRUSR - read permission, owner
  • S_IWUSR - write permission, owner
  • S_IXUSR - execute/search permission, owner
  • S_IRWXG - read, write, execute/search by group
  • S_IRGRP - read permission, group
  • S_IWGRP - write permission, group
  • S_IXGRP - execute/search permission, group
  • S_IRWXO - read, write, execute/search by others
  • S_IROTH - read permission, others
  • S_IWOTH - write permission, others
  • S_IXOTH - execute/search permission, others
  • S_ISUID - set-user-ID on execution
  • S_ISGID - set-group-ID on execution
  • S_ISVTX - on directories, restricted deletion flag

Dodanie parametrów do modułu

Moduły jądra często muszą być dodatkowo konfigurowane. Istnieje możliwość ustawienia dodatkowych parametrów podczas ładowania modułu (lub też podczas ich działania). Poniżej przedstawiono kod posiadający parametr o nazwie "parameter".

static char* parameter="Parametr dla hello_param module";
module_param(parameter, charp, S_IRUGO);
MODULE_PARM_DESC(parameter, "Napis zapisywany w logach");

Wyjaśnienie:

  • static char* parameter - deklarowane zmienne muszą być globalnymi zmiennymi statycznymi
  • module_param(parameter, charp, S_IRUGO); - każda zmienna musi być przetwarzana za pomocą makra module_param(). Pierwszy argument to nazwa parametru, drugi typ zmiennej, a trzeci oznacza uprawnienia do danego parametru. (S_IRUGO - parametr może być czytany przez wszystkich ale nie może być zmieniany).

Istnieje możliwość wyświetlenia wartości zmiennych w logach systemu używając składni analogicznej jak w poleceniu printf:

printk(KERN_INFO "Hello world z modulu Hello - parametr: %3s!\n", parameter);

Obsługa urządzeń znakowych

Podstawowym zadaniem modułu jest dostarczenie użytkownikowi pewnej funkcjonalności sprzętu. Do tego celu jest wymagana komunikacja użytkownika z modułem. Zapewnić to mogą specjalne pliki zwane plikami urządzeń (ang. device files). Pliki te można znaleźć w katalogu /dev i podzielić je można na dwa typy:

  • urządzenia znakowe (np. karty sieciowe}
  • urządzenia blokowe (np. dyski itp.)

Za pomocą polecenia ls -l /dev można wypisać listę wszystkich urządzeń. Urządzenia znakowe rozpoczynają się od "literki" "c", natomiast urządzenia blokowe od literki "b".

Przykład 2 - urządzenie znakowe

https://github.com/skuhl/sys-prog-examples/blob/master/kernel-module/chardev.c

Opis kodu

To jest dobre miejsce by wykonać zadanie 2 z ćwiczeń. Następnie jak zrozumiesz działanie zaprezentowanego modułu przejdź do zrozumienia kodu.

Inicjalizacja modułu

Podczas inicjalizacji modułu init_module() wywołana zostaje funkcja rejestracji urządzenia.

/* This function is called when the module is loaded */
int init_module(void)
{
        Major = register_chrdev(0, DEVICE_NAME, &fops);

        if (Major < 0) {
          printk(KERN_ALERT "Registering char device failed with %d\n", Major);
          return Major;
        }

        printk(KERN_INFO "I was assigned major number %d. To talk to\n", Major);
        printk(KERN_INFO "the driver, create a dev file with\n");
        printk(KERN_INFO "'mknod /dev/%s c %d 0'.\n", DEVICE_NAME, Major);
        printk(KERN_INFO "Try various minor numbers. Try to cat and echo to\n");
        printk(KERN_INFO "the device file.\n");
        printk(KERN_INFO "Remove the device file and module when done.\n");

        return SUCCESS;
}

Podczas inicjalizacji następuje zarejestrowanie modułu do obsługi odpowiedniego urządzenia. W tym celu wykonywana jest funkcja register_chrdev(0, DEVICE_NAME, &fops);.

Parametrami są kolejno:

  • numer główny - na podstawie numeru głównego system rozróżnia który z modułów powinien obsługiwać operacje dotyczące urządzenia w systemie. W przypadku podania 0 system sam przydziela numer.
  • nazwa urządzenia
  • struktura opisująca jakie operacje na urządzeniu będą obsługiwane. W naszym przypadku moduł obsługuje 4 funkcje:
static struct file_operations fops = {
	.read = device_read,
	.write = device_write,
	.open = device_open,
	.release = device_release
};

Opis wykorzystania funkcji:

  • static int device_open(struct inode *, struct file *); - wywoływana podczas otwarcia urządzenia do odczytu (open)
  • static int device_release(struct inode *, struct file *); - wywoływana podczas zamknięcia pliku (close)
  • static ssize_t device_read(struct file *, char *, size_t, loff_t *); - wywoływana podczas operacji czytania z pliku (read)
  • static ssize_t device_write(struct file *, const char *, size_t, loff_t *); - wywoływana podczas operacji zapisu do pliku (write)
Otwarcie urządzenia
/* 
 * Called when a process tries to open the device file, like
 * "cat /dev/mycharfile"
 */
static int device_open(struct inode *inode, struct file *file)
{
        static int counter = 0;

        if (Device_Open)
                return -EBUSY;

        Device_Open++;
        sprintf(msg, "I already told you %d times Hello world!\n", counter++);
        msg_Ptr = msg;
        try_module_get(THIS_MODULE);

        return SUCCESS;
}

W funkcji tej mamy zadeklarowaną zmienną statyczną counter (przy każdym kolejnym wywołaniu jej wartość nie jest kasowana. I tak przy kolejnych wywołaniach zmienna counter wynosi 1,2,3,4, id.

Zmienna Device_Open przechowuje ile razy urządzenie zostało otwarte (np. metodą open). W naszym przypadku nie ma możliwości by urządzenie zostało otwarte więcej niż jeden raz. Wynika to ze sprawdzenie if (Device_Open) return -EBUSY. Tak więc jeżeli wartość zmiennej jest większa od 0 to funkcja zwraca błąd (wartość -EBUSY).

Następnie generowany jest odpowiedni napis I already ..... %d times ...) i umieszczany w pamięci wskazywanej przez zmienną msg.

Ponadto występuje tutaj wywołanie funkcji: try_module_get(THIS_MODULE) - pozwala ona systemowi Linux zliczać liczbę aktywnie podłączonych procesów do modułu. Zapobiega to możliwości usunięcia używanego aktualnie modułu z jądra.

Zamknięcie urządzenia
/* 
 * Called when a process closes the device file.
 */
static int device_release(struct inode *inode, struct file *file)
{
        Device_Open--;          /* We're now ready for our next caller */

        /* 
         * Decrement the usage count, or else once you opened the file, you'll
         * never get get rid of the module. 
         */
        module_put(THIS_MODULE);

        return 0;
}

W funkcji tej zmniejszamy o jeden liczbę otwartych urządzeń Device_Open--. Ponadto funkcja put_module(THIS_MODULE) zmniejsza liczbę aktywnie podłączonych procesów do modułu przez system Linux.

Odczyt z urządzenia
/* 
 * Called when a process, which already opened the dev file, attempts to
 * read from it.
 */
static ssize_t device_read(struct file *filp,   /* see include/linux/fs.h   */
                           char *buffer,        /* buffer to fill with data */
                           size_t length,       /* length of the buffer     */
                           loff_t * offset)
{
        /*
         * Number of bytes actually written to the buffer 
         */
        int bytes_read = 0;

        /*
         * If we're at the end of the message, 
         * return 0 signifying end of file 
         */
        if (*msg_Ptr == 0)
                return 0;

        /* 
         * Actually put the data into the buffer 
         */
        while (length && *msg_Ptr) {

                /* 
                 * The buffer is in the user data segment, not the kernel 
                 * segment so "*" assignment won't work.  We have to use 
                 * put_user which copies data from the kernel data segment to
                 * the user data segment. 
                 */
                put_user(*(msg_Ptr++), buffer++);

                length--;
                bytes_read++;
        }

        /* 
         * Most read functions return the number of bytes put into the buffer
         */
        return bytes_read;
}

Odczyt jest najbardziej skomplikowaną funkcją w naszym kodzie. Wynika to głównie z uwagi na różnice wynikające z działania kodu w trybie użytkownika i jądra. Przykładowymi funkcjami odróżniającymi tryb jądra od trybu użytkownika są:

  • w trybie jądra możliwy jest dostęp do całej pamięci systemu
  • tryb jądra nie obsługuje formatów zmiennoprzecinkowych
  • adresy (wskaźniki) z trybu użytkownika nie są poprawne w trybie jądra (i odwrotnie). Dlatego należy używać specjalnych funkcji do kopiowania danych (w naszym przypadku używana jest funkcja put_user().

Zadaniem funkcja device_read() jest skopiowanie napisu ze zmiennej występującej w module msg (tryb jądra) do bufora buffer(tryb użytkownika). W tym celu wykonywane są następujące operacje:

  • ustawienie liczby aktualnie odczytanych bajtów w tym wywołaniu fukncji bytes_read na 0.
  • sprawdzenie czy wskaźnik na aktualnie kopiowany bajt (*msg_Ptr) nie jest końcem napisu (znak ascii 0).
  • wykonanie pętli, a w każdej jej iteracji skopiowanie jednego znaku do bufora znajdującego się w pamięci użytkownika. W tym celu wykonywane jest polecenie put_user(*(msg_Ptr++), buffer++), które kopiuje jeden znak spod adresu wskazywanego przez msg_Ptr do bufora buffer.

Zauważmy że podczas kopiowania automatycznie inkrementowany jest adres msg_Ptr oraz buffer. W ten sposób w każdej kolejnej iteracji pętli wskaźniki te będą wskazywać kolejny znak w napisie msg_Ptr oraz kolejne miejsce w buforze użytkownika gdzie ten znak powinien zostać skopiowany.

Na końcu funkcji następuje zwrócenie liczby odczytanych znaków.

Zapis do urządzenia
/*  
 * Called when a process writes to dev file: echo "hi" > /dev/hello 
 */
static ssize_t
device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
{
        printk(KERN_ALERT "Sorry, this operation isn't supported.\n");
        return -EINVAL;
}

W naszym przypadku funkcja zwraca błąd. A implementacja tej funkcji jest zadaniem domowym.

Zadania

Zadania do wykonania na zajęciach

Zadanie 1

Utwórz w systemie katalog src_modules w katalogu domowym użytkownika root Następnie skompiluj kod z przykładu 1 poleceniem make.

Załaduj moduł poleceniem

insmod ./hello.ko

Następnie sprawdź działanie napisanego sterownika za pomocą polecenia dmesg.

Jako wynik powinna pojawić się podobny tekst:

[ 1934.076984] Hello world z modulu Hello!

Usuń moduł ze sterownika i ponownie sprawdź wynik operacji za pomocą polecenia lsmod oraz dmesg | tail

Zadanie 2

Stwórz moduł sop_params który:

  • ma parametry: char* name z domyślną nazwą SOP_2017.
  • ma parametr: int number z domyślną wartością równą 1.
  • Podczas ładowania modułu w logach systemu powinien pojawić się napis "Pomyslnie zaladowano modul z parametrami: <name> <number>", gdzie w miejsca <name> oraz number zostaną wstawione wartości zmiennych z modułu.

Załaduj moduł przekazując domyślne parametry. Następnie usuń go i załaduj poleceniem insmod sop_params.ko name=Imie number=indeks. Sprawdź wynik działania modułu w logach systemu.

Zadanie 3

Utwórz moduł jądra chardev i załaduj go poleceniem insmod. Następnie:

  1. Sprawdź logi w poszukiwaniu potrzebnej informacji o utworzeniu pliku specjalnego w systemie w celu obsługi urządzenia.
  2. Odczytaj wiadomość z urządzenia (poleceniem cat).
  3. Zapisz jakąś wiadomość w urządzeniu /dev/chardev i sprawdź komunikat w logach (dmesg).

Zadanie domowe

Napisz modul lab_pamiec.c, który implementuje urządzenie znakowe. Założenia modułu:

  • Zapisywanie znaków do urządzenia znakowego powinno powodować zapamiętanie tekstu w buforze.
  • Odczytanie z urządzenia (/dev/module_pamiec) powinno spowodować wczytanie wcześniej zapamiętanego tekstu.
  • Podczas załadowania modułu powinna być możliwość ustawienia domyślnej wielkości bufora (przy czym cały czas maksymalna długość to 100 znaków). I wartość ta powinna być zapisana w logach.
  • Jeżeli zostanie zapisany do urządzenia tekst przekraczający długość bufora powinien być on zapamiętany i automatycznie przycinany do długości bufora.
  • Utwórz plik log.txt zawierający ostatnie linie logów ukazujące poprawne działanie napisanego modułu z wykorzystaniem przekazanej długości bufora (np. 90).

Przykładowe działanie modułu

$ insmod lab_pamiec.ko
$ echo "ala ma kota" > /dev/lab_pamiec
$ cat /dev/lab_pamiec
ala ma kota
$ rmmod lab_pamiec
$ insmod lab_pamiec.ko length=5
$ echo "ala ma kota" > /dev/lab_pamiec
$ cat /dev/lab_pamiec
ala m
$ rmmod lab_pamiec

Uwaga: Należy zwrócić uwagę, że dostęp do pamięci z poziomu jądra różni się od dostępu do pamięci z poziomu użytkownika (zwykłego programu). Dlatego też podczas komunikacji (np. poleceniem cat /dev/lab_pamiec) należy używać funkcji get_user, put_user, copy_from_user, copy_to_user. Jako wyjaśnienie spójrz na metodę device_read w przykładowym module znakowym.

Do kopiowania bufora w metodzie device_write przydatne mogą być następujące komendy:

  • get_user
  • copy_from_user

Punkty kontrolne rozwiązania

  • poprawny ładowany kod źródłowy modułu wraz z plikiem Makefile
  • obsługa zmiennej buffer_size oraz plik logów potwierdzających poprawne działanie modułu
  • Napisanie metody device_write
  • Poprawne obsłużenie zapisu device_write nawet przy wielokrotnym wywołaniu metody zapisującej - np. wywołanie polecenia zapisu za pomocą komendy cat z tekstem zapisanym w kilku liniach:
$ insmod lab_pamiec.ko
$ cat > /dev/lab_pamiec
ala
ma
kota
$ cat /dev/lab_pamiec
ala
ma
kota
$ rmmod lab_pamiec

Wykonywanie zadania domowego

Proszę zwrócić uwagę, że zadanie domowe należy wykonać na przygotowanym obrazie systemu Linux (nie na maszynach na Proxmox!). Istnieje kilka sposobów wykonania zadania domowego:

  • W salach komputerowych - Wykonanie zadania na komputerach w salach na Wydziale Matematyki i Informatyki
  • qemu - Pobranie obrazu pliku debian_sop.img z contact.dir - Wawrzyniak Wojciech na swój komputer i uruchomienie systemu za pomocą qemu (podobnie jak na zajęciach w salach komputerowych, jednak należy zmienić ścieżki do pliku debian_sop.img w wywoływanej komendzie.
  • VirtualBox - Pobranie obrazu pliku debian_sop.vdi z contact.dir - Wawrzyniak Wojciech (/media/LOGIN-UŻYTKOWNIKA/resources/Wawrzyniak\ Wojciech) i uruchomienie obrazu w programie VirtualBox.

Uwaga: w ostatnim przypadku, mogą wystąpić problemu z dostępem do sieci w uruchomionym systemie. Wtedy należy:

  1. Ustawić jako sieć Mostkowana karta sieciowa (bridged)
  2. Po uruchomieniu systemu zalogować się w terminalu jako "root" i wpisać komendę
dhclient

Następnie odczytać numer IP:

ip show adds

I można już się łączyć przez ssh do maszyny wirtualnej (o odczytanym adresie IP np.

root@debian-sop:~# ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:ad:b2:ff brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.40/24 brd 192.168.1.255 scope global enp0s3
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fead:b2ff/64 scope link 
       valid_lft forever preferred_lft forever

Czyli w powyższym przykładzie 192.168.1.40.

W celu pobrania obrazu systemu np. z domu można podłączyć się przez protokół SFTP do serwera lts.wmi.amu.edu.pl. Obrazy plików debian_sop.img, debian_sop.vdi znajdują się w katalogu: /media/LOGIN-UŻYTKOWNIKA/resources/Wawrzyniak\ Wojciech.

Dodatkowa lektura