Zine.net online

Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc
w Szukaj

mgrzeg.net - Admin on Rails :)

Vista internals - część 1. Zmiany w systemie plików

Stało się. Zainstalowałem Vistę na maszynie wirtualnej – głównie po to, żeby zobaczyć, ‘co też w trawie piszczy’. Mój rekonesans po najnowszym dziecku Microsoftu zamierzam opisać w cyklu kilku tekstów, które pojawią się na blogu.
Wszystko zaczęło się od screencasta, który pojawił się na channel9 jakiś czas temu. Później pomyszkowałem trochę po necie i oczywiście trafiłem na fantastyczną serię tekstów Marka Russinovicha poświęconą nowościom w jądrze Visty. Od tej pory Mark jest moim ‘Wergiliuszem’ po kolejnych sferach systemu. Ale do rzeczy.

Zmiany w systemie plików oraz rejestrze: KTM

Wyobraźmy sobie sytuację, kiedy wypuszczamy poprawkę do naszej aplikacji. Niezależnie od tego, czy inicjatorem była osoba – klient, czy też sama nasza aplikacja, najprawdopodobniej łączymy się ze stroną projektu, ściągamy zestaw aktualizacyjny i aplikacja przystępuje do samoaktualizacji. Przyjmijmy, że aktualizacja zawiera zestaw plików oraz wpisów w rejestrze, które muszą zostać zmienione, aby aktualizacja zakończyła się sukcesem. Wszystko odbywa się prawidłowo do momentu, gdy ma nastąpić podmiana kluczowego komponentu i wtedy nagle ciach – brak zasilania i koniec aktualizacji (inny ciach może obejmować sytuację, gdy po aktualizacji części elementów okazuje się, że z powodów administracyjnych (ACL w systemie plików) nie mamy wystarczających uprawnień do modyfikacji niektórych plików i katalogów). Po włączeniu zasilania klient uruchamia aplikację i – wszystkiego się można domyślić. Jeśli jesteśmy odpowiednio zapobiegliwi, to przed aktualizacją utworzyliśmy kopię wszystkich aktualizowanych elementów; jednak nie jest to standardowe zachowanie (zarówno aplikacji jak i klienta). Oczywiście – telefon do supportu, prośba o pomoc przy aktualizacji, etc. – jednym słowem – kłopot.
A gdyby tak istniał mechanizm ‘wszystko albo nic’, który dokonywałby albo całości update’u, albo wcale go nie przeprowadzał? Do Visty nie istniał żaden mechanizm systemowy wspierający coś takiego – aż w końcu pojawił się Kernel Transaction Manager.

Zajrzyjmy do środka

Czym jest ów KTM? Najkrócej ujmując jest to element systemu (jądra) pozwalający na transakcyjne przeprowadzanie operacji na obiektach jądra. Korzystają z niego zarządcy zasobów (Resource Managers), którzy implementują mechanizmy transakcyjne wspierając się na dostarczonych mechanizmach systemowych. Do najważniejszych RM-ów można z pewnością zaliczyć Transactional NTFS (TxF) oraz Transactional Registry (TxR). Dzięki tym mechanizmom można zebrać zestaw modyfikacji systemu plików i rejestru i opakować je w ramach jednej transakcji. W połączeniu z DTC mamy możliwość przeprowadzenia jednej transakcji zawierającej modyfikacje w systemie plików, bazie danych, etc.
Zanim przejdziemy dalej, muszę przyznać się do jednej rzeczy. Po obejrzeniu wspomnianego screencasta nie szukałem informacji o TxF na stronach MS, ani nigdzie indziej. Przypomniałem sobie DevDays z bodajże 2005 roku i sesję poświęconą WinFS. Obok na półce stoi książka (‘Introducing Microsoft WinFX’ – Brent Rector), którą MS zasponsorował wszystkim uczestnikom, sięgnąłem więc po nią i na stronie 87 znajdujemy:

"WinFS API provides several features cross the entire spectrum of data classes. These features are:
Asynchrony
Transactions
Notifications
Blob/stream support
Cursoring and paging"

Kawałek dalej na tej samej stronie jest odpowiedni rozdział o samych transakcjach z przykładem wykorzystującym BeginTransaction i Commit.
W głowie zapaliła mi się lampka 'WinFS' i na pytanie "Co z WinFS zostało zaimplementowane w Viście?" postanowiłem jak najszybciej sobie odpowiedzieć.
Pomyślałem – 'odpalę program dołączony do screencasta na Viście, zobaczymy co się będzie działo. Czułem, że informacje o tym ‘co się dzieje pod kapturem’ (jak to mówią niektórzy) mogę wydobyć sam, z niewielką pomocą dostępnych narzędzi. I oto, co dostałem.
Jak już wcześniej napisałem, do screencasta dołączony jest przykładowy program pokazujący możliwości TxF. Ściągnąłem go, przekompilowałem na Win XP (dziękować MS, że P/Invoke opiera się na późnym wiązaniu! – inaczej musiałbym użyć NAnta, lub MSBuild na virtualu :) ) i wrzuciłem na Viste. Uruchomiłem Procmon i nie chcąc niczego przegapić, usunąłem wszystkie domyślne filtry. Oczywiście procmon rejestrował zapis do pliku po wykonaniu Write dla TransactedFile oraz coś jeszcze, co nie było dla mnie jasne (Rysunek 1).


Rysunek 1. Zrzut z procmona z zarejestrowaną aktywnością KTM


Zaznaczone na różowo sekcje zawierają odpowiednio zapis do pliku oraz cofnięcie zmian. Dla porządku zaznaczę, że eksplorator nie pokazywał zmian w katalogu aż do samego zatwierdzenia zmian. W przypadku roll-backa plik nie był widoczny w eksploratorze ani przez chwilę.
Zauważmy, że tuż przed zapisem do pliku oraz bezpośrednio po wykonaniu cofnięcia zmian pojawiają się dwa dodatkowe podejrzanie wyglądające pliki metadanych (zaznaczone procmonowym highlightem na seledynowo). To, co mnie zdziwiło, to brak tego typu zapisu po zatwierdzeniu zmian.
W tym miejscu sięgnąłem po narzędzie NTFS File Sector Information Utility (nfi.exe z pakietu OEM Support Tools) i porównałem wyniki jego działania dla Windows XP oraz Windows Vista. Poniżej interesujące nas różnice:

VISTA

XP

File 5
Root Directory
    $STANDARD_INFORMATION (resident)
    $FILE_NAME (resident)
    $INDEX_ROOT $I30 (resident)
    $INDEX_ALLOCATION $I30 (nonresident)
        logical sectors 67105864-67105871 (0x3fff448-0x3fff44f)
        logical sectors 11426352-11426359 (0xae5a30-0xae5a37)
    $BITMAP $I30 (resident)
    Attribute Type 0x100 $TXF_DATA (resident)

File 5
Root Directory
    $STANDARD_INFORMATION (resident)
    $FILE_NAME (resident)
    $OBJECT_ID (resident)
    $SECURITY_DESCRIPTOR (nonresident)
        logical sectors 156280376-156280391 (0x950a638-0x950a647)
    $INDEX_ROOT $I30 (resident)
    $INDEX_ALLOCATION $I30 (nonresident)
        logical sectors 229806432-229806487 (0xdb29160-0xdb29197)
    $BITMAP $I30 (resident)

To samo w postaci zrzutu z winmerge'a:


Rysunek 2. Zrzut z winmerge pokazujący różnice między katalogiem głównym (\) w Windows XP oraz Vista - plik 5 z tablicy MFT


Zapisy plików metadanych kończą się w przypadku XP na File 26 - \$Extend\$Reparse, od 27 zaczynają się ‘normalne’ pliki. W przypadku Visty do pliku 26 istnieje zgodność co do nazw, jednak lista nie zamyka się na pliku 26, tylko 34. Nowości w Viście to:

File 27
\$Extend\$RmMetadata
    $STANDARD_INFORMATION (resident)
    $FILE_NAME (resident)
    $INDEX_ROOT $I30 (resident)

File 28
\$Extend\$RmMetadata\$Repair
    $STANDARD_INFORMATION (resident)
    $FILE_NAME (resident)
    $DATA (nonresident)
    $DATA $Config (resident)

File 29
\$Extend\$RmMetadata\$TxfLog
    $STANDARD_INFORMATION (resident)
    $FILE_NAME (resident)
    $INDEX_ROOT $I30 (resident)
    $INDEX_ALLOCATION $I30 (nonresident)
        logical sectors 43672-43679 (0xaa98-0xaa9f)
    $BITMAP $I30 (resident)

