Do tematu: Podsystem wejścia-wyjścia

Do tematu: Struktury danych

Struktura tty_struct
  1. Pola używane przez dyscyplinę linii N_TTY
  2. O liczbach magicznych
  3. O numerach urządzeń
  4. Flagi ogólne
  5. Flagi dla trybu pakietowego
Struktura tty_struct jest podstawową strukturą opisującą urządzenie terminalowe. Zdefiniowana jest w pliku include/linux/tty.h. Umożliwia dostęp do wszystkich informacji potrzebnych podczas pracy z urządzeniem. Każdemu otwartemu urządzeniu terminalowemu odpowiada dokładnie jedna struktura tty_struct. Ponieważ jest ona tworzona dynamicznie, istnieje tylko wówczas, gdy urządzenie jest otwarte. Dlatego pewne stałe parametry pracy urządzenia terminalowego pamiętane są w innych strukturach, np. termios.
 
typ nazwa pola opis
int magic Liczba magiczna
struct tty_driver driver Sterownik urządzenia terminalowego
struct tty_ldisc ldisc Dyscyplina linii
struct termios* termios Stałe parametry pracy urządzenia terminalowego
struct termios* termios_locked Jeśli proces, wywołując funkcję ioctl, chce zmienić parametry pracy urządzenia terminalowego, tj. zawartość struktury termios, do której wskaźnik pamiętany jest w polu o tej samej nazwie, to przeprowadzenie zmian zależy od zawartości innej struktury termios, a mianowicie tej, do której wskaźnik jest pamiętany w polu termios_locked. Zmiana wartości bitu (ustawienie lub wyzerowanie flagi) w strukturze wskazywanej przez termios jest dokonywana tylko wtedy, gdy odpowiadający bit w strukturze wskazywanej przez termios_locked jest wyzerowany, tzn. dopóki jakiś bit w strukturze *termios_locked jest ustawiony, zmiana wartości odpowiadającego mu bitu w strukturze *termios nie jest możliwa. Aby zmienić zawartość struktury *termios, trzeba najpierw wyzerować odpowiednie bity w strukturze *termios_locked. Zasada ta dotyczy nie tylko flag, ale wszystkich pól struktury termios, a więc również znaków sterujących i dyscypliny linii.
int  pgrp Identyfikator grupy terminalowej. 

Grupa terminalowa, to grupa procesów, której identyfikator jest równy polu pgrp. Zazwyczaj, chociaż nie zawsze, proces należy do grupy terminalowej swojego terminala sterującego. Terminalem sterującym procesu jest urządzenie terminalowe opisywane przez pole tty w strukturze task_struct. Terminalem sterującym grupy procesów jest pierwsze urządzenie terminalowe otwarte przez przywódcę tej grupy. Zwykle przywódcą grupy procesów jest proces shell, a terminalem sterującym terminal, z którego użytkownik rozpoczął pracę. 

int  session Identyfikator sesji. 

Jest on równy identyfikatorowi sesji przywódcy grupy terminalowej danego terminala. 

kdev_t device  Numer urządzenia
unsigned long flags Flagi
int  count Liczba otwartych plików specjalnych, umożliwiających dostęp do danego urządzenia terminalowego. 

Licznik ten jest zwiększany podczas otwierania urządzenia terminalowego (w funkcji init_dev() zdefiniowanej w pliku tty_io.c), zmniejszany podczas zamykania (w funkcji release_dev()). W przypadku pseudoterminali otwieranie polega na otwarciu części nadrzędnej i podrzędnej. Odbywa się to podczas pojedynczego wykonania funkcji init_dev(). Wówczas zwiększana jest o jeden wartość licznika count w części podrzędnej. Ponieważ część nadrzędna nie może być otwarta przez więcej niż jeden proces na raz, wartość licznika count w części podrzędnej jest zawsze o jeden większa od liczby odpowiadających jej otwartych plików specjalnych. Takie postępowanie zapewnia, że część podrzędna zamykana jest tylko wówczas, gdy zamykana jest jej część nadrzędna. 

struct winsize winsize Wymiary okna dla terminali
unsigned char  stopped: 1, hw_stopped: 1, packet: 1 Pole stopped ma wartość 1, jeśli urządzenie terminalowe jest zatrzymane, wartość 0, jeśli nie jest zatrzymane. Mówimy, że urządzenie jest zatrzymane, jeśli w wyniku wprowadzenia przez użytkownika znaku zatrzymania, sterownik przestał wysyłac dane do terminala. Po nadejściu znaku rozpoczęcia, dane ponownie będą wysyłane. 

Pole hw_stopped (ang. hardware stopped) ma wartość 1, jeśli urządzenie terminalowe jest zatrzymane, wartość 0, jeśli nie jest zatrzymane. Pole to jest używane, jeśli dostępne jest sprzętowe sterowanie przepływem danych. 

Pole packet używane jest, gdy urządzenie terminalowe jest częścią nadrzędną pseudoterminala. Proces może przełączyć tryb pracy pseudoterminala na pakietowy wywołując funkcje ioctl z poleceniem TIOCPKT. Wówczas pole packet otrzymuje wartość 1. Praca w trybie pakietowym polega na przekazywaniu procesom pewnych informacji dotyczących części podrzędnej pseudoterminala. Jeśli proces wykonuje funkcję read w odniesieniu do części nadrzędnej pseudoterminala, to w pierwszym bajcie bufora umieszczana zostaje informacja. Jeśli bajt ten ma wartość zero, to wczytane zostały dane. Jeśli ma wartość różną od zera, to wczytany został tylko ten jeden bajt. Wartości, które może przyjmować są zdefiniowane w postaci flag. Znaczenie flag podane zostało przy opisie pola ctrl_status

unsigned char ctrl_status Pole to służy do pamiętania stanu części podrzędnej pseudoterminala, o ile odpowiadająca jej część nadrzędna pracuje w trybie pakietowym. Jeśli użytkownik wykonuje operację czytania z części nadrzędnej pseudoterminala, to pierwszemu bajtowi bufora w przestrzeni użytkownika jest nadawana wartość pola ctrl_status. Wartości poszczególnych bitów tego pola odpowiadają ustawieniu, bądź wyzerowaniu odpowiednich flag.
struct tty_struct * link Jeśli struktura tty_struct opisuje urządzenie, które jest jedną z części pseudoterminala, to zmienna link jest wskaźnikiem na drugi element pary. 
struct fasync_struct*  fasync Lista plików
tty_flip_buffer flip Bufor sterownika urządzenia terminalowego
int max_flip_cnt Obecnie nieużywany
struct wait_queue* write_wait Kolejka procesów czekających na pisanie do urządzenia terminalowego
struct wait_queue* read_wait Kolejka procesów czekających na czytanie z urządzenia terminalowego 
void * disc_data W polu tym zapamiętują prywatne dane niektóre sterowniki sieciowe
void * driver_data Służy do zapamiętywania prywatnych danych sterowników urządzeń terminalowych. Zazwyczaj sterownik opisywany jest przez pewną strukturę, której definicja zależy ściśle od rodzaju urządzenia. Np. dla konsoli zdefiniowana jest struktura vt_struct, dla pseudoterminali pty_struct. Wówczas wskaźnik do takiej struktury pamiętany jest w polu driver_data. 
Początek pliku
 
Pola używane przez dyscyplinę linii N_TTY 

(Ze względów historycznych stanowią część struktury tty_struct)

unsigned int column liczba mówiąca, w której kolumnie będziemy wypisywać bieżący znak na terminal
unsigned char lnext:1 mówi, czy poprzednio otrzymanym przez dysc. linii znakiem był znak VLNEXT (literal next)
unsigned char erasing:1 mówi, czy aktualnie jesteśmy w trybie obsługi znaków ścierania 
unsigned char raw:1, real_raw:1 raw - wartość 1 oznacza, że terminal jest w trybie surowym (nie przetwarzane są znaki końca linii, ścierające), ale że znaki powodujące wysyłanie sygnałów są obsługiwane. raw jest ustawiany na 1 lub 0 w zależności od tego, jakie flagi są związane z terminalem. 

real_raw - wartość 1 oznacza, że dyscyplina linii nie będzie obsługiwać żadnych znaków specjalnych (nie będzie też możliwe wysyłanie sygnałów za pomocą znaków z klawiatury). Parametr ten jest ustawiany na 1, jeśli m.in. włączone są następujące flagi: IGNBRK, IGNPAR, oraz driver terminala ma ustawioną flagę TTY_DRIVER_REAL_RAW 

Flagi 

