2012-03-06 11 views
23

Dowiedziałem się o blokujących funkcjach wejścia/wyjścia do pisania sterownika urządzenia linux i zastanawiam się, jakie jest użycie ERESTARTSYS. Rozważmy następujący:Do czego służy ERESTARTSYS podczas pisania sterownika Linux?

zmienna globalna:

wait_queue_head_t my_wait_q_head; 
int read_avail = 0; 


device_init():

init_waitqueue_head(&my_wait_q_head);


device_read():

printk("I'm inside driver read!\n"); 
wait_event_interruptible(&my_wait_q_head, read_avail != 0); 
printk("I'm awaken!\n"); 


device_write():

read_avail = 1; 
wake_up_interruptible(&my_wait_q_head); 


Kiedy wezwać read() z przestrzeni użytkownika, w wierszu polecenia powiesić aż nazywam write() zgodnie z oczekiwaniami. Komunikaty printk pojawiają się odpowiednio również w dmesg. Jednak widzę niektóre sterowniki napisane tak:

Inna wersja device_read():

printk("I'm inside driver read!\n"); 
if(wait_event_interruptible(&my_wait_q_head, read_avail != 0))  
{return -ERESTARTSYS;} 
printk("I'm awaken!\n"); 

Testowałem drugą wersję device_read() przy użyciu tej samej metody w przestrzeni użytkownika, a wynik jest dokładnie taki sam, więc jaki jest pożytek z ERESTARTSYS?

p/s: Czytałam książkę Linux sterowników urządzeń na ten temat, ale ja nie rozumiem, może ktoś podać przykład do eleborate ?:

Po ominąć tej rozmowy coś obudziła nas, ale nie wiemy, co to jest . Jedną z możliwości jest to, że proces otrzymał sygnał. Instrukcja if, która zawiera wait_event_Anterruptible call, sprawdza w tym przypadku . To stwierdzenie zapewnia prawidłową i oczekiwaną reakcję na sygnały, które mogły być odpowiedzialne za pobudzanie procesu (ponieważ byliśmy w przerywanym śnie). Jeśli nadejdzie sygnał i nie został zablokowany przez proces, właściwym zachowaniem jest zezwolenie górnej części jądra na obsługę zdarzenia. W tym celu do sterownik zwraca -ERESTARTSYS do dzwoniącego; ta wartość to używana wewnętrznie przez warstwę wirtualnego systemu plików (VFS), która albo restartuje wywołanie systemowe, albo zwraca -EINTR do przestrzeni użytkownika. Używamy tego samego rodzaju sprawdzania, aby poradzić sobie z obsługą sygnałów dla każdego zapisu i implementacji zapisu .

Źródło: http://www.makelinux.net/ldd3/chp-6-sect-2

Odpowiedz

43

-ERESTARTSYS połączony jest z pojęciem restartowalnym wywołania systemowego. Wywoływalne wywołanie systemowe to takie, które może być w przejrzysty sposób ponownie wykonane przez jądro, gdy występuje pewne przerwanie.

Na przykład proces przestrzeni użytkownika, który śpi w wywołaniu systemowym, może uzyskać sygnał, wykonać procedurę obsługi, a następnie, gdy program obsługi zwraca, wydaje się, że wraca do jądra i nadal śpi na pierwotnym wywołaniu systemowym.

Za pomocą znacznika API interfejsu API POSIX sigaction, procesy mogą aranżować zachowanie ponownego uruchomienia związane z sygnałami.

W jądrze systemu Linux, gdy sterownik lub inny moduł blokujący w kontekście wywołania systemowego wykryje, że zadanie zostało wznowione z powodu sygnału, może zwrócić -EINTR. Ale -EINTR będzie dymić w przestrzeni użytkownika i spowodować, że wywołanie systemowe powróci -1, przy ustawieniu errno na EINTR.

Jeśli wrócisz -ERESTARTSYS, oznacza to, że twoje wywołanie systemowe może być ponownie uruchomione. Kod ERESTARTSYS niekoniecznie będzie widoczny w przestrzeni użytkownika. To albo zostaje przetłumaczone na -1 i ustawia errno na EINTR (potem, oczywiście, widziany w przestrzeni użytkownika), albo jest tłumaczone na zachowanie restartu systemu, co oznacza, że ​​twoja syscall jest wywoływana ponownie z tymi samymi argumentami (przez brak akcji na części procesu użytkownika: jądro robi to, przechwytując informacje w specjalnym bloku restartu).

Zauważ oczywisty problem z "tymi samymi argumentami" w poprzednim paragrafie: niektóre wywołania systemowe nie mogą być uruchomione ponownie z tymi samymi parametrami, ponieważ nie są one idempotentne! Na przykład, przypuśćmy, że przez ułamek sekundy trwa okres uśpienia, wynoszący 5,3 sekundy. Zostanie przerwana po 5 sekundach. Jeśli uruchomi się ponownie naiwnie, będzie spać przez kolejne 5,3 sekundy. Musi przekazać nowe parametry wezwanemu wezwaniu do snu tylko przez pozostałe 0,3 sekundy; tj. zmienić zawartość bloku restartu. Istnieje sposób, aby to zrobić: wprowadzasz różne argumenty do bloku restartu zadania i używasz zwracanej wartości -ERESTART_RESTARTBLOCK.

Aby odpowiedzieć na drugie pytanie: jaka jest różnica? Dlaczego nie wystarczy napisać procedurę odczytu bez sprawdzania zwracanej wartości i zwracania -ERESTARTSYS? Cóż, ponieważ jest to niepoprawne w przypadku, gdy pobudka jest spowodowana sygnałem! Czy chcesz, aby odczyt zwracał 0 bajtów, gdy tylko nadejdzie sygnał? To może zostać źle zinterpretowane przez przestrzeń użytkownika jako koniec danych. Ten rodzaj problemu nie pojawi się w przypadkach testowych, które nie używają sygnałów.

+1

@Noge: Aby zobaczyć różnicę, wyślij procesowi sygnał po wywołaniu 'read()', a przed wywołaniem 'write()'. – caf