• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            天衣有縫

            冠蓋滿京華,斯人獨憔悴~
            posts - 35, comments - 115, trackbacks - 0, articles - 0
               :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

            7課:文件系統    下載源代碼

            聲明:轉載請保留

            譯者http://www.shnenglu.com/jinglexy

            原作者:xiaoming.mo at skelix dot org

            MSN & Email: jinglexy at yahoo dot com dot cn

             

             

            目標

             

            這一課中,將創建一個磁盤分區,并在她上面建立文件系統。文章看起來比較長,但是過程比較簡單。




            大家都知道,硬盤最小的管理單位是扇區,每個扇區512字節。有兩種方式定位訪問磁盤。一種是CHS模式(Cylinder, Head, Sector),她反映了磁盤的物理結構。扇區(Sector)是磁盤可以直接訪問的最小數據單位(即一個塊)。柱面(Cylinder)也成為磁軌,通常一個1.44M軟盤每柱面有18扇區,而硬盤每柱面含有64扇區。磁頭(Head)表示有多少個盤面(通常每個盤面使用兩個磁頭,正反盤面各一個)。關于CHS相關的資料網絡上可以搜索出很多,這里不贅述。
            例如我建立了一個100M字節的磁盤映象文件,在bocsh可以這樣定義它的結構:

            ata0-master: type=disk, path="minix.img", mode=flat, cylinders=200, heads=16, spt=63

            計算:16 * 64 * 200 * 512 = 100 M512是每扇區字節數)

            很明顯,在CHS模式下,磁盤容量最大可表示為  柱面數 * 每柱面扇區數 * 磁頭數 * 512. 注意:柱面和磁頭從0開始計數,而扇區數則從1開始。不要問我為什么。


            另一種硬盤訪問模式叫LBALinear Block Addressing:線性塊尋址模式),它是物理硬盤的邏輯尋址方式。和線性內存尋址一樣,LBA用線性地址來定位扇區(這樣,柱面和磁頭就不會用到了)。這種方式非常簡單,但是驅動使用的是CHS模式,所以需要進行轉換(LBA也是從0開始計數的)。

            LBA =C-Cs*PH*PS+H-Hs*PS+S-Ss
            磁盤大小LBA = C * H * S - 1

            方向計算(由LBA獲取CHS:下面的公式好像有問題,讀者最好在網上找到正確的資料):
            C = LBA / (heads per cylinder * sectors per track)
            temp = LBA % (heads per cylinder * sectors per track)
            H = temp / sectors per track
            S = temp % sectors per track + 1

            CHSLBA之間,其實相當于seg:offset與線形地址的關系。不同點在于:

            1. CHS是三維的,seg:offset是二維的
            2. LBA的空間更大2^32,CHS空間很小2^24(不考慮sector不能取0的問題)

            如果你使用vmware建立一個虛擬磁盤,可以在啟動時進入bios看到該虛擬磁盤的 CHS LBA 信息。

            下面三個demo對應的源程序分別為dpt(分區表disk partition table),fsroot目錄。


            我們可以定義一個數據結構:


            07/dpt/hd.c

            struct HD_PARAM {
                unsigned int cyl;
                unsigned int head;
                unsigned int sect;
            } HD0 = {208, 16, 63};        //
            定義一個104M磁盤

             

            由于skelix使用LBA尋址,所以需要把LBA地址轉換成CHS地址:
                unsigned int cyl = lba / (HD0.head * HD0.sect);
                unsigned int head = (lba % (HD0.head * HD0.sect)) / HD0.sect;
                unsigned int sect = (lba % (HD0.head * HD0.sect)) % HD0.sect + 1;


            現在已經得到chs地址了,我們將通過 0x1F0-0x1F7端口來訪問硬盤。

            (這些繁文縟節一點也不好玩,但是我又不得不講)


            07/include/hd.h

            #define HD_PORT_DATA         0x1f0
            #define HD_PORT_ERROR        0x1f1
            #define HD_PORT_SECT_COUNT   0x1f2
            #define HD_PORT_SECT_NUM     0x1f3
            #define HD_PORT_CYL_LOW      0x1f4
            #define HD_PORT_CYL_HIGH     0x1f5
            #define HD_PORT_DRV_HEAD     0x1f6
            #define HD_PORT_STATUS       0x1f7
            #define HD_PORT_COMMAND      0x1f7

             

            另人恐慌,不過可以很清楚的來個分組。我們從0x1f0端口讀取數據,

            如果發生了錯誤就從0x1f1讀取錯誤碼,

            0x1f20x1f3端口設置扇區號,

            0x1f40x1f5端口設置柱面號,

            0x1f6端口設置磁頭號。

            端口0x1f7用于讀硬盤狀態或者寫操作命令。


            0x1f7端口常用的命令字如下:
            #define HD_READ              0x20        //
            讀硬盤
            #define HD_WRITE             0x30        //
            寫硬盤

            通過上面定義,我們可以粗略的認為通過以下幾個步驟訪問硬盤,

            (為了簡化步驟,暫時不做一些錯誤檢測)

            下面這個函數hd_rw就是用來操作硬盤的接口。


            07/hd.c

                while ((inb(HD_PORT_STATUS)&0xc0)!=0x40)
                //
            等待硬盤狀態,直到可以寫或讀為止,狀態字節說明如下:

            Bit 7

            控制器忙

            Bit 6

            驅動器準備就緒

            Bit 5

            寫出錯

            Bit 4

            尋道結束

            Bit 3

            數據請求復位

            Bit 2

            ECC效驗錯誤

            Bit 1

            硬盤已收到索引

            Bit 0

            命令執行錯誤

                outb(sects_to_access, HD_PORT_SECT_COUNT); // 準備讀或寫多少個扇區

             

                outb(sect, HD_PORT_SECT_NUM);              // 發送chs地址
                outb(cyl, HD_PORT_CYL_LOW);
                outb(cyl>>8, HD_PORT_CYL_HIGH);
                outb(0xa0|head, HD_PORT_DRV_HEAD);         // a0
            是第一塊硬盤

            Bits 7-5

            必須是 101b

            Bit 4

            HD0(=0第一塊硬盤), HD1(=1第二塊硬盤)

            Bits 3-0

            磁頭號

             

                outb(com, HD_PORT_COMMAND);                // 硬盤操作命令

            HD_READ=0x20

            如果不成功會反復讀

            HD_WRITE=0x30

            如果不成功會反復寫

                if (com == HD_READ)
                    insl(HD_PORT_DATA, buf, sects_to_access<<7);
                else if (com == HD_WRITE)
                    outsl(buf, sects_to_access<<7, HD_PORT_DATA);

                // 說明:insloutsl是從io端口讀寫一串數據的宏匯編指令,

                // 這里使用的是pio方式,mdmaudma不作討論


                while ((inb(HD_PORT_STATUS)&0x80));        //
            等待完成

            事實上,這只是最簡單的處理流程,連錯誤檢測都沒有。雖然是pio方式,

            仍然可以使用中斷,以避免上面的while循環等待,而減少內核浪費的時間。

            不過skelix不準備做這么復雜。

             

            磁盤分區表disk partitiontable,以下簡稱dpt

            現在我們有能力訪問硬盤的扇區了,需要把這些扇區管理起來。硬盤的第一個扇區必須包含硬盤分區表。這個分區表從第一個扇區的0x1be偏移開始,長度是64字節。最多可以包含4個分區(本文不考慮邏輯分區,都使用主分區)。這4個分區的相對偏移分別為0x1be, 0x1ce, 0x1de, 0x1fe,第一個扇區的最后兩個字節必須是0xaa55。

            每個分區項的格式如下:

            Byte  0

            引導標識

            Bytes 1-3

            CHS 信息

            Byte  4

            分區類型

            Bytes 5-7

            CHS 信息

            Bytes 8-11

            該分區的起始扇區號

            Bytes 12-16

            扇區數量

             

            0個字節是引導標識,如果值為0x80標識可引導。對于一塊硬盤來說,只有一個分區是可以引導的。第4個字節定義分區類型,例如FAT32, NTFS, ext2等。有一篇文章http://www.osdever.net/documents/partitiontypes.php?the_id=35,里面定義了常見的分區類型。

            從上面的表可以看到dpt項有兩種方式定位扇區,一種是通過字節1~3和字節5~7中的CHS信息,另一種是字節8~16LBA信息。隨便用哪一種都是一樣的,在本文中使用LBA方式,所以我不準備解釋字節1~3和字節5~7的具體格式了。

            現在我們來建立分區表:
            07/dpt/hd.c

             

            static void
            setup_DPT(void) {
                unsigned char sect[512] = {0};
                sect[0x1be] = 0x80;             //
            第一個分區可引導

                sect[0x1be + 0x04] = FST_FS;    //
            自定義的數據分區類型
                *(unsigned long *)&sect[0x1be + 0x08] = 1;
                *(unsigned long *)&sect[0x1be + 0x0c] = 85*1024*2; /* 85MB */

                sect[0x1ce + 0x04] = FST_SW;    //
            自定義的交換分區類型,后續課程使用
                *(unsigned long *)&sect[0x1ce + 0x08] = 85*1024*2+1;
                *(unsigned long *)&sect[0x1ce + 0x0c] = 16*1024*2; /* 16MB */

                sect[0x1fe] = 0x55;
                sect[0x1ff] = 0xaa;

                hd_rw(0, HD_WRITE, 1, sect);    //
            寫入磁盤

                // 寫到扇區0,扇區數為1sect是寫入緩沖
            }

            現在,我們在啟動的過程中把分區表信息打印出來:
            07/dpt/hd.c

            void
            verify_DPT(void) {
                unsigned char sect[512];
                unsigned i = 0;
                unsigned int *q = (unsigned int *)(HD0_ADDR);

                // 變量q存放讀出的分區表(起始扇區號和扇區數量)數組

                hd_rw(0, HD_READ, 1, sect);
                if ((sect[0x1fe]==0x55) && (sect[0x1ff]==0xaa)) {
                    unsigned char *p = &sect[0x1be];
                    char *s;
                    kprintf(KPL_DUMP, "   | Bootable | Type      | Start Sector | Capacity \n");

                    for (i=0; i<4; ++i) {
                        kprintf(KPL_DUMP, " %d ", i);
                        if (p[0x04] == 0x00) {
                            kprintf(KPL_DUMP, "| Empty\n");
                            p += 16;
                            q += 2;
                            continue;
                        }
                        if (p[0x00] == 0x80)
                            s = "| Yes      ";
                        else
                            s = "| No       ";

                        kprintf(KPL_DUMP, s);
                        /* system indicator at offset 0x04 */
                        if (p[0x04] == FST_FS) {
                            kprintf(KPL_DUMP, "| Skelix FS ");
                        } else if (p[0x04] == FST_SW) {
                            kprintf(KPL_DUMP, "| Skelix SW ");
                        } else
                            kprintf(KPL_DUMP, "| Unknown   ", *p);

                        /* starting sector number */
                        *q++ = *(unsigned long *)&p[0x08];
                        kprintf(KPL_DUMP, "| 0x%x   ", *(unsigned long*)&p[0x08]);
                        /* capacity */
                        *q++ = *(unsigned long*)&p[0x0c];
                        kprintf(KPL_DUMP, "| %dM\n", (*(unsigned long*)&p[0x0c]*512)>>20);

                        // 保存到內存中,32字節偏移,32字節長度

                        p += 16;
                    }
                } else {
                    kprintf(KPL_DUMP, "No bootable DPT found on HD0\n");
                    kprintf(KPL_DUMP, "Creating DPT on HD0 automaticly\n");
                    kprintf(KPL_DUMP, "Creating file system whatever you want it or not!!\n");
                    setup_DPT();
                    verify_DPT();
                }
            }

            在我們編譯觀察結果之前,還需要修改任務函數task1_run  task2_run,因為它們會滾動屏幕把我們想要的結果覆蓋掉。
            07/init.c

            void
            do_task1(void) {
                __asm__ ("incb 0xb8000+160*24+2");
            }

            void
            do_task2(void) {
                __asm__ ("incb 0xb8000+160*24+4");
            }

            按例,還得改改Makefile,加入 hd.o  KERNEL_OBJS, 并在sti() 之前就調用 verify_DPT()函數:
            07/dpt/Makefile

            KERNEL_OBJS= load.o init.o isr.o timer.o libcc.o scr.o kb.o task.o kprintf.o hd.o exceptions.o

             

            07/dpt/init.c

                __asm__ ("ltrw    %%ax\n\t"::"a"(TSS_SEL));
                __asm__ ("lldt    %%ax\n\t"::"a"(LDT_SEL));

                kprintf(KPL_DUMP, "Verifing disk partition table....\n");
                verify_DPT();      /* <<<<< Here it is */

                sti();             //
            任務調度可以進行了

             

            編譯運行一把,OK?。ㄗ詈檬褂靡粋€未分區的磁盤映象來測試)


            文件系統

            分區已經建立,下一步就是組織各個分區上的文件系統。雖然我們可以做到訪問扇區了,但是對于訪問文件卻是不方便的。需要做一些結構化的工作,為此定義了一個表示文件的數據結構:
            07/fs/include/fs.h

             

            #define FT_NML    1             /* normal file */
            #define FT_DIR    2

            struct INODE {                  /*
            存放在硬盤里面,在inode */
                unsigned int i_mode;        /* file mode */
                unsigned int i_size;        /* size in bytes */
                unsigned int i_block[8];
            };
            *nix
            用戶可能對inode比較敏感?,F在我來一一解釋這個數據結構中的域,i_mode定義文件類型。FT_NML 表示這是一個普通文件,FT_DIR 表示是一個目錄。i_size 是文件大小,對于文件夾則是另外意思,后面將會講到。i_block的前6個整形表示文件的前6個扇區號,第七個表示二級指針扇區(即它指向一個扇區,這個扇區里面存放文件后續部分使用扇區號),由 512 / 4 = 128 扇區,表示文件接下來使用的128個扇區。128 * 512 = 64K。i_block數組的最后一個表示三級指針,最大可以表示 (512 / 4) * (512 / 4) * 512 = 8MB字節。所以這個i_block數組最大可以表示 3k + 64K + 8M文件的大小,雖然比較小,但是對于我們的85M 分區來說已經足夠了。最重要的是,它比較簡單。舉例來說把,這個文件節點使用了如下扇區序列: 13, 7, 9, 16, 257, 57, 3, ....., 35, 33, ....., 55, ......., 128, ....., 81.


            對于目錄(也是一種文件)來說,它以類似數組的形式組織: {{file_name, file_inode}, {file_name, file_inode}, {file_name, file_inode}, },定義如下:


            07/fs/include/fs.h

            #define MAX_NAME_LEN 11

            struct DIR_ENTRY {            /
            存放在硬盤里面,在data */

                char de_name[MAX_NAME_LEN];
                int de_inode;
            };

            操作系統中的所有文件都有一個獨一無二的節點編號,如果有了這個節點號,就可以找到對應的文件。最開始的兩個文件永遠是"." "..",表示當前目錄和上級目錄,如果我們切換到下級目錄,可以通過".."來回到上一級。"/"表示最上級目錄,它沒有父節點。
            舉例來說,我們需要定位到 /usr/doc/fvwm/TODO 文件,首先我們找到"/"文件,然后搜索這個文件項下面的doc文件,因為"/"是個目錄,所以先得到"/"目錄的節點編號,然后搜索指向的節點表。然后再搜索到fvwm目錄,并且在這個目錄的節點表中搜索到"TODO"這個文件,并通過"TODO"的節點編號來定位節點這個文件的節點數據結構。最后就可以訪問i_block數組了,也就是可以訪問這個文件了。怎么自己看的都昏菜了,s**t!

            還有兩個問題,一個是需要指定從哪里搜索節點號,我們在磁盤中組織所有節點為數組,并由節點號來索引節點數組。另一個問題是,"/"沒有父節點,需要知道"/"存放在什么地方,這個也好辦,就放在節點數組的第一項好了。

            文件名聲明成12字節,這樣每個節點將占用16字節(另4字節是節點編號),這樣方便磁盤IO之后的一些操作。當磁盤使用一段時間后,有的節點使用了,有的節點沒有使用,那怎么知道呢?一種方法是建立一個位圖表,每個位表示inode數組中的一項。

            07/fs/include/fs.h

            struct SUPER_BLOCK {
                unsigned char sb_magic;    /*
            分區類型 FST_FS  FST_SW *'
                
                unsigned int sb_start;     /* DPT 0x08:
            起始扇區 */
                unsigned int sb_blocks;    /* DPT 0x0c:
            扇區數量 */

                unsigned int sb_dmap_blks;
                unsigned int sb_imap_blks;
                unsigned int sb_inode_blks;
            };

             

            這個超級塊的數據結構用來管理各個分區。例如,下面是一個磁盤分區:
             ________________________________________________________

            //// | \\\\ | //// | \\\\ | //// |       data          | 
             --------------------------------------------------------

            每個分區的第一個扇區(藍色)是boot secotr,我不打算使用它,一個扇區大小。

            第二個扇區(綠色)是超級塊(super block,以下簡稱sb),一個扇區大小。

            黑色是dmap,336個扇區大小。

            紅色是imap,一個扇區大小。

            灰色是inodes,將占有342block,即342 * 8 個扇區大小。

            為了管理這個85M分區,我們額外花了 1.5M 的空間。

             

            verify_fs()函數中定義了超級塊(局部變量)sb,為了方便訪問定義了一些宏,獲取相對整個硬盤的絕對地址(LBA)

            07/fs/incude/fs.h

            #define ABS_BOOT_BLK(sb)        ((sb).sb_start)
            #define ABS_SUPER_BLK(sb)        ((ABS_BOOT_BLK(sb))+1)
            #define ABS_DMAP_BLK(sb)        ((ABS_SUPER_BLK(sb))+1)
            #define ABS_IMAP_BLK(sb)        ((ABS_DMAP_BLK(sb))+(sb).sb_dmap_blks)
            #define ABS_INODE_BLK(sb)        ((ABS_IMAP_BLK(sb))+(sb).sb_imap_blks)
            #define ABS_DATA_BLK(sb)        ((ABS_INODE_BLK(sb))+INODE_BLKS)

             

            說明:dmapdata map)存放的是扇區使用位圖

                  imapinode map)存放inode使用位圖。

                  inodes存放節點表。

            為了方便,一些位的操作函數如下:

            07/fs/fs.c

            void
            setb(void *s, unsigned int i) {
                unsigned char *v = s;
                v += i>>3;            // i
            的單位由位轉換成字節
                *v |= 1<<(7-(i%8));
            }

            void
            clrb(void *s, unsigned int i) {
                unsigned char *v = s;
                v += i>>3;
                *v &= ~(1<<(7-(i%8)));
            }

            int
            testb(void *s, unsigned int i) {
                unsigned char *v = s;
                v += i>>3;
                return (*v&(1<<(7-(i%8)))) !=0;
            }

            例如,設置緩沖區sect1796位,可以使用setb(sect, 1796)

            init.c在調用verify_DPT();創建分區表后,緊接著調用verify_fs();創建文件系統:
            07/fs/fs.c

            void
            verify_fs(void) {
                unsigned int *q = (unsigned int *)(HD0_ADDR);
                unsigned char sect[512] = {0};
                struct SUPER_BLOCK sb;
                unsigned int i = 0, j = 0, m = 0, n = 0;

                /* 
            讀取超級塊 */
                sb.sb_start = q[0];
                hd_rw(ABS_SUPER_BLK(sb), HD_READ, 1, &sb);

                // 很難想象這段代碼不越界,正好越界到sect上了?昏菜!


                /*
            判斷超級塊是否正確,不是就創建文件系統 */
                if (sb.sb_magic != FST_FS) {
                    kprintf(KPL_DUMP, "Partition 1 does not have a valid file system\n");
                    kprintf(KPL_DUMP, "Creating file system\t\t\t\t\t\t\t  ");
                    sb.sb_magic = FST_FS;
                    sb.sb_start = q[0];
                    sb.sb_blocks = q[1];
                    sb.sb_dmap_blks = (sb.sb_blocks+0xfff)>>12;
                    sb.sb_imap_blks = INODE_BIT_BLKS;
                    sb.sb_inode_blks = INODE_BLKS;
                    hd_rw(ABS_SUPER_BLK(sb), HD_WRITE, 1, &sb);

             

                    // dmap位圖中,每個位表示1個扇區,也就是說dmap中每個扇區可以標識512 * 8扇區。

                    // 另外,我們把inode位圖大小固定,即使用1個扇區。
                    /* 
            初始化dmap位圖 */
                    n = ABS_DMAP_BLK(sb);
                    j = sb.sb_dmap_blks+sb.sb_imap_blks+sb.sb_inode_blks+2;
                    memset(sect, 0xff, sizeof sect/sizeof sect[0]);
                    for (i=j/(512*8); i>0; --i) {
                        hd_rw(n++, HD_WRITE, 1, sect);
                        m += 4096;
                    }
                    m += 4096;
                    for (i=j%(512*8); i<512*8; ++i) {
                        clrb(sect, i);
                        --m;
                    }
                    hd_rw(n++, HD_WRITE, 1, sect);

                    memset(sect, 0, sizeof sect/sizeof sect[0]);
                    for (i=sb.sb_imap_blks-(n-ABS_DMAP_BLK(sb)); i>0; --i)
                        hd_rw(n++, HD_WRITE, 1, sect);

                    /*
            初始化inode 位圖 */
                    for (i=sb.sb_imap_blks; i>0; --i)
                        hd_rw(ABS_IMAP_BLK(sb)+i-1, HD_WRITE, 1, sect);

                    /*
            初始化inode 數組 */
                    for (i=sb.sb_inode_blks; i>0; --i)
                        hd_rw(ABS_INODE_BLK(sb)+i-1, HD_WRITE, 1, sect);
                    kprintf(KPL_DUMP, "[DONE]");
                }
                q += 2;

                kprintf(KPL_DUMP, "0: Type: FST_FS ");
                kprintf(KPL_DUMP, "start at: %d ", sb.sb_start);
                kprintf(KPL_DUMP, "blocks: %d ", sb.sb_blocks);
                kprintf(KPL_DUMP, "dmap: %d ", sb.sb_dmap_blks);
                kprintf(KPL_DUMP, "imap: %d ", sb.sb_imap_blks);
                kprintf(KPL_DUMP, "inodes: %d\n", sb.sb_inode_blks);

                /* 
            初始化交互分區 */
                sb.sb_start = q[0];
                hd_rw(ABS_SUPER_BLK(sb), HD_READ, 1, &sb);
                if (sb.sb_magic != FST_SW) {

                    // 注意,和數據分區不同(每個塊占有1個扇區),

                    // 交互分區每個塊占有8個扇區,即4096字節,和內存頁對齊

                    kprintf(KPL_DUMP, "\nPartition 2 does not have a valid file system\n");
                    kprintf(KPL_DUMP, "Creating file system\t\t\t\t\t\t\t  ");
                    sb.sb_magic = FST_SW;
                    sb.sb_start = q[0];
                    sb.sb_blocks = q[1];
                    sb.sb_dmap_blks = (sb.sb_blocks)>>15;    /* 1 bits == 4K page */
                    hd_rw(ABS_SUPER_BLK(sb), HD_WRITE, 1, &sb);
                    kprintf(KPL_DUMP, "[DONE]");   
                }
                /* 
            初始化數據位圖 */
                n = ABS_DMAP_BLK(sb);
                j = sb.sb_dmap_blks+2;
                memset(sect, 0xff, sizeof sect/sizeof sect[0]);
                for (i=j/(512*8); i>0; --i) {
                    hd_rw(n++, HD_WRITE, 1, sect);
                    m += 4096;
                }
                m += 4096;
                for (i=j%(512*8); i<512*8; ++i) {
                    clrb(sect, i);
                    --m;
                }
                hd_rw(n++, HD_WRITE, 1, sect);

                kprintf(KPL_DUMP, "1: Type: FST_SW ");
                kprintf(KPL_DUMP, "start at: %d ", sb.sb_start);
                kprintf(KPL_DUMP, "blocks: %d ", sb.sb_blocks);
                kprintf(KPL_DUMP, "dmap: %d, presents %d 4k-page\n",
                        sb.sb_dmap_blks, sb.sb_blocks>>3);
            }
            最后修改Makefile,然后make clean && make dep && make
            07/fs/Makefile

            KERNEL_OBJS= load.o init.o isr.o timer.o libcc.o scr.o kb.o task.o kprintf.o hd.o exceptions.o fs.o
            編譯,運行。

             

            root 根目錄

            最后一件事情建立根目錄。"/"是所有文件的根目錄,所以我們一開始就必須設置好它。"/"文件永遠使用inode 0,這樣skelix內核才知道怎樣找到它。然后再讀取"/"文件的內容,也就是DIR_ENTRY結構數組。為了更方便的操作,我們先完成一些基礎函數,用來操作blocksinodes。

            07/root/fs.c

            static struct INODE iroot = {FT_DIR, 2*sizeof(struct DIR_ENTRY), {0,}};

            unsigned int
            alloc_blk(struct SUPER_BLOCK *sb) {

                // 根據dmap位圖查找空閑的扇區,返回LBA地址(從1開始)
                unsigned int i = 0, j = 0, n = 0, m = 0;
                unsigned char sect[512] = {0};

                n = ABS_DMAP_BLK(*sb);
                for (; i<sb->sb_dmap_blks; ++i) {
                    hd_rw(n, HD_READ, 1, sect);
                    for (j=0; j<512*8; ++j) {
                        if (testb(sect, j)) {
                            ++m;
                        } else {            /* gotcha */
                            setb(sect, j);
                            if (m >= sb->sb_blocks)
                                return 0;
                            else {
                                hd_rw(n, HD_WRITE, 1, sect);
                                return ABS_BOOT_BLK(*sb) + m;
                            }
                        }
                    }
                    ++n;
                }
                return 0;
            }

            void
            free_blk(struct SUPER_BLOCK *sb, unsigned int n) {

                // 釋放一個扇區:設置dmap位圖中對應的位即可
                unsigned char sect[512] = {0};
                unsigned int t = (n-ABS_BOOT_BLK(*sb))/(512*8)+ABS_DMAP_BLK(*sb);
                hd_rw(t, HD_READ, 1, sect);
                clrb(sect, (n-ABS_BOOT_BLK(*sb))%(512*8));
                hd_rw(t, HD_WRITE, 1, sect);
            }

            static int
            alloc_inode(struct SUPER_BLOCK *sb) {

                // imap表中中找一個空閑的項
                unsigned char sect[512] = {0};
                int i = 0;
                hd_rw(ABS_IMAP_BLK(*sb), HD_READ, 1, sect);
                for (; i<512; ++i) {
                    if (! testb(sect, i)) {
                        setb(sect, i);
                        hd_rw(ABS_IMAP_BLK(*sb), HD_WRITE, 1, sect);
                        break;
                    }
                }
                return (i==512)?-1:i;
            }

            static void
            free_inode(struct SUPER_BLOCK *sb, int n) {

                // 釋放inode
                unsigned char sect[512] = {0};
                hd_rw(ABS_IMAP_BLK(*sb), HD_READ, 1, sect);
                clrb(sect, n);
                hd_rw(ABS_IMAP_BLK(*sb), HD_WRITE, 1, sect);
            }

            // 上面4個函數就是針對dmapimap的操作(申請,釋放)

            static struct INODE *
            iget(struct SUPER_BLOCK *sb, struct INODE *inode, int n) {
                unsigned char sect[512] = {0};
                int i = n / INODES_PER_BLK;
                int j = n % INODES_PER_BLK;

                hd_rw(ABS_INODE_BLK(*sb)+i, HD_READ, 1, sect);
                memcpy(inode, sect+j*sizeof(struct INODE), sizeof(struct INODE));
                return inode;
            }

            static void
            iput(struct SUPER_BLOCK *sb, struct INODE *inode, int n) {
                unsigned char sect[512] = {0};
                int i = n/INODES_PER_BLK;
                int j = n%INODES_PER_BLK;
                hd_rw(ABS_INODE_BLK(*sb)+i, HD_READ, 1, sect);
                memcpy(sect+j*sizeof(struct INODE), inode, sizeof(struct INODE));
                hd_rw(ABS_INODE_BLK(*sb)+i, HD_WRITE, 1, sect);
            }

            // 上面兩個函數分別完成讀/寫磁盤指定下標號對應的inode節點到內存中。

            // 需要注意的是,這些函數對競態條件做處理,因為skelix僅內核讀寫硬盤。

            // 本文中暫時沒有用戶態的多任務。

            主流程如下:
            07/root/fs.c

            static void
            check_root(void) {
                struct SUPER_BLOCK sb;
                unsigned char sect[512] = {0};
                struct DIR_ENTRY *de = NULL;

                sb.sb_start = *(unsigned int *)(HD0_ADDR);
                hd_rw(ABS_SUPER_BLK(sb), HD_READ, 1, sect);
                memcpy(&sb, sect, sizeof(struct SUPER_BLOCK));
                hd_rw(ABS_IMAP_BLK(sb), HD_READ, 1, sect);

                // 加載imap扇區,判斷"/"目錄有沒有創建
                if (! testb(sect, 0)) {            // "/"
            目錄必須使用inode 0,否則halt
                    kprintf(KPL_DUMP, "/ has not been created, creating....\t\t\t\t\t  ");
                    if (alloc_inode(&sb) != 0) {   //
            分配節點號:即imap位圖中的一位
                        kprintf(KPL_PANIC, "\n/ must be inode 0!!!\n");
                        halt();
                    }

                    iroot.i_block[0] = alloc_blk(&sb);    //
            節點分配一個塊(一個扇區)
                    iput(&sb, &iroot, 0);                 //
            寫入節點

             

                    de = (struct DIR_ENTRY *)sect;
                    strcpy(de->de_name, ".");
                    de->de_inode = 0;                     //
            節點號為0
                    ++de;
                    strcpy(de->de_name, "..");
                    de->de_inode = -1;                    //
            節點號為-1,這樣我們就知道是最上層目錄了
                    hd_rw(iroot.i_block[0], HD_WRITE, 1, sect);    //
            寫入"." ".."文件夾

                    kprintf(KPL_DUMP, "[DONE]");
                }
                iget(&sb, &iroot, 0);
                hd_rw(iroot.i_block[0], HD_READ, 1, sect);
                de = (struct DIR_ENTRY *)sect;

                if ((strcmp(de[0].de_name, ".")) || (de[0].de_inode) ||
                    (strcmp(de[1].de_name, "..")) || (de[1].de_inode) != -1) {
                    kprintf(KPL_PANIC, "File system is corrupted!!!\n");
                    halt();
                }
            }

             

            // 再來一個函數打印文件的相關信息
            static void
            stat(struct INODE *inode) {
                unsigned int i = 0;
                char sect[512] = {0};
                struct DIR_ENTRY *de;

                kprintf(KPL_DUMP, "======== stat / ========\n");
                switch (inode->i_mode) {
                case FT_NML:
                    kprintf(KPL_DUMP, "File, ");
                    break;
                case FT_DIR:
                    kprintf(KPL_DUMP, "Dir,  ");
                    break;
                default:
                    kprintf(KPL_PANIC, "UNKNOWN FILE TYPE!!");
                    halt();
                }

                kprintf(KPL_DUMP, "Size: %d, ", inode->i_size);
                kprintf(KPL_DUMP, "Blocks: ");
                for (; i<8; ++i)        //
            打印inode標識使用的扇區
                    kprintf(KPL_DUMP, "%d, ", inode->i_block[i]);

                hd_rw(inode->i_block[0], HD_READ, 1, sect);
                switch (inode->i_mode) {
                case FT_DIR:
                    kprintf(KPL_DUMP, "\nName\tINode\n");
                    de = (struct DIR_ENTRY *)sect;    //
            打印子目錄(只一個扇區)
                    for (i=0; i<inode->i_size/sizeof(struct DIR_ENTRY); ++i) {
                        kprintf(KPL_DUMP, "%s\t%x\n", de[i].de_name, de[i].de_inode);
                    }
                    break;
                default:
                    break;
                }
            }

            現在,我們把上面的函數整理到程序中
            void
            verify_dir(void) {
                unsigned char sect[512] = {0};
                unsigned int *q = (unsigned int *)(HD0_ADDR);
                struct INODE inode;
                struct SUPER_BLOCK sb;

                sb.sb_start = q[0];
                hd_rw(ABS_SUPER_BLK(sb), HD_READ, 1, sect);
                check_root();
                memcpy(&sb, sect, sizeof(struct SUPER_BLOCK));
                stat(iget(&sb, &inode, 0));
            }

            07/root/init.c

            void
            init(void) {

                ……

                kprintf(KPL_DUMP, "Verifing disk partition table....\n");
                verify_DPT();
                kprintf(KPL_DUMP, "Verifing file systes....\n");
                verify_fs();
                kprintf(KPL_DUMP, "Checking / directory....\n");
                verify_dir();

                ……

            }

            不需要再編輯Makefile了,直接make && run好了。

             

            Feedback

            # re: 自己動手寫內核(第7課:文件系統)(原創)  回復  更多評論   

            2007-10-26 16:57 by fggh gqx wq
            我干死了。。。。
            以后轉貼的時候。別那么大臉寫原創。。。。不怕撞死。。。
            還有。。。轉帖也要有責任心。。拜托大哥排一下版。。。

            # re: 自己動手寫內核(第7課:文件系統)(原創)  回復  更多評論   

            2007-10-26 21:27 by Minidx全文檢索
            汗……真的假的……

            # re: 自己動手寫內核(第7課:文件系統)(原創)  回復  更多評論   

            2007-10-30 18:25 by 補考少年
            不想辯駁什么,每天都很忙,我也有自己的事情要做。
            一開始也沒有打算譯這些文章,至于排版,等你讀完整個系列,就會明白我為什么不用圖片了。如果你還堅持我是在轉貼,可以用google和baidu搜一下(搜文章內容即可),好像沒有哪個站點有完整的“原創”吧?

            # re: 自己動手寫內核(第7課:文件系統)(原創)  回復  更多評論   

            2007-12-25 16:24 by ytmf xa ypm
            補考少年 要努力啊....

            別聽上邊那個SZ瞎說...走自己的路...
            支持你的原創....

            # re: 自己動手寫內核(第7課:文件系統)(原創)  回復  更多評論   

            2008-07-24 14:21 by 龐帆
            感覺你的翻譯對比原文還有很多細節沒能夠表達出來
            久久99国产一区二区三区| 久久午夜伦鲁片免费无码| 国内精品伊人久久久久AV影院| 国产精品美女久久久久AV福利| 久久精品午夜一区二区福利| 久久婷婷人人澡人人爽人人爱| 久久免费视频一区| 人妻无码精品久久亚瑟影视| 狠狠久久综合| 亚洲国产成人久久综合区| 欧美与黑人午夜性猛交久久久 | 性做久久久久久久久老女人| 国产精品久久久久久久午夜片 | 久久91精品国产91久久户| 久久精品国产久精国产思思| 久久香蕉国产线看观看精品yw| 日韩精品久久久肉伦网站| 久久精品国产亚洲AV电影 | 久久人人爽人人爽人人AV东京热 | 色欲av伊人久久大香线蕉影院| 欧美一区二区三区久久综合| 韩国免费A级毛片久久| 狠狠人妻久久久久久综合蜜桃| 久久久久国产一级毛片高清板| 无夜精品久久久久久| 亚洲国产精品无码久久一线| 国产成人精品久久二区二区 | 国产精品成人精品久久久| 久久强奷乱码老熟女网站| 伊人久久大香线蕉av不卡| 91精品国产综合久久精品| 久久久久99精品成人片| 热re99久久精品国99热| 国产一区二区精品久久岳| 色播久久人人爽人人爽人人片AV| 久久久久亚洲AV无码永不| 久久久受www免费人成| WWW婷婷AV久久久影片| 久久亚洲2019中文字幕| 国产精品毛片久久久久久久| 日日狠狠久久偷偷色综合96蜜桃|