unsigned char icanon:1 ustawione na 1, jeśli terminal pracuje w trybie kanonicznym, czyli znaki wejściowe są przetwarzane, jest obsługa końca linii, znaków ścierających. icanon jest ustawiane na 1, m.in jeśli jest ustawiona flaga ICANON 
unsigned char closing:1 mówi, czy jesteśmy w trakcie zamykania dyscypliny linii
unsigned short minimum_to_wake ilość znaków w buforze wystarczająca, aby obudzić procesy czekające na czytanie z terminala (patrz pole read_wait)
unsigned overrun_time czas, w którym przybył pierwszy z serii znak z flagą TTY_OVERRUN
int num_overrun ile znaków w ciągu HZ tyknięć zegara przybyło z flagą TTY_OVERRUN
unsigned long process_char_map [256/(8*sizeof(unsigned long)) ] tej tablicy używa się tylko wtedy, gdy terminal nie jest w trybie surowym (raw = 0, real_raw = 0). Tablica ta traktowana jest jak ciąg bitów. Te bity sprawdza się i ustawia. Bit o numerze odpowiadającym znakowi sterującemu jest ustawiony, jeśli ten znak jest obsługiwany przez terminal (o czym mówią flagi). Wypełnianie tej tablicy jest dokonywane w funkcji n_tty_set_termios, która jest też wywoływana przy otwieraniu dyscypliny linii N_TTY
char * read_buf bufor dyscypliny linii, jest alokowany dynamicznie podczas wywołania funkcji open dla dysc.linii. Bufor jest traktowany jak cykliczna kolejka znaków
int read_head pozycja w buforze char_buf, gdzie znajduje się początek danych
int read_tail pozycja w char_buf oznaczająca, że tam jest koniec danych
int read_cnt ilość znaków znajdująca się w buforze
unsigned long read_flags [N_TTY_BUF_SIZE/(8* sizeof(unsigned long)) ] traktuje się tę tablicę jak ciąg bitów. Bity ustawia się na 1, jeśli na pozycji równej numerowi tego bitu w tablicy read_buf znajduje się koniec linii (ostatni znak w linii). Przy otwieraniu dyscypliny linii tablica jest zerowana
Następujące pola używane są tylko wtedy, kiedy icanon = 1
int canon_data liczba znaków buforowanych w read_buf
unsigned long canon_head mówi gdzie jest początek znaków obsługiwanych w trybie kanonicznym (bufor read_buf)
unsigned int canon_column numer kolumny (tryb kanoniczny)
Początek pliku
Liczby magiczne

Liczba magiczna (ang. magic number) jest unikatową liczbą, przypisywaną do typu struct, pozwalającą zidentyfikować dany typ. Przykładowe definicje dla tty_struct, tty_driver oraz tty_ldisc są następujące:

TTY_MAGIC 0x5401

TTY_DRIVER_MAGIC 0x5402

TTY_LDISC_MAGIC 0x5403

Liczby magiczne są przede wszystkim definiowane po to, aby ułatwić wykrywanie błędów w kodzie jądra. Szczególnie przydatne okazują się tam, gdzie mamy do czynienia z wielokrotnym przekazywaniem i rzutowaniem wskaźników do struktur. Dzięki liczbom magicznym możemy w prosty sposób sprawdzać poprawność przekazywanych do funkcji wskaźników.

Jeśli rozbudowujemy kod jądra, dodając m. in. nową strukturę i chcemy skorzystać z pomysłu magicznych liczb, to definiujemy swoją liczbę magiczną, a pierwszemu polu naszej struktury nadajemy nazwę magic i typ int. Wartością tego pola, nadawaną przy inicjalizacji, zawsze powinna być zdefiniowana przez nas liczba magiczna.

Dobry obyczaj wymaga, abyśmy swoją liczbę magiczną umieścili w pliku magic-number.txt, gdzie znajdują się wszystkie definicje liczb magicznych. Takie postępowanie pozwala zachować unikalność tych liczb.

Początek pliku

Numer urządzenia

Numer urządzenia jest liczbą dodatnią. Liczba ta, zdefiniowana dla każdego urządzenia, jednoznacznie identyfikuje urządzenie w systemie operacyjnym Linux. Typ kdev_t jest zdefiniowany następująco:

typedef unsigned short kdev_t;

Numer urządzenia może zostać utworzony tylko w wyniku wykonania makra MKDEV.

#define MINORBITS 8

#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))

Makro to koduje numer główny i drugorzędny urządzenia w jednej liczbie (nazwy argumentów makra, ma oraz mi, są skrótami angielskich słów major - główny oraz minor - drugorzędny). Często używanymi makrami operującymi na numerze urządzenia są MAJOR i MINOR

#define MINORMASK ((1 << MINORBITS) - 1)

#define MAJOR(dev) ((dev) >> MINORBITS)

#define MINOR(dev) ((dev) & MINORMASK)

Obliczają one odpowiednio numer główny i drugorzędny urządzenia o numerze dev (ang. device - urządzenie). Definicje te znajdują się w pliku kdev_t.h.

Pczątek pliku

Flagi
 
