Linux714




Przerwania sprzetowe




do spisu tresci tematu 7


7.1.4 Przerwania sprzetowe

Spis tresci

Wprowadzenie
Obsluga przerwan
"Dolne polowy" i kolejki zadan
Zrodla informacji



Wprowadzenie

Jedynym sposobem, w jaki urzadzenie moze zwrocic na siebie uwage zapracowanego
procesora jest wywolanie przerwania sprzetowego. Przerwanie sprzetowe to
asynchroniczne zdarzenie podobne do sygnalu
- tak jak jadro wysyla sygnaly do procesow uzytkownika informujac je o
sytuacjach wyjatkowych i wymagajacych podjecia natychmiastowej akcji, tak
urzadzenie generuje przerwanie, zeby zawiadomic program obslugi np. o
zakonczeniu operacji wejscia-wyjscia, bledzie. Przerwania generuje rowniez zegar.
Podobny jest sposob obslugi tych dwoch rodzajow zdarzen.


Obsluga przerwan

Zeby moc zareagowac na przerwanie, nalezy zarejestrowac procedure obslugi jednej
z 16 linii przerwan (IRQ - Interrupt Request Lines).
Mozna to zrobic przy pomocy zdefiniowanej w pliku

arch/i386/kernel/irq.c funkcji:


int request_irq( unsigned int irq,
void (*handler)(int , void *, struct pt_regs *),
unsigned long flags,
const char *device,
void *dev_id)



irq
numer przerwania, ktore checemy obslugiwac ( 0 - 15 )
handler
wskaznik do procedury obslugi przerwania,
flags
flagi: SA_INTERRUPT, SA_SHIRQ lub 0,
device
nazwa urzadzenia, ktora mozemy sobie obejrzec czytajac plik /proc/interrupts;
dev_id
ten parametr zostal dodany do wywolania request_irq w nowszych
wersjach Linuxa (okolo 1.3.70), jest zdefiniowany tylko dla urzadzen podlaczanych
do szyny PCI (pozostale przekazuja NULL);
Wynik:
0, -EINVAL jesli irq poza przedzialem 0-15 lub -EBUSY jesli
ta linia jest juz obslugiwana.

Parametry z ktorymi wywolywana jest funkcja handler to :

numer linii przerwania
wskaznik do struktury dev_id sluzacy do
zidentyfikowania uzadzenia, jesli linia przerwania jest dzielona miedzy kilka
(patrz nizej),
struktura opisujace rejestry procesora;

Znaczenie flag:

SA_INTERRUPT
W czasie wykonywania procedury obslugi przerwania wlaczone sa wszytkie inne
przerwania, a po powrocie z procedury obslugi przerwania sprawdzane jest
ustawienie globalnej flagi need_resched. Jesli jest ona ustawiona,
to wywolywana jest funkcja szeregujaca (schedule()). Wywolujac
request_irq z flaga SA_INTERRUPT definiujemy "szybka" procedure obslugi,
w czasie ktorej zamaskowane sa przerwania i po ktorej nie nastepuje
szeregowanie.
SA_SHIRQ
Poniewaz linii przerwan jest tylko 16, w Linuxie 1.3.70 pojawila sie mozliwosc
dzielenia linii przerwania przez kilka programow obslugi urzadzen, kazde z nich
musi uzyc flagi SA_SHIRQ i przekazac identyfikator dev_id.

Programy obslugi urzadzen korzystajacych z przerwan musza czesto szukac "na oslep"
linii przerwania uzywanej przez fizyczne urzadzenie. Przydaja sie do tego dwie
funkcje:

unsigned long probe_irq_on(void)
zwraca maske z ustawionymi bitami odpowiadajacymi przerwaniom dla ktorych
zarejestrowano procedure obslugi
int probe_irq_of(unsigned long)
bierze maske jako parametr i zwraca numer przerwania ktore nastapilo, 0
jesli nie nastapilo przerwanie lub liczbe ujemna jesli nastapilo wiele przerwan

Algorytm poszukiwania linii przerwania moze wygladac tak:


{
sti();
/* pobierz nieuzywane numery linii: */
maska = irq_probe_on();
kaz urzadzeniu wywolac przerwanie; /* uzywajac portow wejscia-wyjscia */
przeczekaj czas wystarczajacy na wywolanie przerwania;
przerwanie = irq_probe_off( maska );
}


Funkcja free_irq( unsigned int irq ) usuwa obsluge przerwania.


Dolne polowy i kolejki zadan

Procedura obslugi przerwania musi nieraz wykonywac dosyc zlozone i czasochlonne
operacje, na przyklad reorganizowac kolejke zadan dostepu do urzadzenia, budzic
czekajace na jakies zdarzenie procesy, przepisywac dane z portow do buforow
(patrz program obslugi dysku).
Podczas wykonywania procedury obslugujacej przerwanie zostaje ono (a nieraz
takze inne przerwania) zamaskowane, na przyklad aby uniknac uszkodzenia roznych
waznych struktur danych. Jesli obsluga przerwania nie jest dosc szybka to
nastepne przerwanie moze nie zostac obsluzone, w efekcie mozemy co najmniej utracic
jakies dane, jesli nie stanie sie cos gorszego.

Aby zmniejszyc ryzyko takiego zdarzenia, mozna podzielic procedure obslugi
przerwania na dwie czesci:

