天天看點

.Net Micro Framework研究—FAT檔案系統實作探索

由于目前.Net Micro Framework并不支援P/Invoke功能(也無法像WinCE一樣開發流式驅動),是以在底層在驅動層面用C直接對存儲器(Flash)進行檔案系統開發是行不通的。幸好.Net Micro Framework提供了ExtendedWeakReference類,其中指派Target對象可以把資料存放到存儲器上(Flash)。

[Serializable]
        private class FlashDatas
        {
            //調入資料
            public static byte[] Load(uint index)
            {
                ExtendedWeakReference ewr = ExtendedWeakReference.RecoverOrCreate(
                        typeof(FlashDatas),                       //類型,任意類都可以,其名稱起到一個索引作用
                        index,                                    //ID号,這個資料比較有用,不同ID号代表不同資料
                        ExtendedWeakReference.c_SurviveBoot);//該标志和.c_SurviveBoot 差別不大
                return ewr.Target as byte[];
            }        
 
            //儲存資料
            public static void Save(uint index, byte[] data)
            {
                ExtendedWeakReference ewr = ExtendedWeakReference.RecoverOrCreate(typeof(FlashDatas), index, ExtendedWeakReference.c_SurviveBoot);
                ewr.Target = data;
            }
    }           

上面的代碼就是ExtendedWeakReference類的具體使用,從代碼可以看出我們無法直接對存儲器進行讀寫,隻能通過儲存類的方式對資料進行存儲,至于該資料存放到何處,那是無從得知的。

我最初的想法是定義一個類,類中定義一個大數組,不過實際調試發現,該數組不能太大,超過幾十K就會出現記憶體溢出。幸好該對象可以是位元組數組,是以我産生了另一個想法,每次儲存一個512位元組大小的位元組數組,相當于磁盤上的一個扇區,以此為存取的最小機關,實作FAT檔案系統。

在我部落格上曾寫了一篇關于FAT檔案系統的文章,如《FAT檔案系統幾點釋疑》(

http://blog.csdn.net/yefanqiu/archive/2008/03/13/2176340.aspx

),我們知道要實作FAT16系統一般至少需要4M存儲空間,實作FAT32一般需要256M空間以上。是以我簡單的做了一個程式,在實際硬體中測試一下存取1024*4個512位元組的數組記憶體是否可行。

測試代碼如下:

private const uint SectorSize=512;   //扇區大小
        private const uint SecPerClus = 4;   //一個簇包含的扇區數
        public static void Main()
        { 
            Debug.Print("Start");
            for (uint i = 0; i < 512; i++)    //1024*4
            {
                byte[] bytData = new byte[SectorSize];
                bytData[0] = (byte)(i % 256);
                bytData[bytData.Length - 1] = bytData[0];
                FlashDatas.Save(i, bytData);
               Debug.Print(i.ToString() + " Save " + bytData[0].ToString() + " " + bytData[bytData.Length - 1].ToString());
 
                //byte[] bytData = FlashDatas.Load(i);
                //if (bytData == null)
                //{
                //    Debug.Print(i.ToString() + " Load Error");
                //    break;
                //}
                //else
                //{
                //    Debug.Print(i.ToString() + " Load " + bytData[0].ToString() + " " + bytData[bytData.Length - 1].ToString());
                //}
            }
            Debug.Print("Exit");
 }           

讓我失望的是,Digi的開發闆存儲個數一旦超過128個就會出現讀失敗,新拿來的iPac-9302開發闆要好一些,512個之内讀寫沒有什麼問題,超過這個數就會出現和Digi開發闆一樣的問題。需要說明的時,在使用讀寫的過程中如果不斷電,讀寫都會成功的。一但斷電重新讀取,讀就會失敗。(當然在我測試過程中出現了各種各樣不同的現象,如隻能成功讀取前幾個)。

杜偉當初還想直接支援FAT32系統呢,目前恐怕FAT16的支援都很困難了,如果實作FAT12系統就有點不值當了。不過杜偉建議說模拟器也支援資料存儲功能,是以先在模拟器中實作該功能。

沒有想到,模拟器存儲器最大存儲僅支援1M,開始我還以為我配置參數不當呢,後來反編譯了模拟器相關的核心代碼,發現1M在代碼中就已經寫死了,相關内容如下。

反編譯 Microsoft.SPOT.Emulator.dll,下面是關鍵代碼

------------------------------------------------------------------------
//記憶體大小0x10000*16 = 1024*1024 也就是1M空間。
public class FlashManager : MemoryManagerBase
{
    // Fields 存儲空間已經寫死,就是1M
    private FlashSector[] _flashSectors = new FlashSector[] { 
new FlashSector(0x10000, FlashSectorUsage.Log, FlashSectorPartition.Start),
new FlashSector(0x10000, FlashSectorUsage.Log, FlashSectorPartition.None), 
new FlashSector(0x10000, FlashSectorUsage.Log, FlashSectorPartition.None), 
new FlashSector(0x10000, FlashSectorUsage.Log, FlashSectorPartition.None), 
new FlashSector(0x10000, FlashSectorUsage.Log, FlashSectorPartition.None), 
new FlashSector(0x10000, FlashSectorUsage.Log, FlashSectorPartition.None), 
new FlashSector(0x10000, FlashSectorUsage.Log, FlashSectorPartition.None), 
new FlashSector(0x10000, FlashSectorUsage.Log, FlashSectorPartition.End), 
 
new FlashSector(0x10000, FlashSectorUsage.Log, FlashSectorPartition.Start), 
new FlashSector(0x10000, FlashSectorUsage.Log, FlashSectorPartition.None), 
new FlashSector(0x10000, FlashSectorUsage.Log, FlashSectorPartition.None), 
new FlashSector(0x10000, FlashSectorUsage.Log, FlashSectorPartition.None), 
new FlashSector(0x10000, FlashSectorUsage.StorageA, FlashSectorPartition.None),
new FlashSector(0x10000, FlashSectorUsage.StorageA, FlashSectorPartition.None), 
new FlashSector(0x10000, FlashSectorUsage.StorageB, FlashSectorPartition.None), 
new FlashSector(0x10000, FlashSectorUsage.StorageB, FlashSectorPartition.End) };
 
....
}
 
//配置設定記憶體
internal override void AllocateMemory()
{
        this.ValidateFlashSectorsInternal();
        uint num = 0;
        for (int i = 0; i < this._flashSectors.Length; i++)
        {
            num += this._flashSectors[i].Length;
        }
        base._size = num;
        base.AllocateMemory();    //配置設定記憶體 ... ...
        for (int j = 0; j < base._size; j++)
        {
            base._memory[j] = 0xff;
        }
        this.InitializeFlashSectorsInternal();
}
 
//配置設定記憶體
internal virtual void AllocateMemory()
{
        this._memory = new byte[this._size];
        this._handle = GCHandle.Alloc(this._memory, 3);
}           

此外模拟器在運作結束時,不能保證執行重載的UninitializeComponent函數,是以無法儲存記憶體的資料,代碼如下。

/// <summary>
        /// Called by the emulator after all components were setup and registered
        /// </summary>
        public override void InitializeComponent()
        {
            base.InitializeComponent(); 
            _form = new YFEmulatorForm(this.Emulator);
            _form.OnInitializeComponent();
 
            //Launch the UI thread.
            Thread uiThread = new Thread(RunForm);
            uiThread.SetApartmentState(ApartmentState.STA);
            uiThread.Start();
 
            //讀Flash資料
            EmulatorFlashPersistance.Load(this);
 
            //必須添加這句,否則不會執行UninitializeComponent方法
            Application.DoEvents();     //這是我添的
        }
        
       
        /// <summary>
        /// Called by the emulator after the program exits
        /// </summary>
        public override void UninitializeComponent()    //這個函數不能保證會運作
        {
            //儲存資料
            EmulatorFlashPersistance.Save(this);        //是以無法儲存最後的結果
            Application.DoEvents(); //儲存資料 
 
            base.UninitializeComponent();
            //When the Micro Framework is shutting down, inform the the WinForm application 
            //to exit as well.
            Application.Exit();
        }           

目前該工作的開展對我來說,實在是一個不小的挑戰。由于國内研究.Net Micro Framework不多,不僅沒有人進行深層次的讨論,也少見相關資料,是以.Net Micro Framework推廣真是任重而道遠啊。

附記:在我上兩篇關于序列槽部署的文章又有了新的進展,最近花了300多元購買了Moxa的UPort1110 USB轉序列槽裝置(一般的雜牌子的裝置大約幾十元一個)還真不錯,在我筆記本上終于可以直接通過序列槽對.Net Micro Framework進行調試了。