1. [RUN! PC] 2010 五月號 - TxF讓檔案系統也能達到交易控制

     

    image 

    五月號就刊出來,還真有點意外 :D,這次稿件趕不及,晚了幾天才交出去,編輯大人還是讓我上五月號啦,真是感謝 :D

    之前執行緒的系列,是打算把各種應用執行緒的演算法都介紹一下,寫了五篇就沒靈感了。實際寫CODE的技巧倒是很多可以介紹,不過 .NET FX 4.0 出來之後,這些鎖碎的 coding 技巧又大幅簡化了,除非有特別的演算法需要 (如同之前那五篇 :D),否則還自己拿 Thread 物件硬幹已經沒什麼意義了,所以就換另一個我有興趣的主題 - Transactional NTFS 來寫。

    這系列第一篇出爐了,主要就是先介紹它的觀念及如何入門,國內這類資訊還不多,我就野人獻曝寫了一篇,試著寫看看了。較鎖碎的實作技巧 (如 P/Invoke) 我會直接貼 BLOG,而較完整的概念及實作探討等等就會整理成文章拿來投稿了。

    再次感謝各位支持啦,底下有範例程式跟一些參考資源的 LINK,需要的歡迎取用 :D

     

    範例程式:

    1. Visual Studio 2008 Project (C#) File: TransactionDemo.zip

     

    參考資訊:

    1. AlphaFS: Brining Advanced Windows FileSystem Support to .NET
      http://alphafs.codeplex.com/
    2. MSDN magazine (July 2007): Enhance Your Apps With File System Transactions
      http://msdn.microsoft.com/en-us/magazine/cc163388.aspx
    3. B# .NET BLOG: Windows Vista - Introducing TxF In C#
      Part 1: Transacted File Delete
      Part 2: Using System.Transactions and the DTC
      Part 3: CreateFileTransacted Demo
    4. Code Project: Windows Vista TxF / TxR
      http://www.codeproject.com/KB/vista/KTM.aspx
    5. BLOG: Because we can
      http://blogs.msdn.com/because_we_can/archive/2005/05/18/419809.aspx
      Discussion and explanation relating to the Transactional NTFS feature coming in Longhorn, plus any other interesting anecdotes...
    6. Performance Consoderations for Transactional NTFS
      http://msdn.microsoft.com/en-us/library/ee240893(VS.85).aspx
    7. When to Use Transactional NTFS
      http://msdn.microsoft.com/en-us/library/aa365738(VS.85).aspx

    2010/05/05 RUN! PC 專欄文章 .NET RUN! PC Transactional NTFS 作品集 作業系統 技術隨筆 物件導向

  2. [RUN! PC] 2010 四月號 - 生產者vs消費者– 執行緒的供需問題

    image 

     

    隔了足足一年,趁著過年,生出第五篇了 :D,再次感謝編輯賞光啦,願意刊登我寫的題材...

    這篇 [生產者/消費者] 其實是延續上一篇 [生產線模式] 而來的,生產線模式,是利用 PIPE 的方式,把工作切成各個階段同時進行。而生產者消費者,則是去探討兩個階段之間如何做好進度的協調。這篇除了講講處理的原則之外,也實作了BlockQueue來簡化這個問題。

    其實更漂亮的用法,是我在 MSDN Magazine 上看到的 BlockingStream, 直接把它作成 System.IO.Stream 的衍生類別。像那些包裝成 Stream 的資料處理 (像是壓縮、加密、或是 Socket 這類),都很適合。不過一般情況下不是所有應用都能套上 Stream 的模式,因此就有了把它包裝成 Queue 的念頭,也就是這篇寫的 BlockQueue 的應用。

    相關的東西其實過去也寫了幾篇,只不過 BLOG 寫的都比較瑣碎,都是針對特定主題較深入的討論,而投稿的文章就會比較完整的介紹原理及作法等等,細節就沒辦法兼顧了。有興趣的讀者可以參考一下我部落格的相關文章 :D   這期的範例程式可以在底下下載,相關的 LINK 也列在底下。

    再次謝謝各位支持啦 :D


    MSDN Magazine 閱讀心得: Stream Pipeline
    RUN!PC 精選文章 - 生產線模式的多執行緒應用
    [RUN! PC] 2008 十一月號 生產線模式的多執行緒應用
    生產者 vs 消費者 - BlockQueue 實作

    2010/04/05 RUN! PC 專欄文章 .NET RUN! PC 作品集 多執行緒 技術隨筆 物件導向

  3. [TxF] #2. 先作功課 - 熟悉 P/Invoke 及 Win32 檔案處理...

     

    其實這篇是多寫的,因為前一篇提到的 Transactional NTFS 官方只提供 Win32 API 而已,不提供包裝好的 managed code 用的 library... 因此現階段想始用它,P/Invoke 是逃不掉的... 這篇就先來複習一下,想要在 C# 裡呼叫 unmanaged code 該怎麼用吧。這邊的例子為了配合後面幾篇,就同樣的以檔案處理為例。

    這篇我不想去長篇大論的討論 P/Invoke 那堆規則及語法,也不想去討論那堆 Marshal 的觀念等等... 想學好 P/Invoke 就別看我這篇了,應該去 MSDN 看... 這篇我只想交待一下該如何配合 windows api 來作檔案處理而已。因為這些是往後要用到 Transactional NTFS 必要的技巧,TxF 新的 API 都是跟 win32 標準檔案處理的 API 一一對應的,弄懂了如何用 win32 api 操作檔案,你大概就學會八成的 TxF 了... 想用 TxF ...  熟悉點 P/Invoke 是應該的...

     

    先從最單純的 MoveFile 開始吧,它沒有扯到啥 pointer 或是 handle ... 算是最單純的例子.. 先來看看這 API 原生的樣子:

    BOOL WINAPI MoveFile( __in LPCTSTR lpExistingFileName, __in LPCTSTR lpNewFileName );

     

     

    這 API 做啥事就不用多說了,把 lpExistingFileName 這檔案搬到 lpNewFileName 去... 搬成功還是失敗,就用 BOOL 值把結果傳回來。這時就要透過 P/Invoke 的用法,想辦法產生一個可以對應到 unmanaged code 的 C# function ... 我先把宣告方式貼出來,再來解試 code ...

    P/Invoke: MoveFile[copy code]
       1:  [DllImport("kernel32.dll")]
       2:  static extern bool MoveFile(string lpExistingFileName, string lpNewFileName);

     

    寫過 C / C++ 的人,大概對 extern 這個 keyword 不陌生吧? extern 這修飾字代表這個 function 是外來的,C# 正好拿來用在這裡。外來的 DLL 都是 function 型態,所以一定是 static, 沒有綁著任何一個物件... 而標在上面的 Attribute: DllImport, 則是標上這個 function 是來自那個 DLL。

    在這個例子裡,這樣就足夠了,直接在程式裡試著寫一段 C# (managed code) 來試看看結果吧:

    P/Invoke Sample #1. MoveFile[copy code]
       1:  public class PInvokeTest
       2:  {
       3:      [DllImport("kernel32.dll")]
       4:      static extern bool MoveFile(string lpExistingFileName, string lpNewFileName);
       5:      public static void Main(string[] args)
       6:      {
       7:          string srcFileName = @"C:\file1.txt";
       8:          string dstFileName = @"C:\file2.txt";
       9:          Console.Write("move file: from [{0}] to [{1}] ... ", srcFileName, dstFileName);
      10:          if (MoveFile(srcFileName, dstFileName) == true)
      11:          {
      12:              Console.WriteLine("OK!");
      13:          }
      14:          else
      15:          {
      16:              Console.WriteLine("FAIL!");
      17:          }
      18:      }
      19:  }

     

    程式執行前,看一下 C:\ 的 DIR *.TXT 指令執行結果:

    image

    沒錯,有 c:\file1.txt 這檔案... 接著來執行範例程式:

    image

    執行成功。再重新看一下 C:\ 的 DIR *.TXT 指令執行結果:

    image

    看來程式很順利的呼叫了 win32 api 裡定義的 MoveFile( ... ) ... 這種範例有點不入流,要處理檔案總不可能只有這樣吧? 接著我們再來看看需要 Open File 加上讀寫檔案內容的應用。

    Windows 是個以 HANDLE 為主的作業系統,一般在寫 C / C++ 程式,都是以指標(POINTER)的方式在處理資料,但是在 windows 裡,作業系統提供的資料用的指標,則特別以 "HANDLE" 來稱呼,它比一般的 POINTER 來說多了一些管理的動作。因此你開啟的檔案,或是建立的視窗,通通都以 HANDLE 來稱呼,而不是單純的用 POINTER (雖然它也是個 POINTER 啦)。接下來的例子就來看看 HANDLE 的應用。

    [copy code]
       1:  public class PInvokeTest2
       2:  {
       3:      [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
       4:      public static extern IntPtr CreateFile(
       5:             string lpFileName,
       6:             uint dwDesiredAccess,
       7:             uint dwShareMode,
       8:             IntPtr SecurityAttributes,
       9:             uint dwCreationDisposition,
      10:             uint dwFlagsAndAttributes,
      11:             IntPtr hTemplateFile
      12:             );
      13:      [DllImport("kernel32.dll", SetLastError = true)]
      14:      static extern bool CloseHandle(IntPtr hObject);
      15:      public static void Main(string[] args)
      16:      {
      17:          IntPtr pFile = CreateFile(
      18:              @"c:\file1.txt",
      19:              0x80000000,
      20:              0x00000001,
      21:              IntPtr.Zero,
      22:              3,
      23:              0,
      24:              IntPtr.Zero);
      25:          Stream fs = new FileStream(pFile, FileAccess.Read);
      26:          TextReader tr = new StreamReader(fs);
      27:          Console.WriteLine(tr.ReadToEnd());
      28:          tr.Close();
      29:          fs.Close();
      30:          CloseHandle(pFile);
      31:      }
      32:  }

     

    這個例子裡,很 "神奇" 的把 unmanaged code 拿到的指標 (IntPtr), 丟給 System.IO.* 底下的 FileStream 來用,竟然還能成功的開啟檔案並且把資料讀出來... 沒錯,MS寫的東西本來就都是同一家人,System.IO 那堆東西就是這樣包出來的。 這段範例程式除了看起來複雜一點之外,跟那堆要查文件才知道是啥意思的 flags 之外,其它都很簡單,不過就 Open File, 然後讀出內容,接著 Close ...

    較特別的是,在 P/Invoke 的世界裡,都用 struct System.IntPtr 這個型別,來代表 unmanaged 世界裡常用到的 POINTER.. 藉著這個型別,我們就可以把兩個世界的橋樑給搭起來。不過當你執行這個範例時,編譯器會給你一個很礙眼的警告:

     

    'System.IO.FileStream.FileStream(System.IntPtr, System.IO.FileAccess)' is obsolete: 'This constructor has been deprecated.  Please use new FileStream(SafeFileHandle handle, FileAccess access) instead.  http://go.microsoft.com/fwlink/?linkid=14202'  

     

    沒錯,這是老用法了,一如用指標有數不盡的缺點,新的語言都想盡辦法扔掉它 (POINTER: 我是無辜的...),連包裝過的 IntPtr 也不例外。在 .NET Framework 2.0 裡就不再建議你用這個建構式了,請改用 SafeFileHandle .... 既然在 windows 的世界裡,一切系統的資源都是以 HANDLE 來處理,自然的在 managed code 裡也有對應的型別,它就是 System.Runtime.InteropServices.SafeHandle 。接著再來看看改用 SafeHandle 的版本:

    [copy code]
       1:  public class PInvokeTest3
       2:  {
       3:      [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
       4:      static extern SafeFileHandle CreateFile(
       5:          string fileName,
       6:          uint fileAccess,
       7:          uint fileShare,
       8:          IntPtr securityAttributes,
       9:          uint creationDisposition,
      10:          uint flags,
      11:          IntPtr template);
      12:      public static void Main(string[] args)
      13:      {
      14:          SafeFileHandle pFile = CreateFile(
      15:              @"c:\file1.txt",
      16:              0x80000000,
      17:              0x00000001,
      18:              IntPtr.Zero,
      19:              3,
      20:              0,
      21:              IntPtr.Zero);
      22:          Stream fs = new FileStream(pFile, FileAccess.Read);
      23:          TextReader tr = new StreamReader(fs);
      24:          Console.WriteLine(tr.ReadToEnd());
      25:          tr.Close();
      26:          fs.Close();
      27:          pFile.Close();
      28:      }
      29:  }

     

    執行的結果,當然也是順利的把文字檔內容印到 CONSOLE 裡了,我就不再多貼,直接看程式碼。

    其實這裡有點偷吃步,用的是 SafeFileHandle, 而不是 SafeHandle。雖然兩者有繼承關係啦。不過後面我就都把它當成 SafeHandle 看待。這個版本的程式,除了把 IntPtr 換成 SafeFileHandle 之外,及最後的 CloseHandle( pFile ) 改成直接呼叫 SafeHandle.Close( ) 之外,沒有太大的不同了。有啦,把 IntPtr 跟 CloseHandle 兩者包裝在同一個 SafeHandler 裡是安全的多,至少 SafeHandle 實作了 IDispose 的介面,在適當的情況下,它至少會自動的被呼叫 Dispose( ) 回收資源...

     

    看到這裡,以前老搞不清楚的 FileStream 為什麼有幾個怪怪的 constructor, 自從研究了 TxF 之後,意外的晃然大悟... 算是額外的收穫吧! 原來就是要配合 P/Invoke 使用...

    其實說穿了,呼叫 win32 api 大概就這幾招了,幾種 unmanaged code 裡用到的型別,指標都可以對應之後,程式寫起來就簡單了。這篇我特意漏掉一部份,就是各種用 uint 來當作 flags 的型別,沒有轉到對應的 enum 列舉型別,我是覺的這是 option 啦,畢竟查查文件就有,多列上來我得多打好多字... 篇幅有限 XD

    這幾個例子如果都看懂後,那麼 TxF 就沒有障礙了! 接下來就看下回分解了 :D    下回會看到第一個 "交易式" 的檔案處理範例! 沒灌 windows vista / 2008 / 7 / 2008 R2 的讀者門,快去準備吧!

     

    參考資訊:

    1. 有用的網站: PINVOKE.NET
      http://www.pinvoke.net/index.aspx

      這網站幫你整理好了各種 win32 api 該如何宣告它的 C# signature, 以供 p/Invoke 使用。如果你不熟它的語法,這網站可以幫你不少忙。另外它也提供了一些方便的工具,像是 winform 的查詢工具,或是 visual studio 用的 add-ins
    2. MSDN 的 API 說明: MoveFile
      http://msdn.microsoft.com/en-us/library/aa365239(VS.85).aspx
    3. 什麼是 P/Invoke ? Wiki 的說明:
      http://en.wikipedia.org/wiki/Pinvoke
    4. Safe Handle & Critical Finalization
      (實在是看不懂的中譯... "安全控制代碼和關鍵結束" ???)
      http://msdn.microsoft.com/zh-tw/library/fh21e17c(v=VS.90).aspx

    2010/03/23 系列文章: 交易式 (Transactional) NTFS .NET C# MSDN Transactional NTFS

  4. [TxF] #1. 初探 Transactional NTFS

    其實想用 TxF (Transactional NTFS, 交易式的 NTFS) 已經很久了,不過老是被一些雜事卡著,到過年期間才有空好好研究一下。這篇主要是介紹而已,就不講太多 Code, 先以瞭解 TxF 是什麼,及如何善用它等等方面為主。詳細的用法,就等後面幾篇吧!

    Transactional NTFS, 中文是 "交易式NTFS",或是常見到的縮寫 "TxF”,早期的相關文章也有人寫 "TxFS”。這是在 Windows Vista / 2008 推出時,首次正式提供的功能。雖然它叫作 Transactional “NTFS”, 實際上它並不是一個新的檔案系統,而是一組新的 API (跟原有的檔案處理 API 幾乎是一對一的對應),  支援你用交易的方式操作檔案。一起推出的還有 Transactional Registry (TxR),一樣是有對應的 windows API,只不過它處理的對像是 windows registry,不是檔案...。

    用這種方式處理檔案的讀寫動作,有種很神奇的感覺,過去都只在資料庫裡有機會這樣用,現在檔案的處理也可以了。配合像是 DTC 這類交易協調器的支援,甚至可以把檔案的處理及資料庫的處理,通通都包裝成一個交易來進行,一但任何一個環節失敗,都可以回復到最初的狀態,感覺好像是在用 DB,而不是寫檔案... 目前官方並沒有推出 managed code 的含式庫,現在要用只有幾種選擇:

    1. 直接用 C / C++ 呼叫 win32 api
    2. 用 P/Invoke,在 C# 裡呼叫 win32 api
    3. 找那些別人 (非官方) 包裝好的 .net class library ..

    這些用起來都有點不踏實,畢竟用 P/Invoke 不是長久之計,總覺的遲早會被替換掉。不過即使如此,這項還是掩蓋不了這技術的價值。我貼一段自己寫的 sample code,讓還沒用過的人體會一下,寫檔案還支援交易處理的 "爽度" ...

     

    [copy code]
       1:  // 建立 KTM transaction object
       2:  IntPtr transaction = CreateTransaction(
       3:      IntPtr.Zero,
       4:      IntPtr.Zero,
       5:      0, 0, 0, 0,
       6:      null);
       7:  string[] files = new string[] {
       8:      @"c:\file1.txt",
       9:      @"c:\file2.txt",
      10:      @"c:\file3.txt"};
      11:  try
      12:  {
      13:      foreach (string file in files)
      14:      {
      15:          // 使用支援交易的 delete file API
      16:          if (DeleteFileTransactedW(file, transaction) == false)
      17:          {
      18:              // 刪除失敗
      19:              throw new InvalidOperationException();
      20:          }
      21:      }
      22:      // 認可交易
      23:      CommitTransaction(transaction);
      24:  }
      25:  catch (Exception ex)
      26:  {
      27:      // 還原交易
      28:      RollbackTransaction(transaction);
      29:  }
      30:  CloseHandle(transaction);

     

    範例裡用到的幾個 method, 像是 CreateTransaction( ), DeleteFileTransactedW( ), CommitTransaction( ), RollbackTransaction( ) ... 等等,都是透過 P/Invoke 的方式呼叫的 win32 api... 除了用的型別不如 pure .net class library 般直覺之外,這樣的 code 也已經很簡單了,短短卅行就可以搞定...

    雖然這樣的 code 實在不大合我胃口,但是它畢竟是個堪用的方案... 對於 code 有潔癖的,可以考慮其它的用法。前面是最基本的 API call,如果你不滿意,MS自家的技術 DTC (Distributed Transaction Coordinator) 當然也支援 TxF。DTC 可以提供額外的好處,就是允許你做分散式的交易管理。意思是你配合 DTC,就可以把 Local File I/O 跟 database access 整合在同一個交易範圍內。

    這邊的 sample code 我就不貼了,在 managed code 裡去呼叫到 COM 的那堆介面 (啥 QueryInterface 的) 實在跟 .NET programming 的 style 有點格格不入... 在 C# 的世界裡,應該用 TransactionScope 才對。在 MS 的世界裡,TxF + TxR + DB 都可以是 TransactionScope 內的一部份。這部份的 Sample Code 我一樣先不貼了,不然貼一堆 code 又沒篇幅說明,感覺很混...

    其實,MS 該做的都做了,唯一缺的就是它竟然沒正式的併入 .NET Framework 內的一員... 如果 TxF 真的是你想用的東西,倒是有個 OpenSource Project 可以考慮一下: AlphaFS, 它的目標是能替換掉 namespace System.IO.*, 所以很多你常用的 class library, 它都有對等一樣用法的版本,當然它提供了更多的功能及改善... 其中 TxF 的支援就在內,你想用 TxF 來開發軟體的話,這是個不錯的選擇...

    總之,這篇只是個開始,目的是想先 "預覽" 一下 TxF 的能耐,及未來它配 DTC / TransactionScope 後,能怎麼應用它的方式,還有其它可用的相關資源。接下來我會陸續整理一些相關的研究心得.. (別太期待,大概一兩週生一篇就很偷笑了 XD),下回見 !

     

     

    參考資訊:

    1. AlphaFS: Brining Advanced Windows FileSystem Support to .NET
      http://alphafs.codeplex.com/
    2. MSDN magazine (July 2007): Enhance Your Apps With File System Transactions
      http://msdn.microsoft.com/en-us/magazine/cc163388.aspx
    3. B# .NET BLOG: Windows Vista - Introducing TxF In C#
      Part 1: Transacted File Delete
      Part 2: Using System.Transactions and the DTC
      Part 3: CreateFileTransacted Demo
    4. Code Project: Windows Vista TxF / TxR
      http://www.codeproject.com/KB/vista/KTM.aspx
    5. BLOG: Because we can
      http://blogs.msdn.com/because_we_can/archive/2005/05/18/419809.aspx
      Discussion and explanation relating to the Transactional NTFS feature coming in Longhorn, plus any other interesting anecdotes...
    6. Performance Consoderations for Transactional NTFS
      http://msdn.microsoft.com/en-us/library/ee240893(VS.85).aspx
    7. When to Use Transactional NTFS
      http://msdn.microsoft.com/en-us/library/aa365738(VS.85).aspx

    2010/03/18 系列文章: 交易式 (Transactional) NTFS .NET C# MSDN SQL Transactional NTFS 作業系統

  5. [Tips] 用 “磁碟鏡像" 更換硬碟 #2, Windows 2003 的跨距磁區

    上一篇,介紹了如何用windows server内建的磁碟鏡像 (Mirror) 更換硬碟後,這次剛好有windows 2003,就拿來試了一下...

    廢話就不多說了,先來看一下 2003 的步驟,再來看看跟 2008 / 2008R2 差在那裡:

    1. 原本的樣子,磁碟1 (8.00GB) 是舊的硬碟,磁碟2 (16.00GB) 是要換上來的新硬碟:
      image001

    2. 磁碟1 + 磁碟2 做成鏡像 (MIRROR):
      image002


      等待重新同步化 (Resync) 完成:
      image003


    3. Resync 完成後,移除鏡像:
      image004


      移除鏡像完成後的狀態:
      image005 

    4. 用 2003 的延伸磁區,把磁碟2後面沒用到的空間也併進 D: 來
      image006


      合併之後的狀態:
      image007 



    整個步驟跟前一篇都差不多,主要都是靠鏡像(MIRROR)搬完資料後,再把新硬碟多的可用空間併進來。唯一的差別就在這裡: 2003 的 "延伸磁區" (英文: extend volume) 是可以把兩個硬碟,或是兩個分割區併在一起使用。在磁碟管理員還是看的到這些分割區的存在。這就有點像 JBOD (Just a Bunch Of Disks) 的模式。

    不過在 2008 之後 (其實 vista 也是),這個功能就改了。原本的名字 extend volumn 現在改成直接擴大原分割區,把原分割區後面可用的空間都納進來,就像你砍掉再重建一個大的分割區一樣,只是資料會留著不會掉。當然有 extend 也有相對的 shrink volume, 這功能會把分割區縮小,騰出空間來讓你多切一個分割區...。而原本 JBOD 模式的功能,則改為 span disk, 用起來效果就如上圖一樣,當然你願意的話,也可以把多顆硬碟 (可以不同容量) 通通併成一個來使用,最多可以併到 32 顆硬碟...。

    沒經過這次更換硬碟,還真沒發現 2008 / vista 總算內建這組 extend / shrink volume 的功能進來。雖然很陽春,不過已經很實用了,在過去這種動作是得搬出像 partition magic 這類軟體才能做的到,而這種東西每次用起來心裡都會毛毛的,深怕一不小心就把資料都給毀了...。

    這篇小品文章就記到這裡,希望有幫到需要的人 :D

    2010/03/11 Tips 技術隨筆 有的沒的