"gorna polowe" (top half), ktora musi zakonczyc sie przed nadejsciem
kolejnego przerwania, i zajmuje sie najczesciej sprawdzeniem stanu urzadzenia
i kopiowaniem danych z urzadzenia do bufora w pamieci
"dolna polowe" (bottom half), ktora wykonuje sie dowolnie dlugo
z odmaskowanymi przerwaniami; powinna zostac wywolana jak najszybciej po
"gornej" czesci procedury

Jadro Linuxa umozliwia wywolywanie i wykonanie "bottom half" (bede poslugiwac
sie oryginalnym terminem z braku zgrabnego tlumaczenia) poza kontekstem konkretnego
procesu - podczas szeregowania procesow lub wychodzenia
z funkcji systemowej. W tym celu jadro utrzymuje tablice bh_base 32
wskaznikow do funkcji typu "bottom half" dla najwazniejszych urzadzen: zegara,
konsoli, klawiatury i paru innych - 20 wpisow pozostaje wolnych
(plik
include/linux/interrupt.h).
Funkcja init_bh pobiera jako parametr numer w tablicy bh_base
i wskaznik na funkcje "bottom half" ktory do tablicy wstawia.
Podczas obslugi przrwania funkcja "top half" zwieksza licznik ilosci wywolan
funkcji "bottom half" znajdujacej sie w bh_base wywolujac funkcje
mark_bh z argumentem bedacym indeksem w tablicy. Przejrzenie tablicy
bh_base i wywolanie zaznaczonych funkcji nastapi po obsludze wszystkich
przerwan, na poczatku najblizszego
wykonania
schedule() lub return_from_sys_call(). Najpierw
wywolywane sa funkcje majace nizszy numer w tablicy

W ten sposob z obsluga jednego przerwania moze byc zwiazanych wiele funkcji
"bottom half". Funkcja "top half" moze wybrac jedna lub wiele z nich. Ograniczeniem
jest jednak niewielki rozmiar tablicy bh_base (okolo 20 wolnych pozycji).
Rozwiazaniem tego problemu sa

Kolejki zadan (Task queues)
Kolejki zadan zdefiniowane sa w pliku

/include/linux/tqueue.h.


struct tq_struct {
struct tq_struct * next;
int sync;
void (* routine)(void *);
void *data;
}

typedef struct tq_struct * task_queue;


Kolejka zadan to po prostu lista struktur tq_struct zawierajacych
wskazniki do funkcji "bottom half" i do ich argumentow.
Funkcje do obslugi kolejek zadan:

void queue_task( struct tq_struct *bh_pointer,task_queue *bh_list)
ta funkcja dolacza nowa stukture tq_struct do listy bh_list,
void run_task_queue( task_queue *list)
wywoluje wszystkie funkcje z listy list poczynajac od ostatnio
wlozonej. Kazda funkcja zostaje najpierw zdjeta z listy a potem wywolana, dzieki
czemu moze wykonujac sie wlozyc sie (albo inna funkcje) na liste.

Podzielone na "gorna" i "dolna" czesc procedury obslugi przerwan moga teraz wygladac
tak:


#define NUMER 20 /* indeks w tablicy bh_base pod */
/* ktory wstawimy wskaznik do funkcji dolna_polowa() */
task_queue kolejka = NULL;
struct tq_struct zadanie_1 = { 0,0,funkcja_1,arg_1 };
struct tq_struct zadanie_2 = { 0,0,funkcja_2,arg_2 };

/* "dolna polowa" uruchamia tylko kolejke */
void dolna_polowa()
{
run_task_queue( &kolejka );
}

/* "gorna polowa" procedury obslugi przerwania */
void gorna_polowa( int irq, void *dev_id, struct pt_regs *regs )
{
sprawdz stan urzadzenia;
przeslij dane;
if( warunek1 ) {
queue_task( &zadanie_1, &kolejka );
mark_bh( NUMER );
}
if( warunek2 ) {
queue_task( &zadanie_2, &kolejka );
mark_bh( NUMER );
}
}

int inicjalizacja_sterownika()
{
...
init_bh( NUMER, dolna_polowa );
}



Zamiast wstawiac funkcje dolna_polowa do tablicy bh_base
i uaktywniac je przy pomocy mark_bh( NUMER ) mozna dodawac je do
zdefiniowanej w pliku kerne/sched.c kolejki tq_immediate,
ktora jest uruchamiana przy przegladaniu tablicy bh_base i ma w tej
tablicy dosyc wysoki numer (7) dzieki czemu umieszczone w niej "dolne polowy"
wykonuja sie wczesnie.


Zrodla informacji

Kod zrodlowy Linuxa, pliki:

include/linux/interrupt.h,

include/linux/tqueue.h,

include/asm-i386/irq.h,

kernel/softirq.c,

arch/i386/kernel/irq.c

Linux Journal - artykuly z dzialu Kernel Korner, szczegolnie
numer 26,

Michael K. Johnson: Artykuly dotyczace programow obslugi przerwan z Kernel Hackers'
Guide,

Michael K. Johnson: Writing Linux Device Drivers



Artur Zawlocki




Wyszukiwarka

Podobne podstrony:
Linux714 (3)
Linux735
linux721
Linux712 (4)
Linux736 (4)
Linux736 (3)
Linux722 (4)
Linux713 (2)

więcej podobnych podstron