Operativsystem IS1350 LAB 2 2011-09-26 Förberedelser: Läs igenom labhäftet. Gör uppgifterna i förväg, använd labtiden till att ställa frågor om du kört fast. Läs igenom tipsen om kärnakompilering på http://bobcares.com/ blog?p=162 Läs igenom artiklarna om kärnahackning http://www.ict.kth.se/courses/is1350/labs/lab2/l-kernelhack1-a4- Mod.pdf http://www.ict.kth.se/courses/is1350/labs/lab2/l-kernelhack2-a4- Mod.pdf Checklista: Inloggningsuppgifter till KTH.SE-konto Laptop med Linux (Uppgifterna är testade med Ubuntu Server på 32-bitars Intel-processor som du kan ladda ned från kurshemsidan) Följande paket behövs för att kunna kompilera en kärna: gcc make manpages-dev initramfs-tools binutils
Mål Efter laborationen ska du kunna Kunna installera en operativsystemskärna i Linux. Förklara vad som krävs för att få en kärna att fungera. Beskriva hårdvaran som används i datorn och använda informationen för att optimera kärnan. Kompilera en egen kärna och beskriva stegen som behövs. Kompilera en kärnamodul och beskriva processen. Sammanfattning Denna labb syftar till att studera en operativsystemskärna samt programmering i kärnaläge (kernel mode). Vi kommer att bekanta oss med källkodsträdet till Linux-kärnan samt processen för att kompilera en kärna. Vi kommer även att testa att skapa en egen kärnamodul (drivrutin). Labben skall utföras gruppvis på en pc-server körandes Linux. Vissa delar för högre betyg utförs individuellt, vissa gruppvis. Redovisning Redovisning kommer att ske gruppvis för betyget godkänt. För högre betyg måste du dessutom redovisa uppgifterna för högre betyg individuellt. (Det är ok att göra uppgifterna i grupp). Ubuntu server på CD OBS! Fungerar med 32-bitars Intel-processorer. Får du felmeddelandet unable to boot - please use a kernel appropriate for your cpu kan du inte använda den färdiga Ubuntu-servern, istället måste du ladda hem och installera en annan version av Linux på din dator, t.ex. Ubuntu desktop. Du kan köra valfri Linux-distribution bara du installerar paketen som nämns i checklistan. Följande instruktioner gäller om du använder den färdiga serverinstallationen från kurshemsidan: 1. Ladda ned ubuntu-server.exe från kurswebben. 2. Packa upp filen ubuntu-server.exe på din hårddisk. 3. Ladda ned och installera vmware-player.
4. Öppna katalogen med ubuntu-server i vmware-player. När du får frågan om du flyttat eller kopierat den virtuella maskinen svarar du flyttat. Användarnamn: ubuntu Lösenord: server OBS! Installationen har inget root-lösenord satt. Använd kommandot sudo för att få root-rättigheter. Uppgift 1: Kompilera Linuxkärnan Själva kompilerandet av kärnan tar ganska lång tid. Planera därför din tid så att du kan göra andra saker medan själva kompileringen pågår. Introduktion Kärnan i Linux har till uppgift att hantera processer och minne och vara ett abstraktionslager mot hårdvaran. För att stödja olika typer av hårdvara läggs drivrutinerna i moduler. Dessa kan laddas in/laddas ur vid behov. När en Linuxdistributör gör en livecd med Linux behöver kärnan drivrutiner för alla tänkbara kombinationer av hårdvara. Eftersom bara de nödvändiga modulerna laddas in kan storleken på kärnan hållas nere. Detta var framför allt viktigt förr i tiden när datorer startades på disketter. Idag är det av betydelse när linux ska köras i t.ex. mobiltelefoner. En linuxkärna kan kompileras från källkod av vem som helst. Det som behövs är en kompilator, till exempel gcc, programmet make, gnu:s programpaket binutils, källkoden till kärnan och gott om tålamod. Att kompilera en kärna tar tid och det kan krävas flera försök innan kärnan fungerar som tänkt. Innan kärnan kompileras behöver den konfigureras. I Linux görs det med hjälp av make-kommandot. Aktuell konfiguration sparas i roten av källkodsträdet under namnet.config. Konfigureringen görs med hjälp av kommandot make. Instruktionerna till make finns lagrade i filen Makefile i roten källkodsträdet. Med parametrar till make kan du välja om du vill konfigurera i text-läge, text-läge med grafik eller i X11-läge. I den här laborationen utgår vi från att du kompilerar i text-läge med grafik.
Konfigurationen för en 2.6.x kärna är indelad i följande grupper General Setup Allmän konfiguration av kärnan Processor type and features Optimeringar av kärnan för olika processorer Power management options Strömsparfunktioner enligt ACPI och APM Bus options (PCI etc) Stöd för olika busstyper som t.ex PCI, PCI-X och PCMCIA. Executable file formats / Emulations Format för körbara filer, endast ELF är intressant för normala linuxdistributioner Networking Stöd för olika typer av nätverk (stöd för hårdvara finns under Device Drivers), t.ex. trådlösa nät Device Drivers Drivrutiner för hårdvara, t.ex. ide, scsi, usb, nätverk. Firmware Drivers Stöd för att använda BIOS för olika funktioner File Systems Drivrutiner för olika filsystemsformat, t.ex. ext3 och vfat Kernel hacking Inställningar för avlusning av kärnan Security options Inställningar för säkerhet som t.ex. SE-Linux Cryptographic API Kryptografiska algoritmer Virtualization Stöd för hosting av gästoperativsystem Library routines CRC-algoritmer För prestandaoptimering är avsnitten Processor type and features och Kernel hacking av störst betydelse. Eftersom det kan finnas flera drivrutiner som är olika bra anpassade för samma hårdvara kan det också löna sig att ta reda på vilka drivrutiner som används av kärnan. Storleken på kärnan avgörs framförallt av valen under avsnitten Networking och Device Drivers.
Skapa kataloger Skapa katalogen lab2 med underkatalogerna build, uppgift 1 och linux i din hemkatalog. mkdir p ~/lab2/build mkdir p ~/lab2/uppgift1 I katalogen uppgift1 ska du spara källkoden till kärnan och i build-katalogen hamnar de kompilerade filerna för den nya kärnan. Hämta och installera källkoden Källkoden distribueras från kernel.org. Vi kan också hämta den från Sunets ftparkiv som speglar källkoden: ftp://ftp.sunet.se/pub/linux/kernels/v2.6/. Om du använder Ubuntu-server från kurshemsidan så finns källkoden redan i användaren ubuntu:s hemkatalog, gör steg 1 och gå sedan direkt till steg 4. Din nya kärna har versionsnummer 2.6.26.5 1. Tag reda på versionsnumret för din linuxkärna med följande kommando uname r 2. Anslut till sunet:s ftp-arkiv och leta reda på en nyare kärna. Om du vill ha senaste versionen så talar filen LATEST-IS... om vilket nummer som är den senaste. 3. Hämta källkoden med wget (ersätt <version> med versionsnumret för kärnan) wget ftp://ftp.sunet.se/pub/linux/kernels/v2.6/linux- <version>.tar.bz2 4. Packa upp kärnan i en lämplig katalog (skriv kommandot på en enda rad och byt ut version mot versionsnumret för kärnan): tar xfvj linux-<version>.tar.bz2 --directory ~/lab2/ uppgift1 5. Skapa en länk till källkodskatalogen så du slipper ange versionsnr när du jobbar med källkoden (skriv kommandot på en enda rad): ln s ~/lab2/uppgift1/linux-<version> ~/lab2/uppgift1/linux Konvertera den gamla konfigurationen När det redan finns en gammal konfiguration så behöver du inte gå igenom hela konfigurationsprocessen och svara på alla frågor. Det räcker med att uppdatera
kärnans konfiguration med de ändringar som gjorts mellan den nya och den gamla versionen. 1. Kopiera den gamla konfigurationen till källkodsträdet (skriv kommandot på en enda rad): cp /boot/config-<din-gamla-kärna-version> ~/lab2/ build/.config 2. Byt katalog till källkodsträdets root: cd ~/lab2/uppgift1/linux Kompilera kärnan När du börjar kompilera kommer den gamla config-filen att anpassas till den nya kärnan. Du återanvänder då de gamla inställningarna och konfigurerar bara de nya inställningarna som inte fanns med i den gamla kärnan. (Håll ned enter så går det snabbare att svara på frågorna). Kompilera kärnan och modulerna med kommandot make O=~/lab2/build OBS! O:et i kommnadot ovan är bokstaven O! Datorer med flera processorer/kärnor eller hyperthreading kan kompilera snabbare om du lägger du till växeln j # till kommandot make (byt ut # mot antalet kärnor/cpu:er). Det gäller dock inte om du använder Ubuntu server som är förinställd på att använda bara en processor i VMWare Player. Installera kärnan Nu är det dags att installera kärnan. Det görs i flera steg. Först ska du kopiera kärnan, dess konfiguration och System.map till katalogen /boot. Du behöver också kopiera alla moduler du kompilerat och skapa en initrd. Den senare är en zip-fil som ska innehålla de moduler som kärnan behöver för att kunna läsa från hårddisken vid uppstart, t.ex. drivrutinen för hårddiskkontrollern. Med en statiskt länkad kärna där alla drivrutiner är inbakade i kärnan behövs ingen initrd. Till sist behöver du lägga till din nya kärnan i menyn som visas när Linux startar. 1. Installera vmlinuz, System.map sudo make O=~/lab2/build install sudo make O=~/lab2/build modules_install 2. Skapa och installera initrd. sudo update-initramfs c k <din-nya-kärna-version>
3. Uppdatera startmenyn (grub). sudo rm /boot/grub/menu.lst sudo update-grub OBS! Svara ja på frågan om du vill skapa menu.lst 4. Om du kan hantera en editor i Linux så får du gärna titta igenom filen /boot/grub/menu.lst och ta bort menyalternativ som saknar initrd. Behåll alltid minst ett alternativ som du vet fungerar. Testa kärnan Nu återstår bara att starta om datorn och testa att den nya kärnan fungerar. Om den inte gör det får du backa tillbaka till början av labbinstruktionen och försöka igen. 1. Starta om datorn (du måste vara root). sudo reboot 2. Du kan behöva välja din kärna i menyn som visas när datorn startar, tryck på ESC när texten med grub kommer upp i början. 3. Använd versionsnumret på den nya kärnan för att identifiera vilken rad du ska starta med. OBS! Alternativen med Default på slutet av raden saknar initrd och startar inte korrekt. I skärmdumpen nedan ska du alltså starta på alternativ 3, Ubuntu 8.04.1, kernel 2.6.26.5. Uppgift 2: Skriva ut meddelanden från kärnan Introduktion I den här uppgiften ska du ändra i kärnan för att få den att skriva ut ett meddelande vid uppstart, det blir ett hello world! exempel för linuxkärnor. Utskrifter från kärnan kan göras med en funktion som heter printk(). Den motsvarar printf från standardbiblioteken i C. Uppgift Din utskrift kan du lägga till i filen init/main.c. Det betyder att meddelandet
kommer att skrivas ut vid initieringen av kärnan. Öppna filen med t.ex. pico eller vi. Leta reda på anropet av funktionen calibrate_delay(). Det kan se ut så här i källkoden: if(late_time_init) late_time_init(); calibrate_delay(); pidmap_init(); Lägg till koden för printk utskriften på raden efter anropet av calibrate_delay(). printk( ***I am a kernel hacker!***\n ); Spara filen och kompilera om kärnan (Du vet hur du gör nu eller hur?) och installera den. Det kan vara svårt att hinna med att se utskriften så du kan starta på den nya kärnan och köra följande kommando när du loggat in: dmesg grep i hello Kommandot dmesg listar alla utskrifter från kärnan, grep visar bara de meddelanden som innehåller hello och i flaggan gör att grep inte skiljer på stora eller små bokstäver. Kan du använda printf för att skriva ut hello world från kärnan? Uppgift 3: Kernel modules Kärnan i Linux är modulärt uppbyggd, vilket innebär att drivrutiner och extra funktionalitet läggs i separata binärer. I den här uppgiften ska du skriva en egen modul. Modulen ska räkna antalet avbrott som sker på nätverkskortet och skriva ut dessa. Skelettet för en modul kan se ut så här: #include <linux/module.h> #include <linux/init.h> static int init mymodule_init(void) { printk ("My module worked!\n"); return 0; } static void exit mymodule_exit(void) { printk ("Unloading my module.\n"); return; } module_init(mymodule_init); module_exit(mymodule_exit); MODULE_LICENSE("GPL");
För att den här laborationen ska fungera måste du ha konfigurerat din kärna att stödja laddningsbara moduler, Loadable module support. 1. Byt katalog till drivers/misc under ~/lab2/uppgift1/linux. 2. Kopiera texten ovan till en fil med namnet mymodule.c 3. Öppna filen Makefile och lägg till följande rad i slutet av filen: obj-m += mymodule.o 4. Kompilera din modul. Du behöver inte kompilera om hela kärnan eftersom din modul kan laddas in i kärnan dynamiskt. Skriv in följande på en enda rad på kommandoraden (skriv kommandot på en enda rad): make O=/home/ubuntu/lab2/build C /home/ubuntu/lab2/ uppgift1/linux SUBDIRS=$PWD modules 5. Nu kan du dynamiskt ladda in din modul och se om den fungerar. Det gör du med följande kommando: sudo insmod mymodule.ko 6. Använd dmesg tail för att se om din modul fungerade. Vilken utskrift ska du få om det fungerar? 7. Ladda ur din modul från kärnan med kommandot sudo rmmod mymodule En riktig modul Nu ska du göra en modul som räknar antalet avbrott som nätverkskortet genererar och skriver in det i kärnaloggen. Kopiera din gamla modulfil mymodule.c till en ny fil med namnet myirqtest.c. Byt ut mymodule mot myirqtest i koden. Först lägger vi till en include fil högst upp i modulfilen: #include <linux/interrupt.h> Nu kommer den svåra delen, att lista ut vilka avbrott som används av
nätverkskortet i din dator. Skriv cat /proc/interrupts Första kolumnen listar vilket avbrottsnummer andra kolumnen antalet avbrott och den tredje vilken enhet som är kopplad till avbrottet. I din nya modul ska du lägga till stöd för parametrar. Parametrar anger du tillsammans med insmod kommandot: Insmod mymodule.ko {param1=value param2=value...} Kommandot ovan är rätt meningslöst om du inte skriver lite kod som kan använda parametrarna. Vi ska ta avbrottsnummer och enhetsnamn som parametrar: #define BUFLEN 10 static int irq; static char interface[buflen]; module_param_string(interface,interface,buflen,0); MODULE_PARM_DESC(interface, "A network interface"); module_param(irq,int,0); MODULE_PARM_DESC(irq, "The IRQ of the network interface"); Avbrottshanterare När det sker ett hårdvaruavbrott med ett visst nummer så kommer avbrottshanterarna som registrerat sig för att lyssna på avbrottet att anropas av kärnan. int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char *devname, void *dev_id); Funktionen request_irg() lägger till din modul till listan på funktioner som ska anropas när det sker ett avbrott. Vilket avbrott vi vill lyssna på anges som en parameter till request_irq(). Övriga parametrar är en pekare till din funktion som ska anropas av kärnan när det sker ett avbrott, en flagga, namnet på enheten och ett id som identifierar vår avbrottshanterare. Du kan skriva in följande kod för din avbrottshanterare static int init myirqtest_init(void) { if (request_irq(irq, &myinterrupt, IRQF_SHARED, interface, &irq)) { printk(kern_err "myirqtest: cannot register IRQ %d\n", irq); return -EIO; } printk("request on IRQ %d succeeded\n", irq); return 0; }
Nu ska du lägga till en funktion som avregistrerar din modul från listan av avbrottshanterare. Avregistrering sker genom att anropa free_irq som tar två parametrar, avbrottsnummer och id:et på din avbrottshanterare. Du kan lägga till följande kod i funktionen myirqtest_exit(): free_irq(irq, &irq); printk("freeing IRQ %d\n", irq); Eftersom flera olika enheter kan dela på samma avbrott skulle vi egentligen behöva titta på registren för att avgöra om det var nätverkskortet som genererade avbrottet. I en riktig modul skulle vi göra detta och om avbrottet var avsett för en annan enhet skulle vi skicka vidare avbrottet till nästa hanterare, annars meddela kärnan att avbrottet är hanterat och inte ska skickas vidare. Eftersom din modul inte hanterar enheter, bara räknar avbrott, måste vi skicka vidare alla anrop till nästa hanterare I kedjan. Det gör vi genom att låta vår avbrottshanterare returnera värdet IRQ_NONE. Din avbrottshanterare ska räkna antalet avbrott och det kan den göra med följande kod: static irqreturn_t myinterrupt(int irq, void *dev_id, struct pt_regs *regs) { static int mycount = 0; if (mycount < 10) { printk("interrupt!\n"); mycount++; } return IRQ_NONE; } Vid redovisningen ska du kunna förklara vad koden ovan gör! Nu din modul klar. Lägg till din nya modul till Makefile så att den kan kompileras: obj-m += myirqtest.o Kompilera din modul enligt tidigare instruktion och ladda in den i kärnan tillsammans med parametrar för modulnamn och avbrottsnummer: sudo insmod myirqtest.ko interface=eth0 irq=9 Ditt nätverkskort kanske inte har namnet eth0 och irq 9. Du behöver inte ens använda nätverkskortet, du kan använda valfri enhet som genererar avbrott med jämna mellanrum, t.ex. hårddisken. Vilka namn enheterna har och vilka irqnummer de använder kan du ta reda på med: cat /proc/interrupts Exempel på utskrift från kommandot:
9: 233 IO-APIC-fasteoi eth0 Använd dmesg för att kontrollera att din avbrottsräknare fungerar. Du kan generera lite nätverkstrafik med ping ifall det behövs. Glöm inte att ladda ur modulen. Vilket meddelande skrivs i dmesg när din modul laddas ur? Kan du med cat /proc/interrupts se om din avbrottsrutin är laddad? Uppgift 4: Slimmad kärna (endast högre betyg) En stor del av processen att kompilera en egen Linux-kärna är att konfigurera vilka moduler man behöver. De centrala delarna i Linux-kärnan som till exempel schemaläggaren eller minneshanteringen går inte att välja till eller bort. Däremot kan man välja exakt vilken hårdvara man vill ha stöd för. I standardkonfigurationen som följer med Ubuntu server är de flesta valen i kärnakonfigurationen aktiva. Eftersom det innebär att mer kod måste kompileras så tar det lång tid för datorn att kompilera ihop kärnan. Därför bör du optimera din kärna så lång som möjligt. Power Management APM är en gammal standard för strömsparfunktioner som ersatts av ACPI. Nyare datorer har ACPI så du kan välja bort APM. Bus options Nyare datorer använder sig av PCI och PCI Express-bussarna. Har du en bärbar dator har den även en PCCard-buss (eller Expresscard). Du kan välja bort ISA, MCA, NatSemi och support för PCI Hotplug. Executable file formats / Emulations Numera används bara ELF, övriga format kan du välja bort. Networking Behåll trådbaserat nätverk (de flesta inställningarna under Networking options). Om du har en bärbar dator behåller du även trådlöst nätverk, Wireless. Övriga alternativ kan du välja bort. Observera att du inte kan välja bort trådlöst nätverk innan du har kryssat bort Wireless LAN under rubriken Device Drivers/Network device support.
Device Drivers De flesta alternativen under rubriken handlar om drivrutiner för hårdvaran i datorn. Följande alternativ kan väljas bort: Memory Technology Device (MTD) support Parallell port support Misc Devices IEEE1394 (Firewire) support* I2O device support Macintosh device drivers ISDN support Telephony support I2C support SPI support Dallas 1-wire support (Välj bort Hardware Monitoring support först) Power supply class support* Hardware Monitoring support Watchdog Timer Support Sonics Silicon Backplane (Välj bort Multifunction device drivers först)* Multifunction device drivers* Multimedia devices* Sound* MMC/SD card support InfiniBand support EDAC error detection and reporting Real Time Clock Userspace I/O drivers Firmware Drivers Här kan du välja bort allt File systems De vanligaste filsystemen för Linux är listade direkt under rubriken, dessa ska du behålla. I underrubrikerna behåller du valen under Pseudo filesystems, Partition Types och Native language support. Övriga alternativ kan du välja bort. Kernel hacking Här kan du behålla standardvalen Security options Välj bort bort NSA SELinux Support Cryptographic API Välj bort Hardware crypto devices
Virtualization Välj bort hela rubriken Library routines Behåll standardvalen Din uppgift är att med hjälp av konfigurationsverktyget göra en slimmad kärna där mycket onödig tagits bort. Vidare skall viktig hårdvara länkas in statiskt i stället för som moduler. Syftet skall vara att bekanta sig med processen och inte åstadkomma en perfekt slimmad kärna. Denna uppgift kan utföras gruppvis men ska redovisas individuellt. Uppgift 5: /proc (endast högre betyg) Många Unixvarianter har ett speciellt filsystem som ligger monterat under /proc. Detta filsystem sparas inte på disk utan är ett virtuellt filsystem i kärnan. Filerna som finns här innehåller t.ex. information om processerna i systemet, avbrottshantering, temperatur på moderkortet m.m. Vissa filer kan man skriva till för att i realtid ändra kärnans beteende. Exempel på saker man kan förändra är TCP/IP-inställningar, paketväxling och SysRq. Du skall modifiera den kärnamodul som du skrev i uppgift 2. Den skall inte längre skriva ut meddelande till konsolen varje gång det kommer ett avbrott. Istället skall den fortsätta att räkna upp räknaren tills modulen laddas ur. Vidare skall du registrera en skrivskyddad-fil någonstans i /proc-filsystemet. När du gör cat på din fil under /proc så skall värdet i räknaren skrivas ut. Här hittar du information om procfs: http://www.compsoc.man.ac.uk/~moz/ kernelnewbies/documents/kdoc/procfs-guide/lkprocfsguide.html Här hittar du kodexempel och allmän information: http://www.freescale.com/files/32bit/doc/app_note/an2744.pdf Tips: Funktionen du behöver använda för att skapa en fil i /proc heter create_proc_read_entry();
Referenser Uppgift 2 och 3 bygger på kod och text från följande källor: [1] http://www-128.ibm.com/developerworks/edu/l-dw-linux-kernelhack1-i.html [2] http://www-128.ibm.com/developerworks/linux/edu/l-dw-linux-kernelhack2- i.html