Blog literacki, portal erotyczny - seks i humor nie z tej ziemi
Do spisu tresci tematu 6
6.3 Pisanie - funkcja write()
Spis tresci
Objasnienia: slowo dostepu,
struct file.
Funkcja write().
Struktura file_operations.
Funkcja ext2_file_write().
Zrodla informacji
Pytania i odpowiedzi
Objasnienia.
Ponizej przedstawiam znaczenie bitow slowa
dostepu do pliku zapisanego w i-wezle na polu mode.
bit setuid - bit nadawania efektywnego identyfikatora uzytkownika (fsuid)
dla procesu wykonujacego plik,
bit setgid - to samo dla fsgid,
bit przyklejania - gdy 1, to w pamieci zostaje kopia pliku po jego wykonaniu,
typ pliku (4 bity z lewej, ósemkowo):
001 - FIFO
002 - Character Device
004 - Directory
inne: Regular (zwykly), Block Device, Link, Socket.
Przyda sie jeszcze znajomosc czesci
struktury file:
struct file {
mode_t f_mode; /* tryb otwarcia pliku (pisanie/czytanie) -
to nie jest ten sam tryb, co w i-wezle (patrz wyzej) */
loff_t f_pos; /* pozycja w pliku */
struct file_operations *f_op; /* operacje
na pliku w konkretnym file-systemie */
unsigned short f_flags; /* czy plik ma szczególne wlasciwosci
(tylko do nadpisywania, niezmienialny, do pisania synchronicznego lub inne)
- dotyczy konkretnego otwarcia pliku, a nie wspolnego dla wszystkich otwarc
i-wezla*/
struct file *f_next, *f_prev; /* wskaznik do poprzedniego i
nastepnego pliku w tablicy plikow, ktora wcale nie jest tablica, ale lista
dwukierunkowa. Jadro trzyma wskaznik do pierwszego pliku. Maksymalny rozmiar
tej listy to 1024 = NR_FILE = maksymalna liczba plikow otwartych w systemie
*/
struct inode *f_inode; /* i-wezel odpowiadajacy plikowi */
... (i inne, dla mojego tematu nieistotne)
}
Ponizej przedstawiam funkcje (jedna) z wirtualego
file-systemu. Oznacza to, ze jest ona niezalezna od file-systemu, ktory
sobie zainstalowalismy.
Funkcja write()
Sluzy do pisania do pliku (w tym rowniez gniazda, gdyz gniazdo jest
typem pliku).
DEFINICJA: int write(unsigned int fd, char *buf, unsigned int licznik)
WYNIK: liczba zapisanych bajtow lub blad.
DANE: deskryptor pliku (fd), wskaznik do danych do zapisania (buf),
ilosc bajtow do zapisania (licznik).
Algorytm pisania:
{
Sprawdz w tablicy plikow prawo do pisania (pole file->mode);
Jezeli licznik=0, zwroc zero;
Upewnij sie, ze:
nie ma blokady na rekord do zapisania (locks_verify_area);
mozemy czytac z obszaru pamieci wskazywanego przez buf o dlugosci
licznik (verify_area);
Jesli proces nie ma euid=0 (effective user id), czyli nie jest jednym
z superuserow, to:
skasuj bit setuid;
jezeli bit setgid jest ustawiony, a grupa nie ma prawa wykonywania
(odpowiedni bit w slowie dostepu do pliku w i-wezle jest zerem), to zostaw
bit setgid, gdyz plik jest kandydatem do obowiazkowego blokowania (mandatory
locking), w p.p. skasuj bit setgid (utrata nadawania efektywnego identyfikatora
grupy dla procesu przy pisaniu do pliku);
Opusc semafor w i-wezle (zabezpieczenie przed jednoczesnym pisaniem);
Wywolaj write dla konkretnego file-systemu lub, jesli mamy do czynienia
z gniazdem, to write dla gniazda - po prostu funkcje ze struktury file_operations;
Podnies semafor;
}
Kazdy filesystem ma swoja strukture file_operations,
w której okreslone sa charakterystyczne dla niego operacje
na plikach:
struct file_operations {
int (* lseek) (struct inode *, struct file *, off_t, int);
int (* read) (struct inode *, struct file *, char *, int);
int (* write) (struct inode *, struct file *, const char
*, int);
int (* open) (struct inode *, struct file *);
i inne ... (plik fs.h)
}
Do struktury tej prowadzi wskaznik z kazdego pliku w tablicy plikow.
Po opuszczeniu semafora, w file-systemie
Ext2 wykonywana przez jadro jest:
Funkcja ext2_file_write()
DEFINICJA: int ext2_file_write(inode *inode, file *file, char *buf,
int licznik)
WYNIK: liczba zapisanych bajtow lub blad.
DANE: plik oraz odpowiadajacy mu i-wezel, buf i licznik - to wskaznik
i rozmiar obszaru w pamieci z danymi (te same, co w funkcji write).
Algorytm pisania, c.d. :
{
Upewnij sie, ze:
file-system nie jest zamontowany read-only;
jestesmy plikiem regularnym, tzn. nie gniazdem, katalogiem, FIFO,
etc.
Jesli plik jest tylko do nadpisywania (mówi o tym pole f_flags
w strukturze file), to zacznij pisac na koncu pliku, w p.p. zacznij od
aktualnej pozycji pliku;
Oblicz numer bloku logicznego, w którym znajduje sie pozycja
w pliku, na której bedziemy pisali, oraz offset (to jest tak, ze
pozycje mozna traktowac jako adres: bity mowiace o numerze bloku i o przesunieciu);
while (licznik>0)
{
Jesli wyszlismy poza pozycje nr 2GB, to zakoncz petle, a na wartosc
wynikowa przypisz blad;
Pobierz bufor odpowiadajacy logicznemu blokowi i-wezla ( getblk()
);
Liczba bajtów do zapisania := min{liczba bajtów od
offsetu do konca bloku, licznik}. Chodzi o to, ze piszemy albo do konca
bloku, albo mniej. Urzadzenie blokowe potrafi zapisac tylko caly blok,
a nie pojedynczy bajt.
Odlicz od licznika liczbe bajtow pozostalych do konca bloku;
Jezli bufor nie jest aktualny i nie mamy go calego zapisac (po to
obliczalismy liczbe bajtow do zapisania), to wczytaj odpowiadajacy mu blok
z dysku (funkcja ll_rw_block(READ,...));
Skopiuj dane w pamieci z (argumentu funkcji) 'buf' do pobranego
bufora;
Zaktualizuj Virtual Memory Cache odnosnie danego bloku - pobierz
strone, zapisz do niej dane z bloku i zwolnij ja ( funkcja update_vm_cache()
);
Przesun pozycje do pisania w pliku oraz miejsce pobierania danych
w 'buf';
Zaznacz, ze bufor jest aktualny i nie jest brudny (brudny oznacza,
ze co innego jest na dysku, niz w pamieci, oraz dane w pamieci sa lepsze);
Jesli plik jest do pisania synchronicznego,
to zapisz bufor do tymczasowej tablicy i nie zapisuj go na dysk, dopoki
jej nie zapelnisz (rozmiar tablicy: NBUF =16);
w p.p. (zapis nie synchroniczny) zwolnij bufor;
Jesli tablica jest juz pelna, to zapisz ja cala ( funkcja ll_rw_block(WRITE,...)
) i pozwalniaj bufory;
Zajmij sie nastepnym blokiem w pliku i ustaw w nim offset na 0.
}
Jesli zostaly bufory w tymczasowej tablicy (liczba blokow nie byla
wielokrotnoscia 16), to je pozapisuj i pozwalniaj;
Jesli pozycja, w której skonczylismy pisac jest wieksza niz
rozmiar pliku, to zwieksz jego rozmiar;
Ustaw w i-wezle czas ostatniej modyfikacji pliku (mtime) i i-wezla
(ctime) na obecny;
Ustaw pozycje pliku w tablicy plików na ta, gdzie skonczylismy
pisac;
Zaznacz, ze i-wezel jest brudny.
}
Zrodla informacji
Przede wszystkim pliki zrodlowe Linuxa (tutaj bez moich komentarzy):
include/linux/fs.h
(naglowki funkcji i definicje stalych)
fs/read_write.c
(tresc funkcji write - pierwszy poziom)
fs/ext2/file.c (funkcja
ext2_file_write)
Opracowany przeze mnie kod zrodlowy - z komentarzem do prawie kazdej
linijki jest tu:
write - systemowy (cz.1)
ext2_file_write (czyli cz.2)
Maurice J.Bach "Budowa systemu operacyjnego UNIX", paragraf
5.3.
Pytania i odpowiedzi
Ogolne pytania pana Raczunasa:
1. Z jakich plikow zrodlowych Linuxa korzystali Panstwo przy opracowywaniu
tematu?
Z plikow wymienionych w zrodlach informacji.
2. Jakie struktury danych znalezione w tych plikach uwazaja za najwazniejsze?
Oczywiscie b.wazna jest struktura file i
slowo dostepu do pliku.
3. Jakie znalezione w plikach zrodlowych rozwiazanie programistyczne
uwazaja Panstwo za najciekawsze?
Pisanie z opoznieniem po 16 blokow w funkcji
ext2_file_write.
4. Jakie znalezli (w tej konkretnej wersji Linuxa) ograniczenia
na rozne zasoby systemowe?
Liczba otwartych plikow nie moze byc wieksza niz 1024 (NR_FILE) oraz
liczba plikow otwartych przez jeden proces nie moze byc wieksza niz 256
(NR_OPEN).
Inne pytania:
5. Czy stale O_NONBLOCK i O_NDELAY maja taka sama wartosc? (odnosnie
funkcji open)
Tak.
Autor: Mateusz Hauzer
E-mail: mhauzer@students.mimuw.edu.pl
tego samego autora: funkcja chown.