File 30
\$Extend\$RmMetadata\$Txf
    $STANDARD_INFORMATION (resident)
    $FILE_NAME (resident)
    $INDEX_ROOT $I30 (resident)
    Attribute Type 0x100 $TXF_DATA (resident)

File 31
\$Extend\$RmMetadata\$TxfLog\$Tops
    $STANDARD_INFORMATION (resident)
    $FILE_NAME (resident)
    $DATA (resident)
    $DATA $T (nonresident)
        logical sectors 536-2583 (0x218-0xa17)

File 32
\$Extend\$RmMetadata\$TxfLog\$TxfLog.blf
    $STANDARD_INFORMATION (resident)
    $FILE_NAME (resident)
    $DATA (nonresident)
        logical sectors 2584-2711 (0xa18-0xa97)

File 33
\$Extend\$RmMetadata\$TxfLog\$TxfLogContainer00000000000000000001
    $STANDARD_INFORMATION (resident)
    $FILE_NAME (resident)
    $FILE_NAME (resident)
    $DATA (nonresident)
        logical sectors 2712-23191 (0xa98-0x5a97)

File 34
\$Extend\$RmMetadata\$TxfLog\$TxfLogContainer00000000000000000002
    $STANDARD_INFORMATION (resident)
    $FILE_NAME (resident)
    $FILE_NAME (resident)
    $DATA (nonresident)
        logical sectors 23192-43671 (0x5a98-0xaa97)

Wygląda na to, że wszystko już jest jasne. Do implementacji TxF użyto rozszerzeń na poziomie woluminów, a nie dodatkowych strumieni plików (jak sądziłem kompletnie bez sensu na początku).
Właściwie to nie wszystko jest jasne – przy okazji analizy przyuważyłem aktywność w niedostępnym dla zwykłego użytkownika katalogu C:\Windows\ServiceProfiles\LocalService\AppData\Local\Temp\TfsStore\Tfs_DAV z udziałem procesu svchost.exe (Rysunek 3). Jeśli ktoś ma jakieś dobre wytłumaczenie, o co chodzi, to zapraszam do komentarzy.


Rysunek 3. Nietypowa aktywność klienta web skorelowana z operacjami w ramach transakcyjnego NTFS


W tym momencie zainteresowałem się obszarem $RmMetadata i tak trafiłem na cykl Marka Russinovicha.
Dla porządku dodam, że do implementacji Transactional Registry (TxR) wykorzystano ‘normalny’ katalog w systemie plików - %SystemRoot%\System32\Config\TxR, a za mechanizm logowania w obu przypadkach (TxR oraz TxF) odpowiedzialny jest CLFS (Common Log File System).

OK – fajna sprawa, ale jak z tego skorzystać w moim programie?

Przejdźmy teraz do sekcji ‘developerskiej’. Transakcje realizowane są w jądrze, jednak API umożliwia korzystanie z tego mechanizmu w trybie user mode. W MSDN opisany jest zbiór funkcji dostępnych z poziomu C oraz C++ (nowe oraz istniejące, zmodyfikowane), jednak nie ma interfejsu zarządzanego, dostępnego w .NET Framework. Przykładowe użycie funkcji z bibliotek ktmw32.dll oraz kernel32.dll poprzez P/Invoke obejmuje zazwyczaj:
CreateFileTransacted, CopyFileTransacted, DeleteFileTransacted, FindFirstFileTransacted, MoveFileTransacted (kernel32.dll), CreateTransaction, CommitTransaction oraz RollbackTransaction (ktmw32.dll) oraz opakowanie interfejsu IKernelTransaction i jego metody GetHandle.
Tu mogę polecić:

  1. artykuł z blogu B#.NET Blog – pierwsza część – pokazuje wykorzystanie mechanizmów low-level dostarczanych z KTM, druga – z wykorzystaniem DTC oraz System.Transactions.
  2. artykuł na CodeProject w którym przedstawiony przykład ilustruje oba mechanizmy (DTC oraz natywny KTM) równolegle.
  3. najlepszy jednak – z punktu widzenia użyteczności – jest przykład dołączony do wspomnianego wcześniej screencasta. Zawiera kompletne opakowanie wspomnianych funkcji w wygodne w użyciu klasy. I właśnie połączenie System.Transactions z KTM stanowi najlepsze zestawienie, dzięki czemu wykonanie dwóch transakcji – jednej zakończonej zatwierdzeniem zmian, drugiej – anulowaniem może wyglądać następująco:

using System;
using System.IO;
using System.Transactions;
using Microsoft.KtmIntegration;

namespace TxFConsole
{
  class Program
  {
    static void Main(string[] args)
    {
      string file1 = "filetxf1.txt";
      string file2 = "filetxf2.txt";
      //pierwszy zapiszmy i zatwierdzmy zmiany
      using (TransactionScope trans = new TransactionScope())
      {
        using (TextWriter writer = new StreamWriter(
          TransactedFile.Open(file1,FileMode.Create,FileAccess.Write,FileShare.None)))
          writer.WriteLine("pierwszy plik!");
        Console.WriteLine("Tu juz jest po zapisie, ale pliku jeszcze nie widac w eksploratorze!");
        Console.WriteLine("Wcisnij jakis klawisz jak sie upewnisz :)");
        Console.ReadKey();
        trans.Complete();
      }
      //drugi zapiszmy, ale cofnijmy zmiany
      using (TransactionScope trans = new TransactionScope())
      {
        using (TextWriter writer = new StreamWriter(
          TransactedFile.Open(file2, FileMode.Create, FileAccess.Write, FileShare.None)))
          writer.WriteLine("drugi plik!");
        Console.WriteLine("Tu juz jest po zapisie, ale pliku jeszcze nie widac w eksploratorze!");
        Console.WriteLine("Tym razem zrobimy rollback, wiec go nie zobaczysz :)");
        Console.ReadKey();
        //nie ma .Complete, wiec using rozwija Dispose i po transakcji
      }
    }
  }
}

Oczywiście do prawidłowego działania należy dodać referencje do System.Transactions.dll oraz KtmIntegration.dll (Microsoft.KtmIntegration)
Na zakończenie tej części jeszcze kilka słów odnośnie WinFS. Nie zamierzam w tym miejscu rozliczać MS z obietnic dotyczących zmian w systemie plików – po prostu wyszło na to, że przy projektowaniu Visty MS poszedł nieco dalej i przygotował obsługę transakcji na znacznie niższym poziomie niż sam system plików.

Opublikowane 16 kwietnia 2007 16:17 przez mgrzeg
Filed under: ,

Powiadamianie o komentarzach

Jeżeli chciałbyś otrzymywać email gdy ta wypowiedź zostanie zaktualizowana, to zarejestruj się tutaj

Subskrybuj komentarze za pomocą RSS

Komentarze:

 

Tarciu said:

Tak z ciekawości - na jakiej maszynie uruchomiłeś wirtuala z Windows Vista? :)

kwietnia 16, 2007 21:21
 

mgrzeg said:

Tarciu, odpowiem Ci, choc chce uniknac bardzo popularnych obecnie dyskusji o wydajnosci / funkcjonalnosci Visty. Jak dla mnie jest to po prostu system operacyjny z wieloma interesujacymi rozwiazaniami.

Zaskocze Cie - sprzet jest skladakiem z 1.5 GB RAMu, 2.4 GHz P4 na dosyc popularnej wowczas plycie asusa i na jednym satanie 120GB (drugi mialem w mirorze, ale rozpialem go bo i tak trzymam wszystko co istotne w repozytorium). Zadne aj-waj, sprzet sprzed prawie 4 lat. Teraz mamy pare barterowych cudakow i moj kierownik zalapal sie na wymiane - a ja mam 'po nim' :).

Na virtuala przeznaczylem 768 mega, wiec trzeba uwazac na sporadyczne stronicowanie (plik maszyny wirtualnej puchnie wtedy niemilosiernie, kompaktowanie nic nie daje!). Na razie nie robie tam nic specjalnego - jak to juz napisalem - chce 'obmacac' system.

kwietnia 16, 2007 21:58
 

Tarciu said:

Sprzęt faktycznie nie jakiś kosmiczny, więc nie jest jeszcze z tą Vistą najgorzej :)

No, ale chciałeś uniknąć dyskusji na ten temat, więc tu stawiam kropkę :)

kwietnia 16, 2007 22:31

Co o tym myślisz?

(wymagane) 
(opcjonalne)
(wymagane) 

  
Wprowadź kod: (wymagane)
Wyślij

Subskrypcje

W oparciu o Community Server (Personal Edition), Telligent Systems