Upload
others
View
1
Download
0
Embed Size (px)
Citation preview
Univerzitet u Novom Sadu
Fakultet tehničkih nauka
Odsek za računarsku tehniku i računarske komunikacije
Linuks u ugrađenim sistemima i razvoj rukovalaca
Procesi, raspoređivanje i prekidi
Sadržaj
Procesi, raspoređivanje i prekidi
Procesi i raspoređivanje
Uspavljivanje procesa
Rukovanje prekidima
2
PROCESI, RASPOREĐIVANJE I PREKIDI
Procesi i raspoređivanje
3
Procesi
Proces je instanca pokrenutog programa
Više instanci istog programa može biti pokrenuto
Memorija za kod programa (tekst sekcija) je deljena
Svaki proces ima svoju sekciju sa podacima, adresni prostor, stanje procesora, otvorene datoteke i signale
U kerenlu postoji posebna struktura za svaki proces
Niti
U Linuksu su niti implementirane kao procesi
Nove niti su implementirane kao obični procesi sa malom razlikom
Razlika u odnosu na običan proces - deljeni resursi sa roditeljskim procesom:
adresni prostor
resursi sistema datoteka
deskriptori datoteka
rukovaocima siganlima
Proces, nit?
Česta je zabuna oko pojmova proces, nit i zadatak
U Unix-u, proces se stvara pomoću fork() i sastoji se od
Adresnog prostora koji sadrži programski kod, podatke, stek, deljene biblioteke, itd.
Jednu nit, kao entitet vidljiv raspoređivaču
Po stvaranju, proces sadrži jednu nit
Dodatne niti mogu biti stvorene unutar postojećeg procesa pomoću pthread_create()
One se izvršavaju u istom adresnom prostoru kao i prva niti procesa
Počinju izvršavanje funkcije prosleđene kao argument pozivu pthread_create() 6
Proces, nit: sa stanovišta kernela
Kernel svaku nit pokrenutu u sistemu predstavlja strukturom tipa struct task_struct
Po pitanju raspoređivanja, nema razlike između inicijalne niti procesa i svih ostalih niti dinamički stvorenih pomoću pthread_create()
7
Životni ciklus niti
Roditeljski proces poziva fork() ili
pthread_create() i kreira novu nit
ZADATAK_POKRENUT Spreman ali se ne
izvršava
ZADATAK_POKRENUT Zapravo se izvršava
ZADATAK_SA_PREKIDIMA ili
ZADATAK_BEZ_PREKIDA Čeka
IZLAZ_ZOMBI Zadatak je završen ali njegovi resursi još nisu oslobođeni. Čeka se da roditeljksi proces potvrdi
kraj .
Proces je izabran od
strane raspoređivača
zadataka
Proces je istisnut od
strane raspoređivača
zadataka zbog
procesa većeg
prioriteta
Proces se
uspavljuje dok
čeka na neki
događaj
Događaj se
pojavluje ili
proces
dobija signal
za buđenje.
Izvršavanje sistemskog poziva
Korisnički programi i sistemski pozivi se zajedno raspoređuju
Sistemski pozivi se izvršavaju u kontekstu niti koja ih zahteva
Proces se izvršava u korisničkom prostoru (može biti istisnut)
Proces nastavlja izvršavanje u korisničkom prostoru (ili ga
menja pioritetniji proces, može biti istisnut)
Kernel kod se izvršava na osnovu poziva iz korisničkog prostora (takođe može biti
istisnut)
Sistemski poziv ili izuzetak
I dalje ima pristup
podacima procesa
(otovrene datoteke...)
PROCESI, RASPOREĐIVANJE I PREKIDI
Uspavljivanje procesa
10
Uspavljivanje procesa
Uspavljivanje procesa je potrebno kada proces (korisnički ili kernelski) čeka podatke
Korisnički proces
Korisnički proces
Sistemski poziv
Sistemski poziv
Obrađivač prekida
Drugi
procesi se
izvršavaju
Čitanje sa
uređaja
zahtev za
podacima
uspavljivanje buđenje
povratak
notifikacija o
spremnim
podacima
Kako se proces uspavljuje (1/3)
Mora se deklarisati red čekanja
Red čekanja se koristi za čuvanje liste niti koje čekaju na događaj
Statička deklaracija reda čekanja
Korisno za deklarisanje u vidu globalne promenljive
DECLARE_WAIT_QUEUE_HEAD (module_queue);
Dinamička deklaracija reda čekanja
Korisno za uključivanje reda čekanja unutar druge strukture
wait_queue_head_t queue;
init_waitqueue_head(&queue);
Kako se proces uspavljuje (2/3)
Postoji nekoliko načina da se uspava kernelski proces
void wait_event(queue, condition);
Spava dok dati C izraz ne postane tačan (ne može biti prekinut, npr. ubijanjem klijentskog procesa u korisničkom prostoru)
wait_event_killable(queue, condition);
Može da bude prekinut samo fatal signalom (SIGKILL).
Vraća -ERESTARTSYS ako je prekinut.
wait_event_interruptible(queue, condition);
Može da bude prekinut bilo kojim signalom.
Vraća -ERESTARTSYS ako je prekinut.
Kako se proces uspavljuje (3/3)
wait_event_timeout(queue, condition,
timeout);
Spava i automatski se budi posle isteka datog vremenskog perioda.
Vraća 0 ako je istekao dati vremenski period ili vrednost različitu od 0 ako je ispunjen uslov.
wait_event_interruptible_timeout(queue,
condition, timeout);
Isto kao prethodni, samo što može da bude prekinut.
Vraća 0 ako je istekao dati vremenski period, -ERESTARTSYS ako je prekinut ili pozitivnu vrednost ako je ispunjen uslov.
Primer uspavljivanja procesa
ret = wait_event_interruptible
(sonypi_device.fifo_proc_list,
kfifo_len(sonypi_device.fifo) != 0);
if (ret)
return ret;
Buđenje
Obično se pokreće od strane obrađivača prekida kada postanu dostupni podaci koje su zahtevali uspavani procesi wake_up(&queue);
Budi sve uspavane procese koji čekaju u datom redu čekanja
wake_up_interruptible(&queue);
Budi samo procese čije spavanje može da bude prekinuto a nalaze se u datom redu čekanja
Ekskluzivno naspram neekskluzivnog uspavljivanja
wait_event_interruptible() postavlja zadatak u neekskluzivno čekanje.
Svi neekskluzivni zadaci se bude pomoću wake_up() / wake_up_interruptible()
wait_event_interruptible_exclusive() postavlja zadatak u ekskluzivno čekanje.
wake_up() / wake_up_interruptible() budi sve neekskluzivne zadatke i samo jedan ekskluzivni zadatak
wake_up_all() / wake_up_interruptible_all() budi sve neekskluzivne i sve ekskluzivne zadatke
Ekskluzivna uspavljivanja su korisna za izbegavanje buđenja više zadataka onda kada će samo jedan biti u mogućnosti da “upotrebi” događaj.
Neekskluzivna uspavljivanja su korisna kada događaj može da zadovolji više zadataka.
17
Implemenatcija uspavljivanja i buđenja procesa (1/2)
Implemenatcija uspavljivanja i buđenja procesa (2/2)
Raspoređivač ne preračunava iznova uslov za buđenje procesa!
wait_event(queue, condition);
Proces se postavlja u stanje TASK_UNINTERRUPTIBLE
wake_up(queue);
Svi procesi koji čekaju u datom redu čekanja se bude, tako da se kasnije raspoređuju i dobijaju priliku da ponovo provere uslov i vrate se na uspavano stanje ukoliko uslov nije ispunjen.
Ukoliko je uslov ispunjen proces se vraća u TASK_RUNNING stanje i njegovo polje need_resched se postavlja na odgovarajuću vrednost
Na ovaj način moguće je probuditi više procesa u isto vreme
Pogledajte include/linux/wait.h za detalje.
PROCESI, RASPOREĐIVANJE I PREKIDI
Rukovanje prekidima
20
Registracija obrađivača prekida (1/3)
Preporučeno je koristiti “managed” API
int devm_request_irq(struct device *dev,
unsigned int irq,
irq_handler_t handler,
unsigned long irq_flags,
const char * devname,
void * dev_id);
Registruje obrađivač prekida, vraća 0 ukoliko je uspešno izvršeno
device za automatsko oslobađanje u vreme uklanjanja modula ili uređaja
irq je zahtevani kanal (linija) prekida. Za platformske u ređaje koristiti platform_get_irq() za dobavljanje broja (linije) prekida.
handler je pokazivač na obrađivač prekida
irq_flags je opciona maska (vidite u nastavku)
devname je registrovano ime
Registracija obrađivača prekida (2/3)
dev_id je pokazivač na neki podatak. Ne sme biti NULL i mora da bude unikatan za deljene linije prekida (irq)
U suprotnom free_irq() ne bi znao koji obrađivač prekida da oslobodi u slučaju da se koristi deljena linija
void devm_free_irq(struct device *dev,
unsigned int irq,
void * dev_id);
Eksplicitno oslobađa obrađivač prekida. U normalnim situacijama dešava se automatski (implicitno).
Definisano u datoteci include/linux/interrupt.h
Registracija obrađivača prekida (3/3)
irq_flags vrednosti (bitske vrednosti, mogu da se kombinuju, ne moraju se postaviti i tada se koristi 0)
IRQF_DISABLED
Brz obrađivač prekida - pokreće se sa onemogućenim prekidima na trenutnom procesoru (umesto samo na liniji prekida)
Trebalo bi ga koristiti samo kad je neophodno
IRQF_SHARED
Pokreće se sa onemogućenim prekidima na trenutnoj liniji i lokalnom procesoru
Linija prekida može biti deljena
Zahteva hardverski statusni registar za signalizaciju podizanja prekida
IRQF_SAMPLE_RANDOM
Prekidi mogu da se koriste za doprinos entropiji sistema koju koriste /dev/random i /dev/urandom
Korisno za generisanje nasumičnih brojeva
Ne koristiti ako je ponašanje prekida predvidljivo
Ograničenja obrađivača prekida
Ne mogu se pokrenuti iz korisničkog konteksta
Ne mogu da prenose podatke u / iz korisničkog prostora (mora da se izvrši preko sistemskih poziva) jer nema garancije u kojem će adresnom prostoru biti sistem u trenutku kada se dogodi prekid
Izvršavanje obrađivača prekida kontroliše procesor, a ne raspoređivač
Ne mogu da izvršavaju akcije koje mogu dovesti do spavanja jer nema ko da ih probudi
Moraju da alociraju memoriju sa GFP_ATOMIC
Obrađivači prekida se pokreću sa isključenim svim prekidima na lokalnom procesoru
Moraju brzo da završe svoj posao da ne blokiraju ostale prekide suviše dugo
Kada registorvati obrađivač prekida?
Na inicijalizaciji
troši puno linija (kanala) prekida
Pri otvaranju uređaja (prvi poziv open operacije nad datotekom)
Bolje za čuvanje linija prekida
Mora se brojati koliko je puta uređaj otvoren kako bi se posle poslednjeg zatvaranja odregistrovao obrađivač prekida
Informacije o registrovanim obrađivačima prekida
Spisak se dobija izlistavanjem datoteke /proc/interrupts
Napomena: brojevi prekida prikazani u levoj koloni su virtuelni brojevi u slučaju da se koristi Device Tree. Pravi fizički brojevi prekida su ili prikazani u dodatnoj koloni ili se mogu pronaći u /sys/kernel/debug/irq_domain_mapping.
CPU0 CPU1
39: 4 0 GIC TWL6030-PIH
41: 0 0 GIC l3-dbg-irq
44: 20294 0 GIC DMA
52: 0 0 GIC gpmc
...
IPI0: 0 0 Timer broadcast interrupts
IPI1: 23095 25663 Rescheduling interrupts
IPI2: 0 0 Function call interrupts
IPI3: 231 173 Single function call interrupts
IPI4: 0 0 CPU stop interrupts
LOC: 196407 136995 Local timer interrupts
Err: 0
Ukupan broj prekida
cat /proc/stat | grep intr
intr 8190767 6092967 10377 0 1102775 5 2 0 …
Ukupan
broj
prekida
Prekidi
na IRQ1
Prekidi
na IRQ2
Prekidi
na IRQ3
...
Prototip obrađivača prekida
irqreturn_t foo_interrupt(int irq, void *dev_id)
irq, broj trenutnog prekida
dev_id, pokazivač koji služi kao identifikator odgovarajućeg uređaja (korisno kada isti modul obrađuje više uređaja). Isti pokazivač koji je prosleđen u pozivu devm_request_irq().
Povratna vrednost:
IRQ_HANDLED: prekid prepoznat i obrađen
IRQ_NONE: prekid nije na uređaju koji obrađuje ovaj modul. Korisno za deljene linije prekida i/ili obaveštavanje kernela o lažnim prekidima.
Zadatak obrađivača prekida
Potvrda prekida uređaju (u suprotnim prekidi više neće biti generisani ili će se generisati iznova i iznova)
Čitanje/pisanje podataka iz/u uređaj
Buđenje uspavanih procesa koji čekaju završetak operacije, tipično korišćenjem redova čekanja
wake_up_interruptible(&module_queue);
Prekidi unutar niti
U Linuksu 2.6.30, u kernel je dodata podrška za prekide unutar niti.
Obrađivač prekida se izvršava unutar niti.
Dozvoljava blokiranje obrade prekida, što je često neophodno za I2C/SPI uređaje jer obrađivač prekida treba da komunicira sa njima.
Omogućava postavljanje prioriteta izvršenja obrađivača prekida, što je korisno za upotrebu Linuksa u realnom vremenu
int devm_request_threaded_irq(
struct device *dev,
unsigned int irq,
irq_handler_t handler, irq_handler_t thread_fn
unsigned long flags, const char *name, void *dev);
handler, “hard IRQ” obrađivač
thread_fn, izvršava se kao nit
30
Podeljeno obrađivanje prekida (1/2)
Obrada prekida se često deli na dva dela
Gornja polovina
Prava obrada prekida, potvrđuje pojavu prekida
Treba da se završi što pre jer su svi prekidi onemogućeni
Manipuliše podacima iz uređaja vezanim za prekid i raspoređuje donji deo za obradu istih
Donja polovina
Generičko Linuks ime za različite mehanizme koji omogućavaju odlaganje obradu posla vezanim za prekide. U Linuksu se implementira kao softirqs, tasklet ili workqueues.
Podeljeno obrađivanje prekida (2/2)
Softirq prekidi
Softirq prekidi su oblik donje polovine obrade
Softirq obrađivači se izvršavaju sa omogućenim prekidima, i određeni softirq obrađivač može da se izvršava na više procesora istovremeno
Izvršavaju se kada su završene sve obrade prekida, pre nego što kernel nastavi raspoređivanje procesa, pa uspavljivanje unutar njih nije dozvoljeno.
Broj softirq prekida je fiksiran u sistemu, tako da softirq prekide ne koriste direktno rukovaoci, već ceo kernel podsistem (mrežni i sl)
Lista softirq prekida je definisan u include/linux/interrupt.h: HI, TIMER, NET_TX, NET_RX, BLOCK, BLOCK_IOPOLL, TASKLET, SCHED, HRTIMER, RCU
HI i TASKLET softirq prekdi se koriste za izvršavanje taskleta
33
Taskleti (1/2)
Taskleti se izvršavaju unutar HI i TASKLET softirq prekida. Izvršavaju se sa omogućenim prekidima, ali dati tasklet se garantovano izvršava na jednom procesoru u trenutku.
Tasklet može da se deklariše statički DECLARE_TASKLET() makroom ili dinamički tasklet_init() funkcijom. Tasklet se jednostavno predstavlja funkcijom. Taskleti se mogu jednostavno koristiti iz individualnih rukovaoca uređaja, za razliku od softirq prekida.
Obrađivač prekida može da rasporedi izvršenje taskleta pomoću
tasklet_schedule() da bi se izvršio sa TASKLET softirq
tasklet_hi_schedule() da bi se izvršio sa HI softirq (viši prioritet)
34
Taskleti (2/2)
Deklaracija taskleta u datoteci sa kodom modula
DECLARE_TASKLET(module_tasklet, // ime
module_do_tasklet,// funkcija
data // parametri
);
Raspoređivanje taskleta u gornjem delu obrade prekida
tasklet_schedule(&module_tasklet);
tasklet_hi_schedule(&module_tasklet);
Taskleti se izvršavaju odmah nakon završetka svih gornjih delova prekida
Primer taskleta: pojednostavljen atmel_serial.c (1/2)
36
/* The tasklet function */
static void atmel_tasklet_func(unsigned long data) {
struct uart_port *port = (struct uart_port *)data;
[...]
}
/* Registering the tasklet */
init function(...) {
[...]
tasklet_init(&atmel_port->tasklet,
atmel_tasklet_func, (unsigned long)port);
[...]
}
Primer taskleta: pojednostavljen atmel_serial.c (2/2)
37
/* Removing the tasklet */
cleanup function(...) {
[...]
tasklet_kill(&atmel_port->tasklet);
[...]
}
/* Triggering execution of the tasklet */
somewhere function(...) {
tasklet_schedule(&atmel_port->tasklet);
}
Workqueues
Workqueues predstavljaju generički mehanizam za odlaganje posla. Upotreba nije ograničena samo na obradu prekida.
Funkcija registrovana kao workqueue se izvršava unutar niti, što znači:
Svi prekidi su omogućeni
Uspavljivanje je dozvoljeno
workqueue se registruje pomoću INIT_WORK() i tipično pokreće pomoću queue_work()
Potpun API u include/linux/workqueue.h nudi još mnogo drugih mogućnosti (stvaranje sopstvenih workqueue niti, itd).
38
Onemogućavanje prekida
Može biti korisno u regularnom rukovaocu
Obezbeđivanje da obrađivač prekida neće istisnuti kod rukovaoca
Onemogućavanje prekida na lokalnom procesoru unsigned long flags;
local_irq_save(flags); // Prekidi onemogućeni
...
local_irq_restore(flags); // Prekidi vraćeni na
prethodno stanje
Mora biti pozvano iz iste funkcije
Maskiranje linije prekida
Korisno za onemogućavanje prekida na određenoj liniji
void disable_irq(unsigned int irq);
onemogućava liniju irq za sve procesore u sistemu
čeka da se završe sve obrade prekida koje su već pokrenute
void disable_irq_nosync(unsigned int irq);
Isto, samo što ne čeka kraj obrada
void enable_irq(unsigned int irq);
Omogućava prekide na liniji irq
void synchronize_irq(unisgned int irq);
Čeka kraj obrada prekida koje su u toku
Provera statusa prekida
Korisno za kod koji se može izvršavati i iz konteksta prekida i procesa kako bi se znalo da li su akcije koje mogu dovesti do uspavljivanja dozvoljene
irq_disabled()
Provera da li su prekidi onemogućeni
in_interrupt()
Da li se kod izvršava u kontekstu izuzetka?
in_irq()
Da li se kod izvršava u kontekstu obrađivača prekida?
Pregled prekida i obrađivača prekida
Rukovaoc uređajem
Registracija obrađivača prekida pri prvom pozivanju opreacije open nad uređajem
Obrađivač prekida
Poziva se kada se desi prekid
Potvrđuje prekid
Ukoliko je potrebno kreira tasklet za obradu podataka
Budi uspavane procese koji čekaju na podatke
Tasklet
Obrađuje podatke
Budi uspavane procese koji čekaju na podatke
Rukovaoc uređajem
Odregistracija obrađivača prekida kada uređaj više nije otvoren