Strumień jedynkowy

Po wielu perypetiach związanych z uruchomieniem „środowiska” do kompilacji modułu jądra, udało mi się uruchomić „Witaj świecie!”. Teraz przyszedł czas na coś bardziej ambitnego. Urządzenie znakowe produkujące jedynki, działające tak samo jak /dev/zero. Czasami takie urządzenie się przydaje. Znacznie rzadziej, ale jak jest potrzebne, to jest problem. Moduł nie niesie ze sobą nic odkrywczego. Typowe urządzenie znakowe. Moduł został wydany w dwóch wersjach:

  • wersja 1.0.0, w pełni funkcjonalny moduł /dev/ones;
  • wersja 1.0.1. przyspieszona wersja /dev/ones.

Moduł implementuje 4 funkcje, które powinno zawierać urządzenie znakowe. Jest to rozsądne minimum. Urządzenie znakowe widziane jest w systemie jako plik zatem::

  • open – funkcja obsługująca otwarcie pliku,
  • read – funkcja obsługująca odczyt pliku,
  • write – funkcja obsługująca zapis do pliku,
  • release – funkcja obsługująca zamknięcie pliku.

Dla urządzenia „plującego” jedynkami sens ma jedynie funkcja read. Funkcje open, write i release nie mają żadnego praktycznego znaczenia, ale wypadałoby, żeby zwróciły coś, co nam jest wygodne. W większości wypadków będzie to „0”, czyli sukces. Pozwoliłem sobie w przypadku funkcji write zwrócić błąd EIO. Wychodzę z założenia, że urządzenie typu /dev/zero czy /dev/random to urządzenie jednokierunkowe, więc chęć zapisu do nich jest bezcelowa, gdyż nie pełni żadnej funkcji.

Większość przykładów „Witaj świecie!” dla urządzeń znakowych funkcja init i exit jest prosta i ogranicza się jedynie do zarejestrowania i wyrejestrowania urządzenia. Niestety taka prosta funkcja nie utworzy nam pliku w gałęzi /dev/ (trzeba z poziomu juzerlandu stworzyć „noda” poleceniem mknod). Żeby urządzenie pojawiło się samo w /dev/ po załadowaniu modułów, trzeba trochę rozszerzyć funkcję init:

  • alokujemy sobie „miejsce” na urządzenie lub urządzenia (tutaj polecam zasięgnąć wiedzę na temat numerów określanych jako major i minor),
  • tworzymy klasę dla urządzenia (pojawi się odpowiedni wpis w /sys/class/),
  • tworzymy urządzenie i inicjujemy jego funkcje (podpięcie naszych funkcji, patrz struktura fileoperations),
  • dodajemy urządzenie

Wydajność przyspieszonego modułu jest podobna do /dev/zero. Wyniki prostego testu dla /dev/ones:

pi@raspberrypi ~/raspberry_pi/modules/dev_ones $ dd if=/dev/ones bs=100M count=1 | hexdump
0000000 ffff ffff ffff ffff ffff ffff ffff ffff
*
1+0 records in
1+0 records out
104857600 bytes (105 MB) copied, 6.21713 s, 16.9 MB/s

Wyniki dla /dev/zero:

pi@raspberrypi ~/raspberry_pi/modules/dev_ones $ dd if=/dev/zero bs=100M count=1 | hexdump
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
1+0 records in
1+0 records out
104857600 bytes (105 MB) copied, 6.04321 s, 17.4 MB/s

Zatem nieźle. Wersja pierwsza była mniej wydajna, w powyższym teście była o 2MB/s wolniejsza. Podejrzewam, że nawet przyspieszona wersja nie jest tak wydajna jak /dev/zero – gdyby to do przetestować dogłębnie, ale taka wydajność jak najbardziej wystarcza do praktycznej pracy z modułem.