A NetBSD kernel belsejében Bevezetés a kernel módú programozásba Hóka Ádám The NetBSD Foundation
Mi az a kernel mód? Supervisor mode kernel, meghajtóprogramok, fajlrendszerek User mode libc, programok, démonok
Mi a különbség? Közös address space A kernel egy nagy program (monolitikus felépítés) Hiba eseten az egész megáll kernel panic, NULL pointer dereference, etc. Nincs libc és más megszokott függvények Van, ami ugyanaz: memcpy Van, ami teljesen más:malloc ↔ kmem_alloc
Memóriafoglalás NAME kmem -- kernel wired memory allocator SYNOPSIS #include void * kmem_alloc(size_t size, km_flag_t kmflags); void * kmem_zalloc(size_t size, km_flag_t kmflags); void kmem_free(void *p, size_t size);
Példa a kmem_alloc használatára uint8_t *data; /* foglalunk 128 byte memoriát */ data = kmem_alloc(128, KM_SLEEP); … /* fontos, hogy tudjuk a meretét később is! */ kmem_free(fp, 128);
Példa a kmem_zalloc használatára /* egy structra van szükségünk */ struct foo *fp; /* lefoglaljuk a struct foo méretű memóriát */ fp = kmem_zalloc(sizeof(*fp), KM_SLEEP); … /* felszabadítjuk */ kmem_free(fp, sizeof(*fp));
Mutexek Céljuk a kritikus kódrészek védelme Akkor van rájuk szükség, ha több szál is hozzáférhet a változóinkhoz, vagy szeretnénk elkerülni valamilyen más race condition-t Típusai: – Adaptive mutex – Spin mutex
Mutex típusok Spin – Rövid időközönként újra próbálkozik Hátránya: terheli a processzort Előnye: csak ez használható interrupt contextben Adaptive – Alapból spin mutexként viselkedik, de egy megadott, rövid idő után várólistára rakja a thread-et (turnstile) Nem terheli a CPU-t, viszont nem használható bárhol
NetBSD kernel mutexek SYNOPSIS #include void mutex_init(kmutex_t *mtx, kmutex_type_t type, int ipl); void mutex_destroy(kmutex_t *mtx); void mutex_enter(kmutex_t *mtx); void mutex_exit(kmutex_t *mtx);
Példa mutexekre #include kmutex_t lock; mutex_init(&lock, MUTEX_DEFAULT, IPL_NONE); mutex_enter(&lock); /*... */ mutex_exit(&lock); mutex_destroy(&lock);
condvar SYNOPSIS #include void cv_init(kcondvar_t *cv, const char *wmesg); void cv_destroy(kcondvar_t *cv); void cv_wait(kcondvar_t *cv, kmutex_t *mtx); int cv_wait_sig(kcondvar_t *cv, kmutex_t *mtx);
condvar int cv_timedwait(kcondvar_t *cv, kmutex_t *mtx, int ticks); int cv_timedwait_sig(kcondvar_t *cv, kmutex_t *mtx, int ticks); void cv_signal(kcondvar_t *cv); void cv_broadcast(kcondvar_t *cv); bool cv_has_waiters(kcondvar_t *cv);
Condvar példa 1. Thread 1: mutex_enter(&lock); cv_wait_sig(&cv, &lock); mutex_exit(&lock); Thread 2: mutex_enter(&lock); cv_signal(&cv); mutex_exit(&lock);
Condvar példa 2. mutex_enter(&lock); r = cv_timedwait_sig(&cv, &lock, hz * 4); mutex_exit(&lock); if (r == EWOULDBLOCK) /* lejárt az időzítő */ else /* jelzést kaptunk */
Egyéb fontos kernel API-k callout – Függvények időzített és periodikus futtatása bufq – struct buf kezelése queue-ban rwlock – read/write lock kthread – kernel thread létrehozása függvényből
Egyéb fontos kernel API-k spl – A CPU interrupt szintjének változtatása kauth – Authentikációs alrendszer delay – rövid szünet (busy wait) store/fetch – Adat másolás a kernel és a userspace között
Egyéb fontos kernel API-k bus_dma – Gépfüggetlen DMA (Direct Memory Access) bus_space – Gépfüggetlen I/O (registerek, memóriák elérése) softint – Szoftveres megszakítások (software interrupt) pmf – Power management framework
config(9) A kernel forrás lefordításáért felelős Kezeli a függőségeket a fájlok között A config(1) program értelmezi A kernel lefordításához fájlokat generál – A fordításhoz szükséges make(1) fájlokat – C forrás és header fájlokat
config(9) példa /usr/src/sys/conf/files # DM9000 Ethernet controllers # device dme: arp, ether, ifnet file dev/ic/dm9000.c dme
config(9) példa /usr/src/sys/arch/evbarm/conf/files.devkit8000 # on-board DM9000 # attach dme at gpmc with dme_gpmc file arch/evbarm/devkit8000/if_dme_gpmc.c dme_gpmc
config(9) példa /usr/src/sys/arch/evbarm/conf/DEVKIT8000 include "arch/evbarm/conf/files.devkit8000" # DM9000 Ethernet (requires omapgpio0) dme0 at gpmc? addr 0x2c intr 121
Hibakeresés Lehetséges hibák: – NULL pointer dereference – NULL function pointer dereference – Egyéb memóriavédelmi hibák – panic() (általában szándékosak) – KASSERT (csak DIAGNOSTIC módban) – Deadlock
Kernel opciók fejleszőknek DIAGNOSTIC – Konzisztencia ellenőrzés (KASSERT és társai) – Kb 15% lassulást okoz! LOCKDEBUG – Mutexek és CV-k használatának ellenőrzése – SMP rendszereken lassú lehet! DEBUG – Hibakereső kódrészek bekapcsolása
ddb, a kernel debugger i386-on Control-Alt-Esc, vagy panic-nál sysctl-lal állítható a működése – ddb.onpanic Whether to enter ddb on a kernel panic – ddb.fromconsole Whether ddb can be entered from the console – ddb.tee_msgbuf Whether to tee ddb output to the msgbuf – ddb.commandonenter Command to be executed on each ddb enter
ddb parancsok trace vagy bt – Stack trace kiírása – /l kapcsolóval mentés dmesg-be sync – Memory dump kiírása és újraindítás reboot – Újraindítás dump nélkül
ddb parancsok show registers – A CPU regisztereinek kiírása x/i 0x0000 – 0x0000 címen lévű utasítás assembly-jének kiírása – A cím lehet függvény neve is. continue – végrehajtás folytatása (panic után értelmetlen)
ddb parancsok ps – A (panic előtt) futó processek listázása break 0x0000 – Breakpoint a 0x0000 címen (a cím lehet fv neve)
Példa egy egyszerű driverre struct prcm_softc { device_t sc_dev; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; bus_addr_t sc_base; bus_size_t sc_size; }; /* for external access to prcm operations */ struct prcm_softc *prcm_sc; /* prototypes */ static int prcm_match(device_t, cfdata_t, void *); static void prcm_attach(device_t, device_t, void *); /* attach structures */ CFATTACH_DECL_NEW(prcm, sizeof(struct prcm_softc), prcm_match, prcm_attach, NULL, NULL);
static int prcm_match(device_t parent, cfdata_t match, void *aux) { struct obio_attach_args *obio = aux; if (obio->obio_addr != OBIOCF_ADDR_DEFAULT) return 1; return 0; }
static void prcm_attach(device_t parent, device_t self, void *aux) { struct obio_attach_args *obio = aux; prcm_sc = device_private(self); prcm_sc->sc_dev = self; prcm_sc->sc_iot = &omap_bs_tag; prcm_sc->sc_base = obio->obio_addr; prcm_sc->sc_size = OMAP2_PRM_SIZE; /* map i/o space for PRM */ if (bus_space_map(prcm_sc->sc_iot, prcm_sc->sc_base, prcm_sc->sc_size, 0, &prcm_sc->sc_ioh) != 0) { aprint_error("prcm_attach: can't map i/o space"); return; } aprint_normal(": Power, Reset and Clock Management\n"); }
static uint32_t prcm_read(bus_addr_t module, bus_addr_t reg) { return bus_space_read_4(prcm_sc->sc_iot, prcm_sc->sc_ioh, module + reg); } static void prcm_write(bus_addr_t module, bus_addr_t reg, uint32_t data) { bus_space_write_4(prcm_sc->sc_iot, prcm_sc->sc_ioh, module + reg, data); }
void prcm_cold_reset() { uint32_t val; val = prcm_read(OMAP3430_GR_MOD, OMAP2_RM_RSTCTRL); val |= OMAP_RST_DPLL3; prcm_write(OMAP3430_GR_MOD, OMAP2_RM_RSTCTRL, val); }
Házi Feladat ;-) bsd/index.html 44bsd/index.html A példa program: A prezentáció letölthető PDF formátumban az alábbi helyről: