- Inicjalizacja zmiennych lokalnych
- Dalsze obliczanie zmiennych lokalnych.
- Zmienną
read
ustawiamy na zero, bo do tej pory nic nie przeczytaliśmy.
- Na zmienną
blocks
obliczamy liczbę bloków które mają być przeczytane. Uwzględniamy przy
tym flagę filp->f_reada
, jeżeli jest ona ustawiona to przy obliczaniu blocks
uwzględniamy
wartość przechowywaną w tablicy read_ahead
. Pilnujemy oczywiście również aby nie wyjść poza urządzenie.
- Sciąganie do tablicy pomocniczej buforów odpowiadających czytanym blokom.
while (blocks) {
// bo jeden blok właśnie będziemy ściągać z pamięci.
--blocks;
*bhb = getblk(dev, block++, blocksize);
// jezeli otrzymany bufor ma nieaktualne dane
if (*bhb && !buffer_uptodate(*bhb))
{
uptodate = 0; // zaznaczamy to
bhreq[bhrequest++] = *bhb;/* dopisujemy do tablicy w której
trzymamy bufory do uaktualnienia */
}
if (++bhb == &buflist[NBUF])
bhb = buflist;
if (uptodate) /* jeśli otrzymaliśmy aktualny
bufor to juz nie robimy tu nic */
break;
if (bhb == bhe) /* jeśli cała nasza tablica bhreq jest
juz pełna to przechodzimy dalej*/
break;
}
- Jak pamiętamy wartością zmiennej blocks
jest ilość bloków które mamy odczytać. Tak więc
jeśli mamy odczytać więcej bloków niż zero to zmniejszamy ilość bloków do odczytania, następnie poprzez
wywołanie funkcji getblk
próbujemy znaleźć bufor odpowiadający interesującemu nas blokowi.
Funkcja ta zwraca nam wskaźnik do bufora lub NULL
. Jeśli nie dostaliśmy NULL
'a sprawdzamy czy
otrzymany bufor jest ''aktualny,, (to znaczy czy dane które zawiera odpowiadają temu co znajduje się
na urządzeniu). Jeśli nie to zaznaczamy to na zmiennej uptodate
, oraz dopisujemy bufor do tablicy
bhreq
aby potem zażądać jego uaktualnienia (przy okazji zwiększamy zmienną bhrequest
gdyż
na niej pamiętamy właśnie ile buforów było ''nieaktualnych,,).
Kolejny warunek odpowiada po prostu za to aby nasza tablica cykliczna się zacykliła. Następne dwie
instrukcje if
sprawdzają kolejno czy otrzymany
ostatnio bufor zawierał aktualne dane, albo czy może przypadkiem zapełniła się nasza lista cykliczna, w
obu przypadkach w przypadku prawdziwości warunku wykonanie pętli jest przerywane.
- Zlecenie uaktualnienia buforów
- Następną rzeczą którą robimy jest ewentualne zlecenie uaktualnienia tych buforów które zawierały
nieaktualne dane. W tym celu wołamy funkcję
ll_rw_block()
. A oto kod który to robi :
if (bhrequest)
{
ll_rw_block(READ, bhrequest, bhreq);
}
- Czekanie na uaktualnienie danych w buforach z naszej pomocniczej tablicy.
- Wieszamy się kolejno na buforach z naszej pomocniczej tablicy czekając aż żądanie odczytu zostanie
zrealizowane. Następnie jeśli bufor nadal nie jest aktualny to znaczy że stało się coś niewłaściwego
i już więcej nic nie będziemy czytać. W przeciwnym przypadku na *buf kopiujemy dane których odczyt
mieliśmy zlecony. Aktualizujemy przy okazji zmienne
read
i *ppos
(zwiększamy o ilość
przekopiowanych do *buf
bajtów), left
(odejmujemy ilość już odczytanych danych).
Zobaczmy kod :
do
{
if (*bhe)
{
wait_on_buffer(*bhe); //czekamy na uaktualnienie
//pierwszego w kolejności bufora
if (!buffer_uptodate(*bhe)) // jeżeli nadal nie jest aktualny
{ /* read error? */
brelse(*bhe); // zaznaczamy ze juz go nie będziemy używać
if (++bhe == &buflist[NBUF])// przesuwamy bhe o jeden (cyklicznie)
bhe = buflist;
left = 0; // nie będziemy już nic więcej czytać
break; // kończymy pętlę
}
}
if (left < blocksize - offset) // jezeli mamy juz przeczytac mniej niz
// odległość między końcem bloku a miejscem w którym znajdują się interesujące
// nas dane
chars = left; // to będziemy kopiować tylko tyle ile nas interesuje
else
chars = blocksize - offset; // wpp. będziemy do buf kopiować wszystko między
// offset a końcem bufora
*ppos += chars; // uaktualniamy pozycję w pliku
left -= chars; // uaktualniamy ilość bajtów do przeczytania
read += chars; // uaktualniamy ilość bajtów przeczytanych
- Kopiowanie danych do pamięci wskazywanej przez buf - poniższy kod nie
wymaga chyba komentarza.
if (*bhe)
{
copy_to_user(buf,offset+(*bhe)->b_data,chars); // kopiujemy dane
brelse(*bhe); // zaznaczamy że nie będziemy już
// używac tego bufora
buf += chars; // teraz będziemy pisac dalej
}
else
{
while (chars-- > 0)
put_user(0,buf++);
}
offset = 0; // następny blok będzie nas interesował
// cały
if (++bhe == &buflist[NBUF]) // przesuwamy bhe
bhe = buflist;
}
while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe)));
- Zakończenie algorytmu - Po wyjściu z głównej pętli funkcji zaznaczamy w buforach
przeczytanych ''z wyprzedzeniem,, że nie będziemy już ich potrzebować (funkcja
brelse
).
Następnie jeśli przeczytaliśmy zero bajtów to zwracamy błąd -EIO
. W przeciwnym wypadku
zwracamy zwracamy ilość odczytanych bajtów, ustawiwszy uprzednio flagę f_reada
dla urządzenia
z którego czytaliśmy. Oto realizujący to kod :
// pętla po buforach odczytanych z wyprzedzeniem
while (bhe != bhb)
{
brelse(*bhe);
if (++bhe == &buflist[NBUF])
bhe = buflist;
};
if (!read) // jeśli nic nie przeczytaliśmy to błąd
return -EIO;
// ustawiamy flagę w pliku reprezentującym nasze urządzenie
filp->f_reada = 1;
return read;