flaga kod komentarz
TTY_THROTTLED 0 (ang. throttle - dusić) Jeśli jest ustawiona oraz w wejściowym buforze znaków dyscypliny linii znajduje się mniej niż TTY_THRESHOLD_UNTHROTTLE znaków, to dyscyplina linii wywołuje, zdefiniowaną w sterowniku urządzenia terminalowego, funkcję unthrottle. W ten sposób powiadamia urządzenie, że jest już miejsce w buforze wejściowym na przyjęcie kolejnych znaków. Jeśli nie jest ustawiona i w wejściowym buforze znaków znajduje się więcej niż TTY_THRESHOLD_THROTTLE znaków, to wywoływana jest funkcja throttle. Jest to wiadomość dla sterownika, że bufor wejściowy dyscypliny linii jest bliski przepełnienia. Np. w przypadku pseudoterminali ostatnią instrukcją funkcji unthrottle jest ustawienie flagi TTY_THROTTLED. Powoduje to, że dyscyplina linii wywołuje funkcję unthrottle za każdym razem, gdy w kolejce wejściowej jest mniej niż TTY_THRESHOLD_UNTHROTTLE znaków. Można wówczas obudzić procesy, które czekają, aby wysłać dane do pseudoterminala lub żeby dane zostały z pseudoterminala wysłane. 
TTY_IO_ERROR 1 Ustawiona informuje o wystąpieniu błedu wejścia-wyjścia. 
TTY_OTHER_CLOSED 2 Dotyczy pseudoterminali. Ustawiona informuje, że drugi element pary wchodzący w skład pseudoterminala (wskaźnik do opisującej go struktury tty_struct jest pamiętany w polu link) jest zamknięty (zawieszony). 
TTY_EXCLUSIVE 3 Procesy mogą zmieniać ustawienia flagi TTY_EXCLUSIVE za pomocą funkcji ioctl. Jeśli flaga ta zostanie dla jakiegoś urządzenia terminalowego ustawiona, to z wyjątkiem procesów nadzorcy, żaden proces nie będzie już mógł otworzyć tego urządzenia. Nie oznacza to wyłączności korzystania przez proces z urządzenia, ponieważ np., zanim flaga została ustawiona, inne procesy mogły już otworzyć to urządzenie. 
TTY_DEBUG 4 Obecnie nieużywana
TTY_DO_WRITE_WAKEUP 5 Flaga ta testowana jest, przede wszystkim, w tych funkcjach sterowników, które wysyłają dane do urządzenia terminalowego (np. write, flush_buffer). Jeśli jest ustawiona, to wywoływana jest funkcja dyscypliny linii write_wakeup, o ile funkcja ta jest zdefiniowana. W dyscyplinie linii N_TTY funkcja write_wakeup nie jest zdefiniowana.
TTY_PUSH 6 Używana przez dyscyplinę linii. 
TTY_CLOSING 7 Ustawiona oznacza, że urządzenie terminalowe jest w trakcie zamykania. Informacja ta jest przechowywana ze względu na konieczność zapobiegania tzw. wyścigom procesów (ang. races). 
Stałe TTY_THRESHOLD_THROTTLE i TTY_THRESHOLD_UNTHROTTLE są zdefiniowane w pliku /drivers/char/n_tty.c następująco:

TTY_THRESHOLD_THROTTLE (N_TTY_BUFSIZE - 128)

TTY_THRESHOLD_UNTHROTTLE 128

Początek pliku

Flagi dla trybu pakietowego
 
flaga kod komentarz
TIOCPKT_DATA 0x00 Informuje o tym, że do bufora w przestrzeni użytkownika zostały wczytane dane.
TIOCPKT_FLUSHREAD 0x01 Ustawiona oznacza, że wejściowy bufor znaków terminala został opróżniony.
TIOCPKT_FLUSHWRITE 0x02 Ustawiona oznacza, że wyjściowy bufor znaków terminala został opróżniony. 
TIOCPKT_STOP 0x04 Ustawiona oznacza, że część podrzędna pseudoterminala jest zatrzymana. Zatrzymanie zostalo spowodowane nadejściem znaku specjalnego VSTOP.
TIOCPKT_START 0x08 Ustawiona oznacza, że część podrzędna pseudoterminala nie jest zatrzymana. Wznowienie pracy nastąpiło po nadejściu znaku specjalnego VSTART
TIOCPKT_NOSTOP 0x10 Ustawiona, jeśli dla części podrzędnej pseudoterminala znakiem zatrzymania nie jest VSTART lub znakiem rozpoczęcia nie jest VSTOP lub część ta pracuje w trybie surowym
TIOCPTK_DOSTOP 0x20 Ustawiona, jeśli dla części podrzędnej pseudoterminala znakiem zatrzymania jest VSTOP, znakiem rozpoczęcia jest VSTART oraz część ta nie pracuje w trybie surowym. 

Autorzy: Anna Petryk, Maciej Kaczmarek