1. 程式人生 > >yaffs2檔案系統結構及應用

yaffs2檔案系統結構及應用

        檔案系統是包括在一個磁碟(包括光碟、軟盤、閃盤及其它儲存裝置)或分割槽中的目錄結構;一個可應用的磁碟裝置可以包含一個或多個檔案系統;如果您想進入一個檔案系統,首先您要做的是掛載(mount)檔案系統;為了掛載(mount)檔案系統,您必須指定一個掛載點(所掛載的目錄)。

mount –t filesystemtype  filesystem mountpoint

e.g  mount  -t yaffs2 /dev/mtdblock1 /mnt

1. Yaffs用到的專用術語

Page : NAND裡可訪問的最小儲存單元,是實際資源儲存區,需要跟檔案系統中"頁"的概念區別開來.具有的屬性:讀,寫;能被標記,表明其是否損壞;還有用於存放校驗資料的oob 單元. Page大小有512位元組的,也有2K位元組的;針對此,oob區域也有不同大小,512B/Page的oob有16個位元組,2KB/Page的oob就是前者的4倍,也就是說64個位元組.

Block : NAND實現了快速的擦,就是因為有了它. 塊結構有兩種:小塊和大塊,小塊NAND FLASH包含32個頁,每頁512+16位元組;大塊NAND FLASH包含64頁,每頁2048+64位元組。

OOB :備用空間(SpareData,OOB),用來儲存ECC校驗/壞塊標誌等資訊,每個Page都有自己的oob.

Chunk :Chunk是Yaffs檔案系統裡的概念,是檔案訪問的基本單元,表示的是yaffs_object所配置到的邏輯資源儲存區。在yaffs中大小與page相同。

Object :構成Yaffs檔案系統的各種元素,比如檔案,目錄,連結,裝置等等.

2.  yaffs檔案系統介紹

2.1  簡介

2.1.1應用場合

            Yaffs(Yet Another FlashFile System)檔案系統是專門針對NAND快閃記憶體設計的嵌入式檔案系統,目前有YAFFS和YAFFS2兩個版本,兩個版本的主要區別之一在於YAFFS2能夠更好的支援大容量的NAND FLASH晶片。

2.1.2  NOR和NAND的比較

            基本上NOR比較適合儲存程式程式碼,其容量一般較小(比如小於32MB),價格較高;而NAND容量可達1GB以上,價格也相對便宜,適合儲存資料。一般來說,128MB以下容量NAND FLASH 晶片的一頁大小為528位元組,用來存放資料,另外每一頁還有16位元組的備用空間(SpareData,OOB),用來儲存ECC校驗/壞塊標誌等資訊,再由若干頁組成一個塊,通常一塊為32頁16K.

            與NOR相比,NAND不是完全可靠的,每塊晶片出廠時都有一定比例的壞塊存在,對資料的存取不是使用地址對映而是通過暫存器的操作,序列存取資料。

2.2  Yaffs檔案系統資料在NAND上的儲存方式

            Yaffs對檔案系統上的所有內容(比如正常檔案,目錄,連結,裝置檔案等等)都統一當作檔案來處理,每個檔案都有一個頁面專門存放檔案頭,檔案頭儲存了檔案的模式,所有者id,組id,長度,檔名,Parent Object ID等資訊。因為需要在一頁內放下這些內容,所以對檔名的長度,符號連結物件的路徑名等長度都有限制。

2.2.1  yaffs1檔案系統

            前面說到對於NAND FLASH上的每一頁資料,都有額外的空間用來儲存附加資訊,通常NAND驅動只使用了這些空間的一部分,yaffs正是利用了這部分空間中剩餘的部分來儲存檔案系統相關的內容。以512+16B為一個PAGE的NAND FLASH晶片為例,yaffs檔案系統資料的儲存佈局如下所示:

0..511

資料區域

512..515

YAFFS TAG

516

Data status byte

517

Block status byte壞塊標誌位

518..519

YAFFS TAG

520..522

後256位元組資料的ECC校驗結果

523..524

YAFFS TAG

525..527

前256位元組資料的ECC校驗結果

可以看到在這裡YAFFS一共使用了8個BYTE用來存放檔案系統相關的資訊(yaffs_Tags)。這8個Byte的具體使用情況按順序如下:

Bits

Content

20

ChunkID,該page在一個檔案內的索引號,所以檔案大小被限制在2^20PAGE即512Mb

2

2 bits serial number

10

ByteCount該page內的有效位元組數

18

ObjectID物件ID號,用來唯一標示一個檔案

12

Ecc,Yaffs_Tags本身的ECC校驗和

2

unused

其中Serial Number在檔案系統建立時都為0,以後每次寫具有同一ObjectID和ChunkID的page的時候都加一,因為yaffs在更新一個PAGE的時候總是在一個新的物理Page上寫入資料,再將原先的物理page刪除,所以該Serial Number可以在斷電等特殊情況下,當新的page已經寫入但老的page還沒有被刪除的時候用來識別正確的page,保證資料的正確性。

            ObjectID號為18bit,所以檔案的總數限制在256k即26萬個左右。

            對於yaffs2因為針對chunk size大於1k的NAND FLASH,在tags各分量及總體尺寸上都做了修改,以便更快更好的處理大容量的NAND FLASH 晶片。由於Tag尺寸的增大,在512+16B型別的NAND FLASH 上就一個chunk對應一個page的情況,目前就無法使用yaffs2檔案系統了(硬體儲存方面)。

            Yaffs2相容yaffs1(註冊yaffs2檔案系統可以操作yaffs1的檔案系統)。YAFFS檔案系統會根據NANDFlash的頁面的大小來主動的配置是使用YAFFS1還是YAFFS2。

2.2.2  yaffs2檔案系統

設計yaffs2檔案系統的初衷是對2kB大小每頁,並且嚴格按照頁順序寫的方式的NAND FLASH的支援。

            yaffs2檔案系統相對yaffs1具有下列優勢:

·        zero page rewrites means fasteroperation. (YAFFS1 uses a single rewrite in the spare area to delete a page).

·        ability to exploit simultaneous pageprogramming on some chips.

·        improves performance relative toYAFFS1 speed(write:1.5x to 5x, delete: 4x, garbage collection: 2x)

·        lower RAM footprint (approx. 25% to50% of YAFFS1).

·        Can support Toshiba/Sandisk MLCparts.

            YAFFS2檔案系統在設計時就充分考慮了大頁NAND FLASH的結構,根據大頁NAND FLASH以頁面為單位存取的特點,將檔案組織成固定大小的頁,利用大頁NAND FLASH 提供的每個面(2112B,其中前2048B儲存資料)64B的備用空間(SpareData,OOB)來存放ECC和檔案系統的組織資訊,這樣不僅能夠實現錯誤檢測和壞塊處理,還能夠提高檔案系統的載入速度。

YAFFS2檔案系統資料在NAND FLASH 的備用空間內的儲存佈局:

Field

Comment

Size for 2kb chunks

blockState

Block state. Non-0xFF for bad block

1byte

chunkID

32-bit chunk Id

4 byte

objectID

32-bit object Id

4 byte

nBytes

Number of data bytes in this chunk

2 byte

BlockSequence

Sequence number for this block

4 byte

tagsEcc

ECC on tags area

3 byte

Ecc

ECC,3 bytes/256 bytes of data

24 byte

Total

42 byte

blockSequence:記錄著各塊被分配出去的先後順序,每分配出去一塊,就加1。

垃圾回收策略:

            YAFFS1刪除資料頁是通過將NAND FLASH的相應頁的一個標誌位位元組(ObjectID)寫為0實現的,而YAFFS2為了能支援某些特殊的NAND FLASH,垃圾回收器讀NAND FLASH每頁的OOB區,得到這個資料頁所屬的檔案ID,若此檔案在YAFFS2虛擬建立的unlinked目錄下,則此資料頁無效。

/kernel/fs/fs-writeback.c/__mark_inode_dirty 刪除檔案的函式

/kernel/fs/yaffs2/yaffs_guts.c/yaffs_create_initial_diràyaffs_create_fake_dir建立四個虛擬目錄:unlinked, deleted,root and lost and found。

如何選擇塊則由一定的策略而定,如最少髒頁塊。如果回收塊中有有效資料,則將有效資料複製到新的空閒扇區中,並重新對其進行對映。垃圾回收器的啟動時機是在FLASH中可用的扇區低於一定的閾值或者FLASH中的髒扇區的時候。也就是說,當系統企圖獲得空閒扇區的時候,發現FALSH中的空閒扇區低於預期了,則開始垃圾回收來獲取更多的空閒扇區。

具體程式碼:

Kernel/fs/yaffs2/yaffs_guts.c

Yaffs_vfs.c/ yaffs2_mountàyaffs2_internal_read_super_mtdà yaffs_internal_read_superàyaffs_bg_startàkthread_run(yaffs_bg_thread_fn, (void *)dev, "yaffs-bg-                                                                        %d",context->mount_id);建立後臺垃圾收集執行緒yaffs_bg_thread_fn:根據可用chunk數A(可用塊中)與總可用chunk數(包括A與已有資料的塊中的空閒chunk數)之間的關係,確定下次垃圾回收的時間,具體見yaffs_vfs.c/ yaffs_bg_gc_urgency函式。垃圾回收的具體實現函式:yaffs_guts.c/yaffs_bg_gc àyaffs_check_gc

另外,在寫資料時也會呼叫yaffs_check_gc函式。

            由於檔案系統的基本組織資訊儲存在頁面的備份空間中,因此,在檔案系統載入時只需要掃描各個頁面的備份空間,即可建立起整個檔案系統的結構,而不需要像JFFS1/2那樣掃描整個介質,從而大大加快了檔案系統的載入速度。

2.3   yaffs檔案系統在在記憶體中的組織方式

2.3.1  SupperBlock

            操作檔案系統的第一步自然是取得SupperBlock了,Yaffs檔案系統本身在NAND Flash上並不存在所謂的SupperBlock塊,完全是在檔案系統mount的過程中由read_super函式填充的,不過有意思的一點是,由於物理上沒有儲存SupperBlock塊,所以NADN Flash上的yaffs檔案系統本身沒有儲存filesystem的魔數(MagicNum),在記憶體中SupperBlock裡的s_magic引數也是直接賦值的,所以儲存在NAND Flash上的任何檔案系統都能被當作yaffs檔案系統mount上來,只是資料都會被當作錯誤資料放在lost+found目錄中,不知道這算不算yaffs檔案系統的一個bug.

超級塊中儲存了全域性檔案,如硬碟已用空間、資料塊可用空間、inode結點的個數和部分可以及時使用的inode結點列表、檔案系統名稱(比如 ext2)、檔案系統的大小和狀態、塊裝置的引用和元資料資訊(比如空閒列表等等)、最近一次的更新時間與檔案系統的狀態等。做一個形象的比喻,這個超級塊就似乎是企業的資產負債表,一個檔案系統中有哪些資源都記錄在這個表中。

Inode結點定義:

Linux系統中的每個檔案都被賦予一個唯一的數值,這個數值稱做索引節點。索引節點儲存在一個稱作索引節點表< inode table>中,該表在磁碟格式化時被分配。每個實際的磁碟或分割槽都有其自己的索引節點表。一個索引節點包含檔案的所有資訊,包括磁碟上資料的地址和檔案型別。檔案型別包括如普通檔案、目錄和特殊檔案這樣的資訊。

            魔數通常位於一個物件中,並用來判斷物件(如檔案)的型別。例如對於a.out格式的執行檔案來說,其開始兩個位元組就是一個魔數,具有特定的值。對於一個檔案系統來說,其超級塊開始的位元組就用來指定檔案系統的型別,也是一個魔數。

通常一個具體的檔案系統在VFS的supper_block結構中除了通用的資料外,還有自己專用的資料,yaffs檔案系統的專用資料是一個yaffs_DeviceStruct結構,主要用來儲存一些相關軟硬體配置資訊,相關函式指標和統計資訊等。

VFS超級塊與YAFFS等具體檔案系統超級塊的關係:

            引用《Linux系統中虛擬檔案系統核心機制研究》的定義:    VFS超級塊是在檔案系統安裝時由系統在記憶體中建立的,對於每個已安裝的檔案系統,在記憶體中都有與其對應的VFS超級塊。VFS超級塊的作用是把在各種檔案系統中表示整體組織結構的資訊轉換成統一的格式。各種檔案系統的VFS超級塊都是一個super-block結構體,VFS超級塊super-block結構體的定義在/include/fs/fs.h中。各檔案系統VFS超級塊例程read-super()把某種檔案系統的管理資訊寫入它自己的VFS超級塊中。VFS超級塊主要包含的資訊有:檔案系統的組織資訊、檔案系統的註冊和安裝資訊、超級塊的屬性資訊,不同物理檔案系統特有的資訊則由聯合體U的各個成員項表示,指向super-operations結構體中包含著對超級塊進行操作的函式指標等。

SupperBlock填充及MagicNum賦值的原始碼呼叫流程

Kernel/fs/namespace.c/

SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,

              char __user *, type,unsigned long, flags, void __user *, data)

->do_mount->do_new_mount->do_kern_mount根據”yaffs2”字串獲取file_system_type結構指標type->vfs_kern_mountàmount_fs:type->mount(type, flags, name, data);此時跟蹤yaffs註冊時的程式碼。Kernel/fs/yaffs2/yaffs_vfs.c/init_yaffs_fsàregister_filesystem(fsinst->fst),而fsinst =fs_to_install;

static struct file_system_to_install fs_to_install[] = {

            {&yaffs_fs_type, 0},

            {&yaffs2_fs_type, 0},

            {NULL, 0}

};

static struct file_system_type yaffs2_fs_type = {

            .owner = THIS_MODULE,

            .name ="yaffs2",

            .mount = yaffs2_mount,

            .kill_sb =kill_block_super,

            .fs_flags =FS_REQUIRES_DEV,

};

此時再跟蹤yaffs2_mountàkernel/fs/super.c/mount_bdev->get(fs_type,test_bdev_super, set_bdev_super, bdev);查詢或建立超級塊。

mount_bdev -> fill_super-> yaffs2_internal_read_super_mtd ->yaffs_internal_read_super->

sb->s_magic = YAFFS_MAGIC;

2.3.2  檔案在記憶體中的組織方式

在mount過程執行read_super的過程中,yaffs檔案系統還需要將檔案系統的目錄結構在記憶體中建立起來。由於沒有super塊,所以需要掃描yaffs分割槽,根據從OOB中讀取出的yaffs_tags資訊判斷出是檔案頭page還是資料page,再根據檔案頭page中的內容以及資料page中的ObjectID/ChunkID/serialNumber等資訊在記憶體中為每個檔案(Object)建立一個對應的yaffs_object物件。

            在yaffs_object結構中,主要包含了:

⑴ 如修改時間,使用者ID,組ID等檔案屬性;

⑵ 用作yaffs檔案系統維護用的各種標記位如髒(dirty)標記,刪除標記等;

⑶ 用作組織結構的,如指向父目錄的Parent指標,指向同級目錄中其他物件連結串列的siblings雙向連結串列頭結構;此外根據Object型別的不同(目錄,檔案,連結),對應於某一具體型別的Object,在yaffs_object中還有其各自專有的資料內容

⑷ 普通檔案:檔案尺寸,用於快速查詢檔案資料塊的yaffs_Tnode樹的指標等

⑸ 目錄:目錄項內容雙向連結串列頭(children)

⑹ 連結:softlink的alias,hardlink對應的ObjectID

            除了對應於儲存在NAND FLASH上的object而建立起來的yaffs_object以外,在read_super執行過程中還會建立一些虛擬物件(Fake Object),這些Fake Object在NAND FLASH上沒有對應的物理實體,比如在建立檔案目錄結構的最初,yaffs會建立四個虛擬目錄(Fake Directory):rootDir,unlinkedDir, lostNfoundDir分別用作根目錄,unlinked物件掛接的目錄,delete物件掛接的目錄,無效或零時資料塊掛接的目錄。通過建立這些yaffs_object, yaffs檔案系統就能夠將儲存在NAND FLASH上資料系統的組織起來,在記憶體中維護一個完整的檔案系統結構。

3.  與其他層的互動

3.1 關於檔案系統的三個易混淆的概念:

建立 以某種方式格式化磁碟的過程就是在其之上建立一個檔案系統的過程。建立文現系統時,會在磁碟的特定位置寫入關於該檔案系統的控制資訊。

註冊 向核心報到,宣告自己能被核心支援。一般在編譯核心的時侯註冊;也可以載入模組的方式手動註冊。註冊過程實際上是將表示各實際檔案系統的資料結構struct file_system_type 例項化。

安裝 也就是我們熟悉的mount操作,將檔案系統加入到Linux的根檔案系統的目錄樹結構上;這樣檔案系統才能被訪問。

3.2  MTD簡介

            MTD是Memory  Technology Device的縮寫,它是底層硬體和上層軟體之間的橋樑。對底層來說,它無論對NOR型或是NAND型都有很好的驅動支援;對上層來說,它抽象出文件系統所需要的介面函式。

一個MTD裝置按照使用者訪問順序可以得到下圖所示的層次結構。圖中NAND特定硬體驅動層為具體NAND裝置的驅動,實現特定硬體的具體操作;NAND通用驅動層是所有NAND裝置的公用部分,實現了NAND裝置發現,通用NAND讀寫等操作; MTD原始裝置層是MTD原始裝置的通用程式碼,此外還包括各個特定的快閃記憶體裝置所註冊的資料,例如NAND分割槽等。MTD向上提供塊裝置和字元裝置兩種介面。檔案系統通過MTD塊裝置介面訪問NAND快閃記憶體驅動。

A、Flash硬體驅動層:硬體驅動層負責驅動Flash硬體。

B、MTD原始裝置:原始裝置層有兩部分組成,一部分是MTD原始裝置的通用程式碼,另一部分是各個特定的Flash的資料,例如分割槽。用於描述MTD原始裝置的資料結構是mtd_info,這其中定義了大量的關於MTD的資料和操作函式。mtd_table(mtdcore.c)則是所有MTD原始裝置的列表,mtd_part(mtdpart.c)是用於表示MTD原始裝置分割槽的結構,其中包含了mtd_info,因為每一個分割槽都是被看成一個MTD原始裝置加在mtd_table中的,mtd_part.mtd_info中的大部分資料都從該分割槽的主分割槽mtd_part->master中獲得。在drivers/mtd/maps/子目錄下存放的是特定的flash的資料,每一個檔案都描述了一塊板子上的flash。其中呼叫add_mtd_device()、del_mtd_device()建立/刪除mtd_info結構並將其加入/刪除mtd_table(或者呼叫add_mtd_partition()、del_mtd_partition()(mtdpart.c)建立/刪除mtd_part結構並將mtd_part.mtd_info加入/刪除mtd_table 中)。

