Systemy operacyjne
Lista 1 (Lab) Termin oddania do 16.10.2026
Uwaga: Zadania z tej listy proszę nie wysyłać na SVN!
1. (2pt) Instalacja programu do wirtualizacji
- Wybierz i zainstaluj program do wirtualizacji:
- VirtualBox
- QEMU
- libvirt
- lub dowolny inny program wirtualizacyjny.
- Zapoznaj się z interfejsem programu:
- Tworzenie maszyn wirtualnych
- Zarządzanie obrazami dysków
- Podstawowe ustawienia sprzętowe: RAM, CPU, dysk, sieć
2. (5pt) Instalacja dystrybucji Ubuntu
- Pobierz obraz instalacyjny systemu Ubuntu Desktop.
- Utwórz nową maszynę wirtualną w wybranym programie i zainstaluj Ubuntu.
- Po zakończeniu instalacji:
- Uruchom system i zaloguj się.
- Naucz się podstawowych komend konsoli:
- Nawigacja w katalogach:
cd,ls,pwd,tree
- Wyświetlanie zawartości plików:
cat,less,head,tail,more
- Instalacja oprogramowania:
apt-get update,apt-get install <pakiet>,apt-get remove <pakiet>,apt-get upgrade
- Sprawdzanie informacji o systemie:
uname -a,df -h,free -h,lsb_release -a,uptime
- Praca na plikach i katalogach:
touch,mkdir,cp,mv,rm,rm -r
- Zarządzanie procesami:
ps aux,ps axjf,top,htop,kill <PID>
- Użytkownicy i uprawnienia:
whoami,id,chmod,chown,who
- Sieć:
ping,wget,curl,ip a,netstat -tulnp
- Szukanie:
find,grep,grep -r,locate
- Nawigacja w katalogach:
3. (8pt) Instalacja dystrybucji Arch Linux
Pobierz obraz systemu Arch Linux.
Utwórz nową maszynę wirtualną i zainstaluj Arch Linux zgodnie z:
- Oficjalnym przewodnikiem Arch Linux
- Alternatywnie, instrukcje wideo
W trakcie instalacji poznaj i umiej wyjaśnić w kilku zdaniach poniższe zagadnienia:
- BIOS vs UEFI
- BIOS (Basic Input/Output System) – tradycyjny system startowy komputera, inicjalizujący sprzęt i ładujący system operacyjny.
- UEFI (Unified Extensible Firmware Interface) – nowoczesny system startowy z graficznym interfejsem i obsługą dużych dysków, szybszym startem i zabezpieczeniami (Secure Boot).
- MBR vs GPT
- MBR (Master Boot Record) – starszy styl partycjonowania dysków, obsługujący maksymalnie 2 TB i do 4 partycji podstawowych.
- GPT (GUID Partition Table) – nowoczesny system partycjonowania, obsługujący dyski powyżej 2 TB i wiele partycji (ile?).
- Program do partycjonowania (np.
fdisklubcfdisklub...)- Pozwala tworzyć, usuwać i modyfikować partycje na dysku.
- Wykonaj podstawowe polecenia w maszynie wirtualnej:
fdisk -l– lista dysków- Tworzenie nowej partycji
- Ustawienie typu partycji
- Zapis zmian
- System plików
- Struktura do przechowywania plików i katalogów.
- Popularne systemy plików w Linux:
ext4,ext3,btrfs,xfs.
- Proces instalacji
- Tworzenie partycji
- Montowanie systemu plików
- Instalacja pakietów podstawowych
- Konfiguracja bootloadera
- Ustawienia sieci i konta użytkownika
- BIOS vs UEFI
4. (10pt) Instalacja systemu Xv6 (x86-32, x86-64 oraz RISCV)
- Zainstaluj system Xv6, który będziemy wykorzystywać jako referencyjną implementację prostego systemu operacyjnego (wersja dla procesora x86 wersja 32-bitowa). W środowisku GNU/Linux (Ubuntu, ArchLinux, ...) pobierz z github system operacyjny Xv6 oraz pobierz łatę xv6.patch. Łata pozwala na kompilację dla nowszych wersji gcc ≥ 12:
$ git clone https://github.com/mit-pdos/xv6-public.git
Cloning into 'xv6-public'...
⋮
$ cd xv6-public
$ wget https://cs.pwr.edu.pl/zawada/so/xv6.patch
$ patch -p1 -i xv6.patch
- Możesz teraz zbudować i uruchomić ten system. Na początek musisz zainstalować emulator qemu (np. apt install qemu lub pacman -S qemu-desktop):
$ make qemu
- Można też uruchomić bezpośrednio w terminalu (bez środowiska graficznego) przez emulator qemu tak:
$ make qemu-nox
- Z emulatora wychodzimy przez kombinację klawiszy ctrl+a x, czyli jednocześnie naciskamy ctrl i a, potem klawisz x. Pomoc skrótów pokazuje kombinacja ctrl+a h np. ctrl+a c - konsola qemu gdzie można zobaczyć dużo informacji o maszynie emulującej przez qemu np. info, info mem, info block, info registers, info qtree. System możemy śledzić przez debugger gdb uruchamiając:
$ make qemu-gdb # lub make qemu-nox-gdb
- W drugim terminalu na początek pozwolmy gdb na automatyczne ładownie pliku .gdbinit który jest przygotowany w źródłach xv6:
# komendę uruchamiamy tylko raz!
$ echo "set auto-load safe-path /" >>~/.gdbinit
- Uruchamiamy gdb:
$ gdb ./kernel GNU gdb (GDB) ⋮ Reading symbols from ./kernel... + target remote localhost:26000 The target architecture is set to "i8086". [f000:fff0] 0xffff0: ljmp $0x3630,$0xf000e05b 0x0000fff0 in ?? () + symbol-file kernel (gdb) break *0x7c00 Breakpoint 1 at 0x7c00 (gdb) c Continuing. [ 0:7c00] => 0x7c00: cli Thread 1 hit Breakpoint 1, 0x00007c00 in ?? () (gdb)
Szczegóły prawdopodobnie będą się różnić od powyższych w zależności od wersji gdb która została użyta, ale gdb powinien się zatrzymać w punkcie przerwania i powinna to być powyższa instrukcja cli.
5.
(*10pt) Napisz własny mini-kontener w języku C
(mini-container)
Napiszemy program w języku C, który pobiera jako argument ścieżkę do
katalogu pełniącego rolę nowego systemu plików (rootfs), a
następnie uruchamia w nim odizolowaną powłokę /bin/sh.
Program musi realizować następujące kroki:
- Izolacja procesów i środowiska: Utworzenie nowego
procesu potomnego za pomocą wywołania systemowego
clone()z odpowiednimi flagami izolacji:
CLONE_NEWPID– nowa przestrzeń PID (proces wewnątrz kontenera widzi siebie jako PID 1).CLONE_NEWUTS– osobna nazwa hosta (hostname).CLONE_NEWNS– osobna przestrzeń punktów montowania (mount namespace).
- Zmiana nazwy hosta: Ustawienie unikalnej nazwy
hosta wewnątrz kontenera za pomocą
sethostname(). - Zmiana korzenia systemu plików: Przejście do
podanej ścieżki za pomocą
chroot()oraz zmiana katalogu roboczego na/(chdir("/")). - Izolacja
/proc: Zamontowanie wirtualnego systemu plikówprocpod ścieżką/procwewnątrz kontenera (mount("proc", "/proc", "proc", 0, NULL)), aby komendy typupsitoppokazywały wyłącznie procesy uruchomione w kontenerze. - Uruchomienie procesu: Zamiana obrazu procesu
potomnego na powłokę
/bin/shprzy użyciu rodziny funkcjiexec().
Przykładowy program w C
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <unistd.h>
#define STACK_SIZE (1024 * 1024) // 1 MB stosu dla procesu potomnego
static char child_stack[STACK_SIZE];
struct container_config {
char *rootfs;
};
int child_main(void *arg) {
struct container_config *config = (struct container_config *)arg;
// 1. Zmiana nazwy hosta
if (sethostname("kontener-demo", 13) != 0) {
perror("sethostname");
return 1;
}
// 2. Izolacja punktów montowania (zabezpieczenie przed propagacją na hosta)
mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL);
// 3. Zmiana rootfs
if (chroot(config->rootfs) != 0 || chdir("/") != 0) {
perror("chroot/chdir");
return 1;
}
// 4. Montowanie wirtualnego systemu plików /proc dla nowych PID-ów
mount("proc", "/proc", "proc", 0, NULL);
// 5. Uruchomienie interaktywnej powłoki
char *args[] = { "/bin/sh", NULL };
execvp(args[0], args);
perror("execvp");
return 1;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Użycie: sudo %s <sciezka_do_rootfs>\n", argv[0]);
return 1;
}
struct container_config config = { .rootfs = argv[1] };
// Flagi izolacji Linuksa
int flags = CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWNS | SIGCHLD;
// Utworzenie nowego procesu w osobnej przestrzeni nazw
pid_t child_pid = clone(child_main, child_stack + STACK_SIZE, flags, &config);
if (child_pid == -1) {
perror("clone");
return 1;
}
// Czekanie na zakończenie kontenera
waitpid(child_pid, NULL, 0);
return 0;
}Przygotuj środowisko
testowe (rootfs)
Aby przetestować program, potrzebujesz katalogu ze strukturą plików i binariami przygotowanymi pod architekturę docelową (np. statycznie skompilowany BusyBox):
# 1. Tworzenie katalogu dla kontenera
$ mkdir -p mini_rootfs/{bin,proc,sys,dev}
# 2. Pobranie i skopiowanie BusyBoxa (dostarcza podstawowe narzędzia /bin/sh, ps, ls)
$ wget https://busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox -O mini_rootfs/bin/busybox
$ chmod +x mini_rootfs/bin/busybox
# 3. Utworzenie dowiązań do powłoki
$ ln -s busybox mini_rootfs/bin/sh
$ ln -s busybox mini_rootfs/bin/ps
$ ln -s busybox mini_rootfs/bin/ls
# 4. Kompilacja i uruchomienie programu C
$ gcc -o mini_container mini_container.c
$ sudo ./mini_container ./mini_rootfs
Zadania do zrobienia
- Izolacja sieciowa (
CLONE_NEWNET): Dodaj flagęCLONE_NEWNETi za pomocą komendip linklub dedykowanych wywołań stwórz para-interfejsvethłączący hosta z kontenerem. - Ograniczenia zasobów (cgroups v2): Wpisz PID
kontenera do nowo utworzonej grupy w
/sys/fs/cgroup/i nałóż limit pamięci (np.memory.max = 50M). Pokaz działanie limitu poprzez uruchomienie alokacji pamięci przekraczającej limit.
6. (*10pt) Wirtualizacja aplikacji w kontenerach Docker i Podman w systemie GNU/Linux
- Instalacja środowiska kontenerowego
- Tworzenie i uruchamianie kontenerów
Pobierz oficjalny obraz systemu Ubuntu:
$ docker pull ubuntu $ podman pull ubuntuUruchom kontener w trybie interaktywnym:
$ docker run -it ubuntu bash $ podman run -it ubuntu bashZainstaluj w kontenerze kompilator GCC:
$ apt update && apt install gcc -y
- Program testowy „Hello, World”
Wewnątrz kontenera utwórz plik
hello.cz prostym programem w C wypisującym „Hello, World”.Skompiluj program:
$ gcc hello.c -o helloUruchom wynikowy plik wykonywalny i zaprezentuj rezultat:
$ ./hello
- Tworzenie własnego obrazu z Dockerfile / Containerfile
Utwórz
Dockerfile(dla Docker) orazContainerfile(dla Podman) zawierający:- bazę
FROM ubuntu:latest - instalację GCC
- skopiowanie pliku
hello.cdo obrazu - kompilację programu w trakcie budowania obrazu
- ustawienie domyślnego polecenia
CMD ["./hello"]
- bazę
Zbuduj własny obraz:
$ docker build -t hello-docker . $ podman build -t hello-podman .
- Uruchamianie własnych obrazów
Uruchom kontener z własnym obrazem:
$ docker run hello-docker $ podman run hello-podmanPokaż wynik działania programu.
