Rodzaje urządzeń wej/wyj
W systemach UNIXowych występują dwa rodzaje urządzeń wej/wyj:
blokowe: dyski, taśmy, ....
znakowe: terminale, urządzenia sieciowe, ...
Ponieważ wszystko w UNIXie jest plikiem, to również urządzenia wej/wyj są reprezentowane w systemie plików UNIXa przez tzw.
pliki specjalne.
Pliki te jak wszystkie inne pliki w systemie posiadają nazwę i i-węzeł zawierający (także):
prawa dostępu
typ pliku: b oznacza blokowy, c oznacza znakowy Pliki specjalne są tworzone przez administratora np. tak:
mknod /dev/tty13 c 2 13
Pow. oznacza, że administrator utworzył plik specjalny o nazwie
/dev/tty13 reprezentujący urządzenie znakowe o numerze głównym urządzenia 2 (określającym typ urządzenia w odp.
tablicach rozdzielczych) i numerze drugorzędnym 13.
Schemat podsystemu plików
open close read write ioctl mountopen umount readclose write
Tablica rozdzielcza urządzeń
znakowych
Pamięć bufora Tabl. rozdzielcza urz. blok.
open closereadwrite ioctl Podprogram obsługi
urządzeń
Prog. obsługi przerwań
open close strategia Podprogram obsługi
urządzeń
Program obsługi przerwań Wektor przerwań Wektor przerwań
Przerwanie od urządzenia
Tablice rozdzielcze
poz. open close strategia 0 1 gdopen gtopen gdclose gtclose gdstrategia gtstrategia
Tablica rozdzielcza urządzeń blokowych
poz. open close read 0 1 conopen dzbopen conclose dzbclose conread dzbread
Tablica rozdzielcza urządzeń znakowych
write ioctl 2 3 syopen nulldev nulldev nulldev syread mmread 4 5 gdopen gtopen gdclose gtclose gdread gtread conwrite dzbwrite sywrite mmwrite gdwrite gtwrite conioctl dzbioctl syioctl nodev nodev nodev To tylko przykład w Linuxie jest podobnie ale inaczej 4
cd.
Tablice rozdzielcze
W pow. tabeli nulldev oznacza pusty podprogram obsługi:
nie rób nic, natomiast nodev oznacza brak podprogramu obsługi.
Sposób obsługi operacji wej/wyj dla urządzeń specjalnych różni się od obsługi analogicznych operacji wej/wyj dla plików zwykłych.
Algorytm obsługi operacji wej/wyj dla urządzeń specjalnych:
proc. otrzymuje deskr. pliku użytkownika poziom użytkownika
na jego podstawie zagląda do tablicy plików i dalej do tablicy i-węzłów
na podstawie i-węzła jądro ustala typ pliku i
odwołuje się do odpowiedniej tablicy rozdzielczej jądro pobiera z i-węzła główny i
drugorzędny numer urządzenia
na podstawie nru głównego wywołuje odp. procedurę obsługi z tablicy rozdzielczej przekazując procedurze nr drugorzędny
poziom jądra
uwagi
Tablice rozdzielcze
Różnica między wywołaniami procedur wej/wyj dla plików
specjalnych i plików zwykłych polega na tym, że pierwsze nie blokują węzła pliku specjalnego natomiast drugie blokują odp
i-węzeł. Gdyby następowała blokada i-węzła pliku specjalnego, to inne procesy korzystające z tego samego urządzenia spały by z powodu drzemki procesu aktualnie korzystającego z urządzenia. Stąd algorytmy otwierania i zamykania plików specjalnych muszą uwzględniać to, że nie są jedynymi użytkownikami urządzeń.
Zdarza się, że operacje read/write dla urządzeń znakowych
buforują wewnętrznie przesyłane dane. Jeżeli urządzenie zapycha
się, to operacja write powoduje zaśnięcie procesu.
Procedury strategii służą do transmisji danych między podręczną
pamięcią bufora a urządzeniem. Procedura strategii może kolejkować żądania wej/wyj do danego urządzenia na liście
pomocniczej lub wykonać bardziej wyrafinowane kolejkowanie.
ioctl
Tablice rozdzielcze
Funkcja ioctl umożliwia procesom ustawianie sprzętowych opcji poszczególnych urządzeń oraz opcji programowych związanych z podprogramami obsługi urządzeń.
Programy korzystające z ioctl muszą wiedzieć jaki jest rodzaj
pliku, dla którego tę funkcję wywołują - jest to wyjątek w UNIXie od zasady nie rozróżniania typu pliku.
Składnia:
ioctl (deskr_pliku, polecenie, argument) deskr_pliku ustalony przez uprzednie wywołanie open
polecenie predefiniowana stała oznaczająca akcję
argument wskaźnik do odp. struktury, każdy podprogram
obsługi interpretuje ją wg własnej specyfikacji
Ćwiczenie
Zamontuj dyskietkę (mount /dev/fd0 /mnt/floppy). Skopiuj na nią duży plik. Wykonaj umount /mnt/floppy.
Kiedy następuje zapis? Co zrobi odp. procedura strategii? Co się stanie gdy tak samo postąpimy z małym plikiem?
Podprogramy obsługi przerwań
wektory przerwań
ttyintr 0 ttyintr 1 consintr printintr 0płyta tylna
komputera
urządzenia
tty00 tty07 tty08 tty15 console printer00 printer07 8Podprogramy obsługi przerwań
cd.
Jeżeli tty09 wygeneruje przerwanie, to jądro wywoła podprogramobsługi związany ze sprzętowym położeniem tego urządzenia (tu: ttyintr 1).
Wiele urządzeń może być związanych z jednym wektorem przerwań. Podprogram obsługi przerwania jako parametr otrzymuje nr
wektora przerwań (tu: 0 lub 1) oraz informacje z samego
przerwania umożliwiające stwierdzenie, że to tty09 a nie tty15 spowodował przerwanie.
Numer urządzenia używany przez podprogram obsługi przerwania identyfikuje jednostkę sprzętową (np. tty09), numer drugorzędny pliku specjalnego identyfikuje urządzenie na potrzeby jądra
systemu.
Podprogram obsługi przerwania wiąże drugorzędny numer urządznia z odpowiednim numerem jednostki sprzętowej.
Podprogramy obsługi dysków
Programy użytkowe mogą korzystać z surowego (znakowego) lub
blokowego dostępu do urządzeń dyskowych (tak nie jest w Linuxie).
Czytając i pisząc z/na urządzenia blokowe, tak naprawdą czytamy/
zapisujemy z bufora za pośrednictwem podprogramów strategii.
Może to prowadzić do czytania danych uprzednio zapisanych do bufora lecz jeszcze nie zapisanych na dysk.
Procedury dostępu do dysków przez urządzenia znakowe
czytają/piszą bezpośrednio z/do dysku do/z pamięci użytkownika.
Przykład
Załóżmy, że pliki specjalne /dev/dsk15 i /dev/rdsk są
urządzeniami, odpowiednio, blokowym i znakowym z tym samym numerem drugorzędnym (związane z tym samym dyskiem
fizycznym). Przyjrzyjmy się programowi prog1008.c. Kiedy w buforach programu mogą się pojawić różnice?
Pow. program ma szansę zadziałać na UNIXach typu System V - Linux obsługuje urządzenia znakowe trochę inaczej niż reszta UNIXów. 10
TeleType, terminale i pty
Większość urządzeń przeznaczonych do pracy interakcyjnej (nie tylko w systemach UNIXowych) posiada bardzo podobny interfejs odziedziczony po historycznym już szeregowym terminalu
drukującym
TeleType. Interfejs ten, zwany tty, dzięki swej
prostocie posiada szerokie zastosowanie, np.: terminale szeregowe, konsole, X-terminale, połączenia sieciowe, .... .
Próbowano zaimplenetować interfejs tty. Najbardziej znane są:
sgtty pochodzący z systemu BSD - dziś odchodzi się od niego
termio pochodzący z Systemu V
termios pochodzący ze standardu POSIX - nadzbiór termio
Koncepcyjnie interfejs tty wspiera przepływ danych pomiędzy
programem, a urządzeniami (sprzętem), np.: interpreter poleceń i modem lub terminal lub drukarka lub ... .
Instnieje również możliwość podłączenia do obu końcówek interfejsu tty oprogramowania. Nazywamy go wówczas
Terminal
koncepcje
program
A
C
D
sterownik
terminala
B
program A generuje sekwencje znaków wyjściowych na D i interpretuje sekwencje znaków wejściowych z C; A może
wchodzić w interakcję z C i D za pomocą funkcji systemowych
read i write
główna funkcja sterownika terminala B obsługuje transfer danych między programem A a sprzętem C i D; B zwykle składa się z dwóch części: ze sterownika urządzenia (niskopoziomowe
oprogramowanie będące interfejsem sprzętu) i modułu protokołów, zwanego też dyscypliną linii (odwzorowuje jedne sekwencje
Terminal
punkt widzenia jądra
powłoka
użytkownika
dyscyplina
linii
sterownik
urządzeni
a
bufor
wejściowy
bufor
wyjściowy
13Dyscyplina linii
Dyscyplina linii jest wewnętrznym interfejsem jądra służącym do
filtrowania danych tekstowych.
Może ona pracować w dwóch trybach:
kanonicznym: przekształca sekwencje znaków na oczekiwane
przez użytkownika
surowym: brak jakiejkolwiek konwersji Funkcje dyscypliny linii:
dzieli wejściowy ciąg znaków na linie
przetwarza znaki ścierające i kasowania linii
wypisuje na terminalu echo odebranych znaków
rozbudowuje dane wyjściowe, np.: TAB zamienia na ciąg spacji
wysyła do procesów sygnały o zawieszeniu terminala
umożliwia pracę w trybie surowym
We wczesnych latach 70'tych dyscyplina linii znajdowała się w
shellu i edytorach. Ritchie zauważył, że spełnia funkcje potrzebne wielu programom i przeniósł ją do jądra.
Listy znakowe
Listy znakowe są podstawową strukturą danych dla dyscypliny linii.
p
i
c
o
p
l
i
k
.
t
x
t
blok znakowy0 8
5
0
wskaźnik na nast. tabl. znakową początek koniec tablica znakowa16
znaków
Jądro utrzymuje listę wolnych bloków/tablic znakowych i wykonuje następujące operacje:
przydziela podprogramowi obsługi blok znakowy z listy wolnych
zwraca blok znakowy do listy wolnych bloków znakowych
pobiera, usuwa i wstawia znaki z/na listę znakową
usuwa grupy znaków po jednym bloku
Listy znakowe
przykład
Usuwamy z listy znakowej tekst: .txtp
i
c
o
p
l
i
k
0 8
1
0
16
znaków
Dopisujemy do listy znakowej teskt: .wa?ny.txt
p
i
c
o
p
l
i
k
.
w
a
?
n
y
.
0 8
0 3
8
0
24
znaki
t
x
t
16Terminal - tryb kanoniczny
schemat
Proces
dyscypliny linii
surowa lista
znakowa
wyjściowa
lista znakowa
kanoniczna
lista znakowa
moduł
dyscypliny
linii
klawiatura
procesy
systemu
ekran
17Terminal - tryb kanoniczny
opis
Zapisywanie do terminala przez proces:
dyscyplina linii przepisuje znaki z przestrzeni użytkownika do listy wyjściowej aż do wyczerpania danych
dyscyplina linii przetwarza listę wyjściową na kanoniczną i
jeżeli kanoniczna przekroczy ustalony rozmiar to wysyła z niej dane na terminal usypiając inne piszące procesy systemowe
Jeżeli wiele procesów pisze na terminal, to każdy z nich niezależnie wykonuje powyższy algorytm - dane mogą się pomieszać.
program1014.c demonstruje zalewanie wyjściowej listy znakowej przez 18 procesów - procesy wysyłają ponad 64 bajty
jednorazowo, co zmusza jądro do utworzenia conajmniej dwóch bloków znakowych (w Sys. V jeden blok znak. zajmuje 64 bajty) Program ten może nie działać poprawnie na Linuxie z większymi blokami znakowymi lub szybszym modułem dyscypliny linii -
program przedstawia jedynie ideę - dostosowanie go do potrzeb Linuxa zostawiam jako zadanie dla studentów (o ile to możliwe:-)
Czytanie danych z terminala
dyscyplina linii kopiuje znaki z surowej listy znakowej i
przetworzone kopiuje do list: wyjściowej i kanonicznej (by echować klawiaturę na ekranie)
umożliwienie czytania z terminala przez wiele procesów jest z natury niejednoznaczne, jądro stara poradzić sobie z taką
sytuacją najlepiej jak potrafi
z drugiej strony umożliwienie czytania z terminala wielu procesom jest konieczne - w przeciwnym razie procesy korzystające z stdio nie mogły by działać
program1016.c przedstawia rywalizację procesów o linie danych z terminala (procesy czytają po jednej linii). Przykład ten
pokazuje, że procesy powinny synchronizować dostęp do
terminala na poziomie użytkownika, a nie polegać na jądrze. Spróbować w dowolny sposób zsynchronizować procesy czytające z pow. programu - niezbędne informacje nt synchronizacji - patrz
wykład.
Tryb surowy terminala
Znaki mogą być przesyłane do terminala bez obróbki (na surowo). Jądro musi jednak wiedzieć kiedy spełnić żądanie czytania - znak końca wiersza jest teraz zwykłym znakiem.
Jądro wywoła funkcję read, jeżeli z terminala wprowadzi się pewną ustaloną liczbę znaków lub po upływie ustalonego czasu. Tryb surowy jest szczególnie ważny dla programów
ukierunkowanych na ekran takich jak np. vi.
program1017.c przedstawia użycie funkcji ioctl do ustawienia
parametrów terminala związanego z deskryptorem pliku o nr 0 (czyli stdin).
readpass.c przedstawia użycie funkcji biblioteki termios do chwilowego wyłącznia echowania na ekranie znaków
pobieranych z klawiatury
Ćwiczenie
Napisz program przejmujący kontrolę nad terminalem dla realizacji własnych funkcji?
Polecenie stty
UWAGA: Większość ustawień terminala można zmieniać za pomocą
komendy stty np:
stty -a wypisuje na ekranie aktualne ustawienia termios
stty -g > zapis_stty zapisuje do pliku aktualne ustawienia w
formacie gotowym do odczytu (stty $(cat zapis_stty))
stty sane ustawia rozsądne wartości większości parametrów stty erase znak ustawia znak kasowania do tyłu na znak
Inne znaki ustawialne przez stty:
kill usunięcie wszystkich znaków aż do początku linii
intr znak przetwania, powoduje wysłanie sygnału SIGINT, do
programu czytającego z terminala i wszystkich innych procesów rozpoznających ten terminal jako swój terminal sterujący
quit wysłanie do grupy procesów związanych z terminalem sygnału SIGQUIT, skutkiem przeważnie jest tzw. zrzut rdzenia (core
dump) czyli zapisanie na dysku przestrzeni pamięci programu i
cd.
Inne znaki ustawialne przez stty
cd:
eof znak końca strumienia wejściowego do terminala, zwykle jest to znak Ctrl-D
stop znak chwilowo zawieszający wyjście na terminal, zwykle jest to znak Ctrl-S
start znak używany do ponownego uruchamiania wyjścia,
zatrzymanego uprzednio przez Ctrl-S, zwykle jest to znak Ctrl-Q, jeżeli znak Ctrl-S nie niezostał zdefiniowany, to Ctrl-Q jest ignorowany
susp wysyła sygnał SIGQUIT do grupy procesów powiązanych z
terminalem, powoduje zawieszenie bieżącej grupy procesów pierwszoplanowych i umieszczenie ich w tle, zwykle jest to znak Ctrl-Z
Wartości
time
i
min
:
Ustawiane tylko w trybie niekanonicznym. Sterują odczytem wejścia terminala - określają co się stanie, gdy program będzie się starał czytać deskryptor pliku związany z terminalem.
Polecenie stty
Wartości
time
i
min
Przypadki wzajemnych ustawień
time
i
min
:
time = 0
i
min = 0
read zawsze natyczmiast będzie zwracał znaki; jeśli są dostępne jakieś znaki to zostaną zwrócone, jeśli nie read zwróci zero i nie zostaną przeczytane żadne znaki
time > 0
i
min = 0
read zwraca liczbę dostępnych znaków, natomiast jeśli upłynęło
time
dziesiątek sekund i nie ma żadnych znaków na wejściu, tozwraca zero
time = 0
i
min > 0
read będzie czekać, aż zostanie przeczytanych
min
znaków, a potem zwraca ich liczbę; na końcu pliku zwraca zerotime > 0
i
min > 0
read zwraca liczbę odczytanych znaków, gdy odczyta
min
znaków lub gdy między odczytywanymi znakami upłynie czastime
dziesiątek sekund
Biblioteka
termios
termios jest standardowym interfejsem opisanym przez POSIX. Jest on sterowany przez ustawienia wartości w strukturze typu
termios oraz przez wywołania odpowiednich funkcji - oba
zdefiniowane w pliku nagłówkowym termios.h.
Parametry, którymi można wpływać na zachowanie terminala pogrupowane są w następujące tryby: wejściowy, wyjściowy,
sterowania, lokalny i specjalnych znaków kontrolnych. #include <termios.h> struct termios { tcflag_t c_iflag; tcflag_t c_oflag; tcflag_t c_cflag; tcflag_t c_lflag; cc_t c_cc[NCCS]; };
Elementy pow. struktury odpowiadają pięciu typom parametrów wymieniomym powyżej.
Biblioteka
termios
cd.
Bieżące ustawienia terminala wczytujemy do struktury termios wskazywanej przez wsk_termios realizuje następująca funkcja:
int tcgetattr(int despliku,
struct termios *wsk_termios);
Po wprowadzeniu nowych ustawień do struktury wywołujemy funkcję:
int tcsetattr(int despliku, int działanie,
const struct termios *wsk_termios);
Parametr działanie nadzoruje sposób wprowadzania zmian. Możliwe wartości to:
TCSANOW natychmiastowa zmiana wartości
TCSADRAIN zmiana wartości, gdy bieżąca kolejka wyjściowa zostanie opróżniona
TCSAFLUSH zmiana wartości, gdy bieżące wyjście zostanie
zakończone, ale pomija wszelkie dostępne aktualnie wejścia jeszcze nie zwrócone wywołaniem read
patrz plik terminal1.c
Tryby
termios
wejściowe
Tryby te określają sposób w jaki są przetwarzane znaki wejściowe (np. z klawiatury), zanim zostaną przekazane programowi. Makra, które możemy przypisać polu c_iflag struktury termios:
BRKINT generuje przerwanie, gdy na linii zostaną stwierdzone okoliczności powodujące przerwanie
IGNBRK ignoruje na linii okoliczności powodujące przerwanie
ICRNL odwzoruje znak “powrotu karetki” na znak nowego wiersza IGNCR ignoruje odbieranie znaku powrotu karetki
INLCR zamienia znak nowej linii na znak powrotu karetki INGPAR ignoruje znaki z błędem parzystości
INPCK wykonuje sprawdzanie parzystości odbieranych znaków PARMRK zaznacza błędy parzystości
ISTRIP przycina wszystkie nadchodzące znaki
Tryby
termios
wyjściowe
Tryby te określają sposób w jaki są przetwarzane znaki wyjściowe (pochodzące z programu) przed przekazaniem ich do portu
szeregowego lub na ekran. Makra, które możemy przypisać polu c_oflag struktury termios:
OPOST włącza przetwarzanie na wyjściu
ONLCR zamienia wyjściowe znaki nowej linii na pary znaków powrotu karetki i przesuwu o wiersz
OCRNL zamienia wyjściowe znaki powrotu karetki na znaki nowej linii
ONOCR w kolumnie 0 nie będzie żadnych znaków powrotu karetki ONLRET nowa linia oznacza także wykonanie powrotu karetki
OFILL wysyła znaki wypełniające, aby zapewnić odstęp OFDEL jako znaku wypełniającego używa DEL a nie NULL itd ... (patrz man termios)
Tryby
termios
sterowania
Tryby te sterują sprzętowymi charakterystykami terminala. Makra, które możemy przypisać polu c_cflag struktury termios:
CLOCAL ignoruje wszystkie linie statusu modemu CREAD umożliwia odbieranie znaków
CS5, CS6, CS7, CS8 używa pięciu, sześciu, siedmiu, ośmiu bitów w wysyłanych lub odbieranych znakach
CSTOPB dla każdego znaku używa dwóch bitów stopu zamiast jednego
HUPCL zawiesza modem przy zamykaniu (tj. kiedy sterownik terminala stwierdza, że ostatni dekryptor pliku,
odnoszący się do terminala został zamknięty, linie sterujące modemem zostaną zawieszone)
PARENB umożliwia generowanie i sprawdzanie parzystości PARODD ustosuje raczej bity nieparzyste niż parzyste
Tryby
termios
lokalne
Tryby te kontrolują różne charakterystyki terminala. Makra, które możemy przypisać polu c_lflag struktury termios:
ECHO umożliwia lokane echo znaków wejściowych ECHOE wykonuje kombinację [Backspace], [Space],
[Backspace], gdy odbierze ERASE (tylko dla ICANON) ECHOK kasuje linię po znaku KILL (tylko dla ICANON)
ECHONL odpowiada echem na znaki nowej linii (tylko dla ICANON) ICANON umożliwia kanoniczne przetwarzanie wejścia
IEXTEN umożliwia implementowanie specyficznych (dodatkowych) funkcji przetwarzania wej/wyj terminala
ISIG włącza obsługę sygnałów
NOFLSH uniemożliwia zerowanie kolejki po odebraniu sygnałów SIGINT i SIGQUIT
TOSTOP przy próbach zapisu wysyła sygnał SIGTTOU procesom w tle
Tryby
termios
znaki kontrolne
Elementy tablicy c_cc struktury termios zawierają mapowanie znaków tzw. kontrolnych na obsługiwaną funkcję. Tablica ta jest wykorzystywana na dwa sposoby, jeden dla trybu kanonicznego i jeden dla trybu surowego.
Indeksy dla trybu kanonicznego:
VEOF znak EOF VEOL znak EOL VERASE znak ERASE VINTR znak INTR VKILL znak KILL VQUIT znak QUIT VSUSP znak SUSP VSTART znak SUSP VSTOP znak SUSP
Indeksy dla trybu surowego:
VINTR znak INTR VMIN wartość MIN VQUIT znak QUIT
VSUSP znak SUSP VTIME wartość TIME
VSTART znak SUSP VSTOP znak SUSP
patrz raz jeszcze plik readpass.c
termios
przykłady
Zakładamy, że zmienna tdes przechowuje ustawienia terminala.
tdes.c_cc[VQUIT] = \031; znak QUIT ustawiony na Ctrl-Y tdes.c_cc[VMIN] = 64; minimalna liczba znaków
tdes.c_cc[VTIME] = 2; dwie dziesiąte części sekundy tdes.c_lflag &= ~ICANON; wyłącz tryb kanoniczny
Co oznacza ?:
tdes.c_lflag &= ~(ISIG|ECHO|ICANON);
Ćwiczenie
Zademonstruj na przykładzie własnego programu kilka ustawień terminala w trybie kanonicznym i trybie surowym?