C、MTD裝置層:基於MTD原始裝置,linux系統可以定義出MTD的塊裝置(主裝置號31)和字元裝置(裝置號90)。MTD字元裝置的定義在mtdchar.c中實現,通過註冊一系列fileoperation函式(lseek、open、close、read、write)。MTD塊裝置則是定義了一個描述MTD塊裝置的結構mtdblk_dev,並聲明瞭一個名為mtdblks的指標陣列,這陣列中的每一個mtdblk_dev和mtd_table中的每一個mtd_info一一對應。

D、裝置節點:通過mknod在/dev子目錄下建立MTD字元裝置節點(主裝置號為90)和MTD塊裝置節點(主裝置號為31),通過訪問此裝置節點即可訪問MTD字元裝置和塊裝置。

E、根檔案系統:在Bootloader中將JFFS(或JFFS2)的檔案系統映像jffs.image(或jffs2.img)燒到flash的某一個分割槽中。

F、檔案系統:核心啟動後,通過mount命令可以將flash中的其餘分割槽作為檔案系統掛載到mountpoint上。

            一個MTD原始裝置可以通過mtd_part分割成數個MTD原始設備註冊進mtd_table,mtd_table中的每個MTD原始裝置都可以被註冊成一個MTD裝置。

            具體的NAND快閃記憶體驅動是和NAND通用驅動相關聯的,要實現一個NAND快閃記憶體硬體驅動,需要實現以下部分:初始化函式,硬體相關的裝置就緒函式和控制函式。為了靈活起見,還可以實現硬體相關的命令函式,硬體相關的等待函式和硬體ECC函式。

            在程式設計上,MTD為上層提供了一系列的介面,可以使上層程式不關心底層的硬體細節,而通過這些函式直接訪問。這些介面包括:erase, read, write,read_ecc, write_ecc, read_oob, write_oob, sync, lock, unlock, suspend, resume,block_isbad, block_mardbad等函式。通過這些抽象出的介面,檔案系統就可以方便的對Flash進行各種操作了。

平臺裝置資料在kernel/arch/arm/mach-msm/Devices-9615.c

             #define DMOV_NAND_CHAN        3   // Dma.h檔案中

#define MSM_NAND_PHYS            0x1B400000

static struct resourceresources_nand[] = {

   [0] = {

                      .name   = "msm_nand_dmac",

                      .start     = DMOV_NAND_CHAN,

                      .end      = DMOV_NAND_CHAN,

                      .flags    = IORESOURCE_DMA,

    },

   [1] = {

                     .name   = "msm_nand_phys",

                     .start  = MSM_NAND_PHYS,

                     .end    = MSM_NAND_PHYS + 0x7FF,

                     .flags  = IORESOURCE_MEM,

   },

};

struct flash_platform_datamsm_nand_data = {

   .parts           = NULL,

   .nr_parts      = 0,

};

struct platform_device msm_device_nand= {

   .name          = "msm_nand",

   .id        = -1,

 .num_resources    = ARRAY_SIZE(resources_nand),

   .resource     = resources_nand,

   .dev             = {

        .platform_data     = &msm_nand_data,

   },

};

Kernel/arch/arm/mach-msm/nand_partitions.c檔案中get_nand_partitions函式:

         msm_nand_data.nr_parts= 1;

          msm_nand_data.parts = msm_nand_partitions;

而#define MSM_MAX_PARTITIONS 18

static structmtd_partition msm_nand_partitions[MSM_MAX_PARTITIONS];而msm_nand_partiti- ons陣列中資料的填充:

__tagtable(ATAG_MSM_PARTITION, parse_tag_msm_partition); ATAG_MSM_PARTITION(#define ATAG_MSM_PARTITION 0x4d534D70 /* MSMp */該型別的tag結構體空間裡的資料由parse_tag_msm_partition函式來解析)。tag結構體空間裡資料由bootloader寫入。

解析時機:kernel/init/main.c/start_kernelà/kernel/arch/arm/kernel/setup.c/setup_arch(&command_ line ); àsetup_machine_tagsàparse_tags(tags)分析。

/kernel/drivers/mtd/devices/msm_nand.c

/kernel/drivers/mtd/mtdcore.c

msm_nand.c/msm_nand_initàmsm_nand_probe填充msm_nand_info *info結構;呼叫msm_nand_scan(&info->mtd, 1)填充mtd的操作函式(read,write等),資料結構,ECC檢測模式等;呼叫setup_mtd_deviceàmtdcore.c/mtd_device_registerà add_mtd_ device註冊MTD裝置。

Yaffs操作函式的註冊:

Kernel/fs/yaffs2/yaffs_vfs.c

init_yaffs_fsàcreate_proc_entry : Install the proc_fsentries;呼叫         register_filesystem(fsinst->fst)註冊檔案系統:

fsinst = fs_to_install;

static struct file_system_to_install fs_to_install[]= {

{&yaffs_fs_type, 0},

{&yaffs2_fs_type, 0},

{NULL, 0}

};

static struct file_system_typeyaffs2_fs_type = {

.owner = THIS_MODULE,

.name = "yaffs2",

.mount = yaffs2_mount,

.kill_sb = kill_block_super,

.fs_flags = FS_REQUIRES_DEV,

};

yaffs2_mountàmount_bdevàyaffs2_internal_read_super_mtdàyaffs_internal_read_super註冊functions

param->write_chunk_tags_fn =nandmtd2_write_chunk_tags;

param->read_chunk_tags_fn =nandmtd2_read_chunk_tags;

.........................

inode->i_op =&yaffs_dir_inode_operations;

      inode->i_fop= &yaffs_dir_operations;

.........................

讀寫操作流程:

Yaffs2_mtdif2.c /nandmtd2_write_chunk_tagsàmsm_nand.c /msm_nand_writeàmsm_nand_write_ oob 將IO請求壓入請求佇列,由mtdblock.c

static struct mtd_blktrans_opsmtdblock_tr = {

.name       = "mtdblock",

.major      = 31,

.part_bits  = 0,

.blksize   = 512,

.open       = mtdblock_open,

.flush      = mtdblock_flush,

.release    = mtdblock_release,

.readsect   = mtdblock_readsect,

.writesect  = mtdblock_writesect,

.add_mtd    = mtdblock_add_mtd,

.remove_dev = mtdblock_remove_dev,

.owner      = THIS_MODULE,

};

init_mtdblockàmtd_blkdevs.c/register_mtd_blktransàtr->add_mtd(tr, mtd);<=> mtdblock_add_ mtdàadd_mtd_blktrans_dev:  

        new->thread =kthread_run(mtd_blktrans_thread, new,"%s%d", tr->name, new-> mtd->index);建立mtd_blktrans_thread執行緒來處理IO請求:àmtdblock_writesectàdo_cached_write

Yaffs2_mtdif2.c /nandmtd2_read_chunk_tagsà msm_nand.c /msm_nand_readàmsm_nand_read_ oob   後續操作與write類似。

4.Yaffs2檔案系統整合及應用相關

4.1  註冊Yaffs2檔案系統

            ⑴在核心中建立YAFFS目錄fs/yaffs2,並把下載的YAFFS2程式碼複製到該目錄下面。

            ⑵參考yaffs程式碼中的Kconfig檔案,修改你自己的Config.in檔案,使得可以配置YAFFS2。

            ⑶修改fs/makefile,加入yaffs目錄,即obj-$(CONFIG_YAFFS_FS)    += yaffs2/

            在配置YAFFS的時候需要注意一點,即使你的NAND FLASH是512+16B的,不需要使用YAFFS2,也需要將對2k page的NAND FLASH的支援這一項選上,否則編譯無法通過(因為部分程式碼沒有用CONFIG巨集包起來),不知道這是不是這個版本的個別現象,還是對Makefile還需要進一步的修改。

            此外就是最好把Lets Yaffs do itsown ECC選上,理由後面會說,其他選項就無所謂了,主要是對效能的調整,看著選吧,按推薦配置好了,比如Turn off debug chunk erasecheck,這一項,平均可以提高20-30%左右的擦寫速度。

            kernel\fs\yaffs2\yaffs_vfs.c\init_yaffs_fs呼叫register_filesystem完成對檔案系統的註冊

            module_init(init_yaffs_fs)

4.2  通過mkyaffs2image製作yaffs檔案系統

            根據apps_proc\external\yaffs2\Android.mk檔案生成mkyaffs2image可執行程式,用來製作yaffs2檔案系統的映象.

            需要注意的是,製作出來的yaffs image檔案與通常的檔案系統的image檔案不同,因為在image檔案裡除了以512位元組為單位的一個page的data資料外,同時緊跟在後還包括了16位元組為單位的NandFlash備用區OOB的資料。所以實際上是以528個位元組為單位的。就是因為包含了這額外的16位元組/page的資料,所以通常的下載其它型別image的工具就無法正常下載yaffs image了,需要修改你所使用的下載工具的程式碼,使得它能將yaffs image中的這些額外資料也寫入NandFlash OOB中。

          這裡還有一點需要注意的是,通過mkyaffsimage製做出來的image其OOB中也包含它自己計算的ECC校驗資料,其校驗演算法有可能和MTD驅動的校驗演算法不同,如果在核心中由MTD來處理ECC,會造成MTD認為所有的page都校驗錯誤。要解決這個問題通常有兩種方法:

            第一、重新配置核心,進入檔案系統模組的選擇介面,選中"Lets Yaffs doits own ECC",同時將MTD中的ECC校驗關閉。但是這種不校驗資料ECC的做法,在系統使用過程中產生壞塊位時,就會顯示出系統的健壯和容錯性很差。

            第二、通過修改bootloader(比如uboot),在下載檔案系統時,通過硬體計算並讀出資料ECC,並修改image中的ECC後,將OOB資料寫入到晶片中。

在kernel目錄下執行make menuconfig命令,File systems--->Miscellaneous filesystems(NEW)--- >

apps_proc\external\yaffs2 \Android.mk檔案中最後一條語句

$(call dist-for-goals,droid,$(LOCAL_BUILT_MODULE))//呼叫dist-for-goals函式,該函式的作用是將$(LOCAL_BUILT_MODULE)即mkyaffs2image拷貝到droid環境變數所定義的目錄中去(如下所示):

/build/tmp/work/i686-linux/yaffs2-utils-native-git-r1/yaffs2/yaffs2/mkyaffs2image

/build/tmp/work/i686-linux/yaffs2-utils-native-git-r1/home/driver/SRC/X715_0223/apps_proc/build/tmp/sysroots/i686-linux/bin/ mkyaffs2image

/build/tmp/work/i686-linux/yaffs2-utils-native-git-r1/sysroot-destdir/home/driver/SRC/X715_0223/apps_proc/build/tmp/sysroots/i686-linux/bin/ mkyaffs2image

/build/tmp/work/i686-linux/yaffs2-utils-native-git-r1/staging-pkg/sysroots/i686-linux/bin/ mkyaffs2image

/build/tmp/sysroots/i686-linux/bin/ mkyaffs2image

            呼叫mkyaffs2image時會自動到相應的目錄中去找。

4.2.1 9615-cdp-usr-image..usrfs.yaffs2映象檔案的生成

     build/qcom-recipes/recipes/images/9615-cdp-usr-image.inc

        do_make_usr () {

   rm -rf ${USR_IMAGE_ROOTFS}

   mkdir-p ${USR_IMAGE_ROOTFS}

   mv ${IMAGE_ROOTFS}/usr/* ${USR_IMAGE_ROOTFS}

  OUTPUT_FILE=${DEPLOY_DIR_IMAGE}/${USR_IMAGE_BASENAME}.usrfs.yaffs2

           mkyaffs2image ${USR_IMAGE_ROOTFS}${OUTPUT_FILE}

           chmod644 ${OUTPUT_FILE}

    }

4.2.2  micro-msm-9615-cdp-image-eglibc-ipk-20120301-9615-cdp.rootfs.yaffs2映象檔案的生成

        build/openembedded/conf/bitbake.conf:383:IMAGE_CMD_yaffs2= "mkyaffs2image

                        ${EXTRA_IMAGECMD}${IMAGE_ROOTFS} ${DEPLOY_DIR_IMAGE}/${IMAGE_NAME}.rootfs.yaffs2"

5. 根檔案系統相關

5.1 簡介

       boot loader裝入kernel, 然後kernel需要執行/sbin/init, 讀取這個檔案就必須先mount根檔案系統, 早期是通過啟動時的root=引數告訴核心根檔案系統在哪個裝置上,  隨著硬體和技術的發展,現在根檔案系統可能位於一個網路儲存如NFS上, 可能由於RAID而散佈於多個裝置上, 可能位於一個加密裝置上需要提供使用者名稱和密碼,這時root=引數就顯得不夠了. 為了應付這種局面, 先後出現兩種機制來作為boot loader裝載kernel到真正的/sbin/init執行這個啟動過程的橋樑: initrd和initramfs, 兩者有類似的地方, 比如都是由核心執行其上的某個程式(initrd是/linuxrc, initramfs是/init),由這個程式決定載入什麼驅動以及如何裝載根檔案系統.(可參考kernel/init/do_mounts_initrd.c/handle_initrd: pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD))

5.2 ramdisk介紹

       本系統使用ramdisk。

       要想使用RamDisk你必須或是得到核心的支援或是以模組的形式將他載入到系統中。其中核心的配置選項是 CONFIG_BLK_DEV_RAM. ramdisk就是指使用一部分記憶體空間來模擬硬碟分割槽,也就是說ramdisk是一個塊裝置,要用mkfs格式化,才能真正使用它.

.ramdisk在核心2.0/2.2版本就已經支援.

.ramdisk裝置是它不允許重新宣告它的記憶體空間,所以ramdisk塊通常會一直佔用空間的記憶體直到系統重啟.

.ramdisk的一個缺點是在它上面建立一個檔案系統,它同時會消耗緩衝區快取記憶體和檔案系統快取,理論上,它可以消耗一個磁碟檔案的兩倍隨機記憶體.

.ramdisk的另一個缺點是它大小固定,之後不能改變

.現在大多數需要RAM臨時儲存的應用程式會使用tmpfs檔案系統而不是ramdisk裝置.

5.3  initrd介紹

     本系統支援initrd,核心的配置選項是CONFIG_BLK_DEV_INITRD

       initrd 的英文含義是 boot loaderinitialized RAM disk,就是由 boot loader 初始化的記憶體盤。在 linu2.6核心啟動前, boot loader 會將儲存介質中的initrd 檔案載入到記憶體,核心啟動時會在訪問真正的根檔案系統前先訪問該記憶體中的 initrd 檔案系統。在 boot loader 配置了 initrd 的情況下,核心啟動被分成了兩個階段,第一階段先執行 initrd 檔案系統中的init,完成載入驅動模組等任務,第二階段才會執行真正的根檔案系統中的 /sbin/init 程序。

       Initrd的具體實現程是:bootloader把根檔案系統映象裝載到記憶體指定位置,把相關引數傳遞給核心,核心啟動時把initrd中的內容複製到ramdisk中(ram0),把initrd佔用的記憶體釋放掉,在ram0上mount根檔案系統。

核心對initrd 的處理流程:

     boot loader 把核心以及 initrd 檔案(/dev/initrd)載入到記憶體的特定位置。/dev/initrd是由boot loader初始化的裝置,儲存著initrd。

     核心判斷initrd的檔案格式,如果是cpio格式。將initrd的內容釋放到rootfs中。執行initrd中的/init檔案,執行到這一點,核心的工作全部結束,完全交給/init檔案處理。

     如果是initrd 是以 ramdisk 形式存在的,需要額外掛載到 rootfs 上才能使用。具體流程(參考/kernel/init/do_mounts_initrd.c檔案中的handle_initrd函式):

     ①核心把/dev/initrd裝置的內容解壓縮並拷貝到/dev/ram0裝置上。

     ②核心以可讀寫的方式把/dev/ram0裝置掛載為原始的根檔案系統。

     ③如果/dev/ram0被指定為真正的根檔案系統,那麼核心跳至最後一步正常啟動。

     ④執行initrd上的/linuxrc,該執行緒負載載入核心訪問根檔案系統必須的驅動,以及載入根檔案系統。

     ⑤/linuxrc執行完畢,真正的根檔案系統被掛載。

     ⑥如果真正的根檔案系統存在/initrd目錄,那麼/dev/ram0將從/移動到/initrd.否則如果/initrd目錄不存在,/dev/ram0將被解除安裝。

     ⑦在真正的根檔案系統上進行正常啟動過程,執行/sbin/init

5.4  根檔案系統載入過程

(1)  註冊rootfs的檔案系統

       kernel/init/main.c/start_kernel----->vfs_caches_init---->kernel/fs/dcache.c/mnt_init---->kernel/fs/namespace.c/mnt_init---->init_rootfs

(2) 載入根檔案系統

A. cpio包根檔案系統掛載

            static int __init kernel_init(void * unused)

{  

 ... ...  

do_basic_setup();  

if(!ramdisk_execute_command)  

   ramdisk_execute_command = "/init";  

    if (sys_access((const char __user *)ramdisk_execute_command, 0) != 0)

 {      

           ramdisk_execute_command = NULL;    

         prepare_namespace();  

    }  

    init_post();  

    return 0;

            }

            在 kernel_init() 中會呼叫 do_basic_setup(),而 do_basic_setup() 又會呼叫 do_initcalls(),所以 cpio 包型別的 initrd (如果有的話)就會在此時被填充到 rootfs 中去。接下來初始化 ramdisk_execute_command 變數,這個變量表示在 cpio 包中要被執行的第一個程式,可通過在核心啟動引數中給 rdinit= 賦值來指定。接下來檢查在 rootfs 中是否存在變數 ramdisk_execute_command 所指的檔案。如果有,就說明 cpio 包型別的 initrd 成功載入了,那就不需要核心再呼叫 prepare_namespace() 來掛載根檔案系統了,這些都留給 cpio 包裡的 ramdisk_execute_command 所指的程式去完成了;如果沒有,就說明核心並沒有成功用上 cpio 包型別的 initrd,還需要呼叫 prepare_namespace() 來繼續準備載入根檔案系統,並清空變數ramdisk_execute_command。無論怎樣,核心都會繼續執行 init_post()。

填充rootfs時的程式碼流程:

            rootfs_initcall呼叫 initramfs.c 中的 populate_rootfs() 函式來填充 rootfs。如果在 populate_rootfs() 中成功地 unpack_to_rootfs() 的話,之後核心就不會再對 initrd 作任何操作,也不會去掛載根檔案系統,所有的工作都留給 cpio 包(也就是rootfs)中的 /init 去完成了。

B. ramdisk型別根檔案系統掛載

            kernel_init->prepare_namespace->initrd_load()

int __init initrd_load(void)

{

            if (mount_initrd) {

              create_dev("/dev/ram",Root_RAM0);

              /*

               * Load the initrd data into /dev/ram0. Executeit as initrd

               * unless /dev/ram0 is supposed to be ouractual root device,

               * in that case the ram disk is just set uphere, and gets

               * mounted in the normal path.

               */

              if(rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) {

                     sys_unlink("/initrd.image");

                     handle_initrd();

                     return 1;

              }

            }

            sys_unlink("/initrd.image");

            return 0;

}

            變數 mount_initrd 是是否要載入 initrd 的標誌,預設為1,當核心啟動引數中包含 noinitrd 字串時,mount_initrd 會被設為0 。接著為了把 initrd 釋放到記憶體盤中,先需要建立裝置檔案,然後通過 rd_load_image 把之前儲存的 /initrd.image 載入到記憶體盤中。之後判斷如果核心啟動引數中指定的最終的根檔案系統不是記憶體盤的話,那就先要執行 initrd 中的 linuxrc;如果最終的根檔案系統就是剛載入到記憶體盤的 initrd 的話,那就先不處理它,留到之後當真正的根檔案系統處理。

            需要補充的是,只要沒有用到 cpio 型別的 initrd,那核心都會執行到rd_load_image("/initrd.image"),無論是否真的提供了 initrd 。如果沒有提供 initrd,那 /initrd.image 自然不會存在,rd_load_image() 也會提早結束。另外 /dev/ram 這個裝置節點檔案在 rd_load_image() 中用完之後總會被刪除(但相應的記憶體盤中的內容還在)。

            handle_initrd()函式中,用到了一個real_root_dev 變數,它是一個 int 型的全域性變數,它的值從變數 ROOT_DEV 轉換過來。變數 real_root_dev 是和檔案 /proc/sys/kernel/real-root-dev 相關聯的(參見 kernel/sysctl.c 第405行左右),所以如果在執行 initrd 中的 /linuxrc 時改了 /proc/sys/kernel/real-root-dev 的話,就相當於又重新指定了真正的根檔案系統。之所以要新弄一個 real_root_dev 變數,使因為 procfs 不支援改 kdev_t 型的 ROOT_DEV 變數。

            另外,在 rootfs 中會建有兩個裝置檔案節點:/dev/root 是真正的根檔案系統的裝置節點,其裝置號是 ROOT_DEV 的值;/dev/root.old 是 ramdisk 型的 initrd 的裝置節點,其裝置號總是 Root_RAM0 。handle_initrd->mount_root->create_dev("/dev/root",ROOT_DEV);

            走完上述流程回退到prepare_namespace() 函式時就會直接跳到 out: 標籤處,剩下還有兩行程式碼要執行:

out:

sys_mount(".", "/", NULL,MS_MOVE, NULL);

sys_chroot(".");

}

就是把當前目錄替換 rootfs,使其成為 Linux VFS 的根目錄。

rootfs是用來mount根/的檔案系統 ,它可以是任何支援的檔案系統,比如ext2,romfs等

rootfs檔案系統是基於記憶體的檔案系統,也是虛擬的檔案系統,在系統啟動之後,隱藏在真正的根檔案系統後面,不能被解除安裝。

            Initrd可以說是啟動過程中用到的一種機制。就是在裝載linux之前,bootloader可以把一個比較小的根檔案系統的映象裝載在記憶體的某個指定位置,姑且把這段記憶體稱為initrd,然後通過傳遞引數的方式告訴核心initrd的起始地址和大小(也可以把這些引數編譯在核心中),在啟動階段就可以暫時的用initrd來mount根檔案系統。Initrd的最初的目的是為了把kernel的啟動分成兩個階段:在kernel中保留最少最基本的啟動程式碼,然後把對各種各樣硬體裝置的支援以模組的方式放在initrd中,這樣就在啟動過程中可以從initrd所mount的根檔案系統中裝載需要的不同的硬體。在啟動完成的最後階段,根檔案系統可以重新mount到其他裝置上,但是也可以不再重新mount(很多嵌入式系統就是這樣)。Initrd的具體實現過程是這樣的:bootloader把根檔案系統映象裝載到記憶體指定位置,把相關引數傳遞給核心,核心啟動時把initrd中的內容複製到ramdisk中(ram0),把Initrd佔用的記憶體釋放掉,在ram0上mount的根檔案系統不再切換,因為這個時候實際的裝置就是ram0。還有就是Initrd的起始地址引數為虛擬地址,需要和bootloader中用的實體地址對應。

相關推薦

yaffs2檔案系統結構應用

        檔案系統是包括在一個磁碟(包括光碟、軟盤、閃盤及其它儲存裝置)或分割槽中的目錄結構;一個可應用的磁碟裝置可以包含一個或多個檔案系統;如果您想進入一個檔案系統,首先您要做的是掛載(mount)檔案系統;為了掛載(mount)檔案系統,您必須指定一個掛載點(所掛

Linux ext4檔案系統原理-檔案系統結構檔案解析

基本概念 1扇區(sector)=512位元組(byte) 1資料塊(block)=8扇區=4KB(mkfs時指定,預設4KB,可設定為1KB - 64KB) Ext4欄位使用little-endian順序寫入磁碟;但journal日誌使用big-endian順序寫入磁碟。

Linux-(1)Linux樹狀檔案系統結構各資料夾的作用

  Linux檔案系統為一個倒轉的單根樹狀結構.   檔案系統的根為"/"   Linux中所有的東西都是檔案.   如下圖所示:     各資料夾的作用如下:   bin: 存放所有使用者都可以執行的可執行檔案.   boot: 存放系統引導,啟動相關的內容. 其中的

Linux系統安全應用

linux 賬號安全 nmap 弱口令檢測 楊書凡 作為一個開放源代碼的操作系統,Linux服務器以其安全、高效和穩定的顯著優勢而得以廣泛應用。下面主要從賬戶安全、系統引導、登錄控制的角度,優化Linux系統的安全性賬號安全控制 用戶賬號,是計算機使用者的身份憑證,每個訪問系

系統安全應用

需要 自動 uid 完整 切換 設置密碼 b- 掃描 開放 1、賬號安全系統賬號清理將非登錄用戶shell設為/sbin/nologin2、鎖定長期不使用的賬號3、刪除無用的賬號鎖定賬號文件passwd、shadow鎖定:chattr?+i?/etc/passwd?/etc

基於Springboot技術的部落格系統實踐應用之四(Elasticsearch)

本部落格從全文搜尋、ES簡介、ES核心概念、ES與SpringBoot整合以及ES實戰共五個方面進行詳細介紹和應用。 1、全文搜尋介紹 全文搜尋搜尋的物件主要有兩種: 1、結構化資料:具有固定格式或固定長度的資料,例如,資料庫,元資料 2、非結構化資料:無固定格式或者無固定長度的

基於Springboot技術的部落格系統實踐應用之三(Spring Data JPA)

本部落格將從JPA簡介、Spring Data JPA用法介紹、Spring Data JPA、Hibernate與SpringBoot整合以及資料持久化實戰四個方面進行詳細描述Spring Data JPA的用法和應用。 1、JA

基於Springboot技術的部落格系統實踐應用之一

部落格系統功能如下: 涉及到的技術: 通過技術實現,完成程式碼開發,最後系統效果如下圖: 部落格實戰 部落格實戰分三步走: 第一點:主要講解的核心技術: 1、Gradle、Gradle Wrapper 2、開發環境搭建及專案執行 3、 Th

基於Springboot技術的部落格系統實踐應用之二(thymleaf)

一、概念             Thymleaf是一個jave模板引擎,與SpringBoot整合 非常方便,類似於Freemarker,但是比Freemarker效能要好一些;Thymleaf支援自然語言,即:原型就是

ext2檔案系統結構分析

轉載自:https://blog.csdn.net/yuzhihui_no1/article/details/50256713 ext2檔案系統 總體儲存佈局 我們知道,一個磁碟可以劃分成多個分割槽,每個分割槽必須先用格式化工具(例如某種mkfs命令)格式化成某種格式的檔案系統,然後才能

FAT32檔案系統結構分析

        FAT,英文為File Allocation Table,文件分配表。先要記住幾個概念:         扇區:一般扇區為512個位元組。         簇:

FAT16檔案系統結構扇區資料分析

FAT,英文為File Allocation Table,文件分配表。先要記住幾個概念: 扇區:一般扇區為512個位元組。 簇:由若干個扇區組成,是存取資料的最小單位。如果簇大小為16K,檔案大小為1位元組,那也要用一個簇來存,而且該簇不用再拿來他用。 FAT檔案系統就是專門管理這些簇的

linux 檔案系統結構

1 - Linux 和windws 不同,他是沒有碟符的概念的,只有一個根目錄,它是目錄與檔案的源頭,然後從根目錄下一層一層的都是各種檔案。,如同樹枝狀,因此被稱為目錄樹解構。Linux是一切皆檔案。 2 - Linux 是用正斜槓來 " / " 來標識目錄的。 3 - 目錄樹的特點是什麼?   3.1

linux檔案系統結構樹(強烈建議儲存)

根目錄是整個系統最重要的一個目錄,因為不但所有的目錄都是由根目錄衍生出來的, 同時根目錄也與開機/還原/系統修復等動作有關。 由於系統開機時需要特定的開機軟體、核心檔案、開機所需程式、 函式庫等等檔案資料,若系統出現錯誤時,根目錄也必須要包含有能夠修復檔案系

unix/linux 檔案系統結構淺析

一、物理磁碟到檔案系統 檔案系統用來儲存檔案內容、檔案屬性、和目錄。這些型別的資料如何儲存在磁碟塊上的呢?unix/linux使用了一個簡單的方法。如圖所示. 它將磁碟塊分為三個部分: 1)超級塊,檔案系統中第一個塊被稱為超級塊。這個塊存放檔案系統本身的結構資訊。比如

資料庫系統原理應用教程複習筆記(第3 版)

最近在複習資料庫相關知識點,過幾天就要考試了; 第一章 資料庫基礎知識 1、資料庫管理是資料處理的基礎工作,資料庫是資料管理的技術和手段。資料庫中的資料具有整體性和共享性。 1.2、資料庫系統的核心:資料庫管理系統。 1.3、資料庫核心:資料模型; 2、資料庫(DB)是一個按資料結構來儲存和管理資料的

Hadoop分散式檔案系統架構設計要點

Datanode    HDFS採用master/slave架構。一個HDFS叢集是有一個Namenode和一定數目的Datanode組成。Namenode是一箇中心伺服器,負責管理檔案系統的namespace和客戶端對檔案的訪問。Datanode在叢集中一般是一個節點一個,負責管理節點上它們附帶的儲存。在內

[原始碼和文件分享]NTFS檔案系統介紹檔案定位

背景 在日常生活中,我們開啟我們的電腦操作各種檔案。我們都知道,檔案資料都儲存在硬碟上,但是,硬碟中儲存的資料都是0、1的二進位制資料,我們的電腦怎麼從這一大堆0、1資料中知道哪個是哪個檔案呢? 這就是檔案系統在起作用,它對硬碟的資料設定格式規則,儲存資料的時候,就按這個儲存規則進行儲存,那

各種分散式檔案系統簡介適用場景

常見的分散式檔案系統有,GFS、HDFS、Lustre 、Ceph 、GridFS 、mogileFS、TFS、FastDFS等。各自適用於不同的領域。它們都不是系統級的分散式檔案系統,而是應用級的分散式檔案儲存服務。 Google學術論文,這是眾多分散式

redis 資料結構應用場景

1. String 常用命令: get、set、incr、decr、mget等 應用場景: String是最常用的資料型別,普通的key/value都可以歸為此類,value其實不僅是String,也可以是數字。 比如想知道什麼時候封鎖一個IP地址(訪問超過幾次)。INCRBY命令讓這些變得很容易,