Tworzenie dużych plików

Tworzenie dużych plików

  • Comments 5
  • Likes

Jak można szybko utworzyć duży plik? To zależy, czy jesteś programistą czy administratorem. Administrator użyje fsutil file createnew <nazwa> <rozmiar> a programista używając SetFilePointerEx przesunie kursor w pliku w to miejsce, które określił jako koniec pliku a potem wywoła funkcję SetEndOfFile.
Obie metody mają ten sam skutek i dokładnie tą samą wadę. Skutek jest jasny. W mgnieniu oka, na dysku powstaje plik o zadanym rozmiarze.

A co jest w takim pliku w środku...? I tu sprawa nie jest taka trywialna jak się na pierwszy rzut oka wydaje.

Z jednej strony może się wydawać, że w takim pliku powinno być to, co wcześniej leżało w sektorach na dysku. Bo przecież nie po to błyskawicznie tworzymy plik, żeby później pracowicie wypełniać jego sektory zerami. Poza tym, o ile taka operacja jest dość szybka na plikach mierzonych w kilobajtach, o tyle dla kilku gigabajtów może już potrwać dość długo.
Z drugiej strony, możliwość utworzenia takiego pliku to niezła luka w zabezpieczeniach! Sektory na dysku nie mają praw dostępu i nie ma żadnej gwarancji, że w tym miejscu, w którym tworzymy nasz plik, wcześniej nie leżał skasowany już dokument innego użytkownika. A swój plik można łatwo otworzyć i dane z jego wnętrza dadzą się odczytać...

Jak więc, tak naprawdę, zachowa się system Windows?

Otóż każdy plik w systemie ma oprócz rozmiaru, przypisaną specjalną wartość Valid Data Length czyli liczoną od początku pliku ilość danych, która została wyzerowana. Oznacza to, że plik faktycznie tworzy się błyskawicznie, jednak ilość zainicjowanych danych jest równa 0. Teoretycznie, plik zawiera same zera, jednak faktycznie pojawią się one na dysku dopiero, gdy ktoś spróbuje po nie sięgnąć.  Ponieważ rozwiązanie polega na liczeniu ilości przygotowanych danych, sięgnięcie w pliku w miejsce X, oznacza, że wszystkie informacje od 0 do X zostały zainicjowane. To, co leży poza X, zainicjowane będzie, gdy ktoś tam sięgnie pierwszy raz.

Efekt jest ciekawy: założenie pliku 100GB trwa kilka sekund, a zapisanie (albo nawet odczyt!) na jego końcu jednego bajta - godzinę. Dlatego, że sięgnięcie do tego bajta wymaga przesunięcia aż do niego wskaźnika końca zainicjowanych danych czyli faktycznego zapisania tych wszystkich zer po drodze. Po takim zainicjowaniu, oczywiście kolejne zapisy odbywają się już błyskawicznie.

Praktycznych przykładów nie jest wiele. Rzadko trafia się na duże pliki, których początek nie jest ważny, za to końcówka się liczy. Jednak sytuacje takie się zdarzają i wtedy może być ciekawie. Są to pliki VHD i pliki baz danych. Zwłaszcza VHD jest ciekawy, ponieważ opis struktury pliku znajduje się na jego końcu.

Czy można sobie z tym jakoś poradzić szybciej niż zerując sektor po sektorze? Zwłaszcza wiedząc (a jest tak w przypadku i baz danych i plików VHD) że za chwilę i tak nadpiszemy te dane. Można! Wskaźnik końca zainicjowanych danych daje się "na siłę" przemieścić w dowolne miejsce pliku. Oczywiście prowadzi to do wspomnianego wcześniej naruszenia poufności danych, więc wymaga specjalnego prawa w systemie. Prawo to widoczne jest jako "Perform volume maintenance tasks" w lokalnych zasadach zabezpieczeń.

Dla SQLowców oczywiście ta sama rzecz nazywa się inaczej i w takim przypadku mówią oni o "Instant File Initialization".

Tak czy inaczej, jeżeli trzeba, to można szybko utworzyć duży plik, a później bez oczekiwania na jego wyzerowanie sięgać w dowolne miejsce. Warto o tym wiedzieć, bo osobiście spędziłem kilka ostatnich nocy śledząc, dlaczego plik tworzy się w kilka sekund a pojedynczy bajt w nim zapisuje się kilkadziesiąt minut.

Autor: Grzegorz Tworek [MVP]

 

PS. Stary jak komputery problem "bajtu" czy "bajta" jest bardzo jasno opisany w Słowniku PWN: tylko "bajta" jest poprawnie. Jest to jednak jeden z tych nielicznych przypadków, kiedy słownik nie jest ścisły. Tak naprawdę, obie formy są dopuszczalne i poprawne, więc wyłącznie od upodobań piszącego zależy, która zostanie użyta. Odradzam tylko mieszanie obu form w jednym tekście, bo dziwnie się to wtedy czyta.

Comments
  • Grzesiu, bardzo ciekawy tekst. Rzeczywiście nigdy wcześniej się nie zastanawiałem jak to jest z szybkim tworzeniem dużych plików i późniejszym dostępem do nich :)

    Interesuje mnie jeszcze Twoje PS. Skąd domniemanie - informacja, że obie formy są poprawne. Sprawdziłem jeszcze w wielkim słowniku fleksyjnym i tam też jest tylko bajta. Skąd więc informacja o poprawności drugiej formy? :)

  • @Zygmunt: też z PWN: http://poradnia.pwn.pl/lista.php?szukaj=bajt&kat=18

  • Dzięki. Tego szukałem.

    Warto mieć takie rzeczy na podorędziu... :)

  • a opcja setzerodata w fsutil..

  • Setzerodata to troszkeczkę coś innego, mimo że faktycznie w temacie. Ale to po prostu polecenie, które pozwala na celowe zapisanie zerami fragmentu od X do Y. Bywa użyteczne, ale można go nie użyć. A w normalnej sytuacji inicjowania pliku raczej nie można uniknąć.

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment