• <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>

            天衣有縫

            冠蓋滿京華,斯人獨(dú)憔悴~
            posts - 35, comments - 115, trackbacks - 0, articles - 0
               :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            7課:文件系統(tǒng)    下載源代碼

            聲明:轉(zhuǎn)載請(qǐng)保留

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

            原作者:xiaoming.mo at skelix dot org

            MSN & Email: jinglexy at yahoo dot com dot cn

             

             

            目標(biāo)

             

            這一課中,將創(chuàng)建一個(gè)磁盤(pán)分區(qū),并在她上面建立文件系統(tǒng)。文章看起來(lái)比較長(zhǎng),但是過(guò)程比較簡(jiǎn)單。




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

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

            計(jì)算:16 * 64 * 200 * 512 = 100 M512是每扇區(qū)字節(jié)數(shù))

            很明顯,在CHS模式下,磁盤(pán)容量最大可表示為  柱面數(shù) * 每柱面扇區(qū)數(shù) * 磁頭數(shù) * 512. 注意:柱面和磁頭從0開(kāi)始計(jì)數(shù),而扇區(qū)數(shù)則從1開(kāi)始。不要問(wèn)我為什么。


            另一種硬盤(pán)訪問(wèn)模式叫LBALinear Block Addressing:線性塊尋址模式),它是物理硬盤(pán)的邏輯尋址方式。和線性內(nèi)存尋址一樣,LBA用線性地址來(lái)定位扇區(qū)(這樣,柱面和磁頭就不會(huì)用到了)。這種方式非常簡(jiǎn)單,但是驅(qū)動(dòng)使用的是CHS模式,所以需要進(jìn)行轉(zhuǎn)換(LBA也是從0開(kāi)始計(jì)數(shù)的)。

            LBA =C-Cs*PH*PS+H-Hs*PS+S-Ss
            磁盤(pán)大小LBA = C * H * S - 1

            方向計(jì)算(由LBA獲取CHS:下面的公式好像有問(wèn)題,讀者最好在網(wǎng)上找到正確的資料):
            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之間,其實(shí)相當(dāng)于seg:offset與線形地址的關(guān)系。不同點(diǎn)在于:

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

            如果你使用vmware建立一個(gè)虛擬磁盤(pán),可以在啟動(dòng)時(shí)進(jìn)入bios看到該虛擬磁盤(pán)的 CHS LBA 信息。

            下面三個(gè)demo對(duì)應(yīng)的源程序分別為dpt(分區(qū)表disk partition table),fsroot目錄。


            我們可以定義一個(gè)數(shù)據(jù)結(jié)構(gòu):


            07/dpt/hd.c

            struct HD_PARAM {
                unsigned int cyl;
                unsigned int head;
                unsigned int sect;
            } HD0 = {208, 16, 63};        //
            定義一個(gè)104M磁盤(pán)

             

            由于skelix使用LBA尋址,所以需要把LBA地址轉(zhuǎn)換成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;


            現(xiàn)在已經(jīng)得到chs地址了,我們將通過(guò) 0x1F0-0x1F7端口來(lái)訪問(wèn)硬盤(pán)。

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


            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

             

            另人恐慌,不過(guò)可以很清楚的來(lái)個(gè)分組。我們從0x1f0端口讀取數(shù)據(jù),

            如果發(fā)生了錯(cuò)誤就從0x1f1讀取錯(cuò)誤碼,

            0x1f20x1f3端口設(shè)置扇區(qū)號(hào),

            0x1f40x1f5端口設(shè)置柱面號(hào),

            0x1f6端口設(shè)置磁頭號(hào)。

            端口0x1f7用于讀硬盤(pán)狀態(tài)或者寫(xiě)操作命令。


            寫(xiě)0x1f7端口常用的命令字如下:
            #define HD_READ              0x20        //
            讀硬盤(pán)
            #define HD_WRITE             0x30        //
            寫(xiě)硬盤(pán)

            通過(guò)上面定義,我們可以粗略的認(rèn)為通過(guò)以下幾個(gè)步驟訪問(wèn)硬盤(pán),

            (為了簡(jiǎn)化步驟,暫時(shí)不做一些錯(cuò)誤檢測(cè))

            下面這個(gè)函數(shù)hd_rw就是用來(lái)操作硬盤(pán)的接口。


            07/hd.c

                while ((inb(HD_PORT_STATUS)&0xc0)!=0x40)
                //
            等待硬盤(pán)狀態(tài),直到可以寫(xiě)或讀為止,狀態(tài)字節(jié)說(shuō)明如下:

            Bit 7

            控制器忙

            Bit 6

            驅(qū)動(dòng)器準(zhǔn)備就緒

            Bit 5

            寫(xiě)出錯(cuò)

            Bit 4

            尋道結(jié)束

            Bit 3

            數(shù)據(jù)請(qǐng)求復(fù)位

            Bit 2

            ECC效驗(yàn)錯(cuò)誤

            Bit 1

            硬盤(pán)已收到索引

            Bit 0

            命令執(zhí)行錯(cuò)誤

                outb(sects_to_access, HD_PORT_SECT_COUNT); // 準(zhǔn)備讀或?qū)懚嗌賯€(gè)扇區(qū)

             

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

            Bits 7-5

            必須是 101b

            Bit 4

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

            Bits 3-0

            磁頭號(hào)

             

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

            HD_READ=0x20

            如果不成功會(huì)反復(fù)讀

            HD_WRITE=0x30

            如果不成功會(huì)反復(fù)寫(xiě)

                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);

                // 說(shuō)明:insloutsl是從io端口讀寫(xiě)一串?dāng)?shù)據(jù)的宏匯編指令,

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


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

            事實(shí)上,這只是最簡(jiǎn)單的處理流程,連錯(cuò)誤檢測(cè)都沒(méi)有。雖然是pio方式,

            仍然可以使用中斷,以避免上面的while循環(huán)等待,而減少內(nèi)核浪費(fèi)的時(shí)間。

            不過(guò)skelix不準(zhǔn)備做這么復(fù)雜。

             

            磁盤(pán)分區(qū)表disk partitiontable,以下簡(jiǎn)稱dpt

            現(xiàn)在我們有能力訪問(wèn)硬盤(pán)的扇區(qū)了,需要把這些扇區(qū)管理起來(lái)。硬盤(pán)的第一個(gè)扇區(qū)必須包含硬盤(pán)分區(qū)表。這個(gè)分區(qū)表從第一個(gè)扇區(qū)的0x1be偏移開(kāi)始,長(zhǎng)度是64字節(jié)。最多可以包含4個(gè)分區(qū)(本文不考慮邏輯分區(qū),都使用主分區(qū))。這4個(gè)分區(qū)的相對(duì)偏移分別為0x1be, 0x1ce, 0x1de, 0x1fe,第一個(gè)扇區(qū)的最后兩個(gè)字節(jié)必須是0xaa55

            每個(gè)分區(qū)項(xiàng)的格式如下:

            Byte  0

            引導(dǎo)標(biāo)識(shí)

            Bytes 1-3

            CHS 信息

            Byte  4

            分區(qū)類型

            Bytes 5-7

            CHS 信息

            Bytes 8-11

            該分區(qū)的起始扇區(qū)號(hào)

            Bytes 12-16

            扇區(qū)數(shù)量

             

            0個(gè)字節(jié)是引導(dǎo)標(biāo)識(shí),如果值為0x80標(biāo)識(shí)可引導(dǎo)。對(duì)于一塊硬盤(pán)來(lái)說(shuō),只有一個(gè)分區(qū)是可以引導(dǎo)的。第4個(gè)字節(jié)定義分區(qū)類型,例如FAT32, NTFS, ext2等。有一篇文章http://www.osdever.net/documents/partitiontypes.php?the_id=35,里面定義了常見(jiàn)的分區(qū)類型。

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

            現(xiàn)在我們來(lái)建立分區(qū)表:
            07/dpt/hd.c

             

            static void
            setup_DPT(void) {
                unsigned char sect[512] = {0};
                sect[0x1be] = 0x80;             //
            第一個(gè)分區(qū)可引導(dǎo)

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

                sect[0x1ce + 0x04] = FST_SW;    //
            自定義的交換分區(qū)類型,后續(xù)課程使用
                *(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);    //
            寫(xiě)入磁盤(pán)

                // 寫(xiě)到扇區(qū)0,扇區(qū)數(shù)為1sect是寫(xiě)入緩沖
            }

            現(xiàn)在,我們?cè)趩?dòng)的過(guò)程中把分區(qū)表信息打印出來(lái):
            07/dpt/hd.c

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

                // 變量q存放讀出的分區(qū)表(起始扇區(qū)號(hào)和扇區(qū)數(shù)量)數(shù)組

                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);

                        // 保存到內(nèi)存中,32字節(jié)偏移,32字節(jié)長(zhǎng)度

                        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();
                }
            }

            在我們編譯觀察結(jié)果之前,還需要修改任務(wù)函數(shù)task1_run  task2_run,因?yàn)樗鼈儠?huì)滾動(dòng)屏幕把我們想要的結(jié)果覆蓋掉。
            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() 之前就調(diào)用 verify_DPT()函數(shù):
            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();             //
            任務(wù)調(diào)度可以進(jìn)行了

             

            編譯運(yùn)行一把,OK!(最好使用一個(gè)未分區(qū)的磁盤(pán)映象來(lái)測(cè)試)


            文件系統(tǒng)

            分區(qū)已經(jīng)建立,下一步就是組織各個(gè)分區(qū)上的文件系統(tǒng)。雖然我們可以做到訪問(wèn)扇區(qū)了,但是對(duì)于訪問(wèn)文件卻是不方便的。需要做一些結(jié)構(gòu)化的工作,為此定義了一個(gè)表示文件的數(shù)據(jù)結(jié)構(gòu):
            07/fs/include/fs.h

             

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

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


            對(duì)于目錄(也是一種文件)來(lái)說(shuō),它以類似數(shù)組的形式組織: {{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 {            /
            存放在硬盤(pán)里面,在data區(qū) */

                char de_name[MAX_NAME_LEN];
                int de_inode;
            };

            操作系統(tǒng)中的所有文件都有一個(gè)獨(dú)一無(wú)二的節(jié)點(diǎn)編號(hào),如果有了這個(gè)節(jié)點(diǎn)號(hào),就可以找到對(duì)應(yīng)的文件。最開(kāi)始的兩個(gè)文件永遠(yuǎn)是"." "..",表示當(dāng)前目錄和上級(jí)目錄,如果我們切換到下級(jí)目錄,可以通過(guò)".."來(lái)回到上一級(jí)。"/"表示最上級(jí)目錄,它沒(méi)有父節(jié)點(diǎn)。
            舉例來(lái)說(shuō),我們需要定位到 /usr/doc/fvwm/TODO 文件,首先我們找到"/"文件,然后搜索這個(gè)文件項(xiàng)下面的doc文件,因?yàn)?span lang="EN-US">"/"
            是個(gè)目錄,所以先得到"/"目錄的節(jié)點(diǎn)編號(hào),然后搜索指向的節(jié)點(diǎn)表。然后再搜索到fvwm目錄,并且在這個(gè)目錄的節(jié)點(diǎn)表中搜索到"TODO"這個(gè)文件,并通過(guò)"TODO"的節(jié)點(diǎn)編號(hào)來(lái)定位節(jié)點(diǎn)這個(gè)文件的節(jié)點(diǎn)數(shù)據(jù)結(jié)構(gòu)。最后就可以訪問(wèn)i_block數(shù)組了,也就是可以訪問(wèn)這個(gè)文件了。怎么自己看的都昏菜了,s**t!

            還有兩個(gè)問(wèn)題,一個(gè)是需要指定從哪里搜索節(jié)點(diǎn)號(hào),我們?cè)诖疟P(pán)中組織所有節(jié)點(diǎn)為數(shù)組,并由節(jié)點(diǎn)號(hào)來(lái)索引節(jié)點(diǎn)數(shù)組。另一個(gè)問(wèn)題是,"/"沒(méi)有父節(jié)點(diǎn),需要知道"/"存放在什么地方,這個(gè)也好辦,就放在節(jié)點(diǎn)數(shù)組的第一項(xiàng)好了。

            文件名聲明成12字節(jié),這樣每個(gè)節(jié)點(diǎn)將占用16字節(jié)(另4字節(jié)是節(jié)點(diǎn)編號(hào)),這樣方便磁盤(pán)IO之后的一些操作。當(dāng)磁盤(pán)使用一段時(shí)間后,有的節(jié)點(diǎn)使用了,有的節(jié)點(diǎn)沒(méi)有使用,那怎么知道呢?一種方法是建立一個(gè)位圖表,每個(gè)位表示inode數(shù)組中的一項(xiàng)。

            07/fs/include/fs.h

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

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

             

            這個(gè)超級(jí)塊的數(shù)據(jù)結(jié)構(gòu)用來(lái)管理各個(gè)分區(qū)。例如,下面是一個(gè)磁盤(pán)分區(qū):
             ________________________________________________________

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

            每個(gè)分區(qū)的第一個(gè)扇區(qū)(藍(lán)色)是boot secotr,我不打算使用它,一個(gè)扇區(qū)大小。

            第二個(gè)扇區(qū)(綠色)是超級(jí)塊(super block,以下簡(jiǎn)稱sb),一個(gè)扇區(qū)大小。

            黑色是dmap336個(gè)扇區(qū)大小。

            紅色是imap,一個(gè)扇區(qū)大小。

            灰色是inodes,將占有342個(gè)block,即342 * 8 個(gè)扇區(qū)大小。

            為了管理這個(gè)85M分區(qū),我們額外花了 1.5M 的空間。

             

            verify_fs()函數(shù)中定義了超級(jí)塊(局部變量)sb,為了方便訪問(wèn)定義了一些宏,獲取相對(duì)整個(gè)硬盤(pán)的絕對(duì)地址(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)

             

            說(shuō)明:dmapdata map)存放的是扇區(qū)使用位圖

                  imapinode map)存放inode使用位圖。

                  inodes存放節(jié)點(diǎn)表。

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

            07/fs/fs.c

            void
            setb(void *s, unsigned int i) {
                unsigned char *v = s;
                v += i>>3;            // i
            的單位由位轉(zhuǎn)換成字節(jié)
                *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;
            }

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

            init.c在調(diào)用verify_DPT();創(chuàng)建分區(qū)表后,緊接著調(diào)用verify_fs();創(chuàng)建文件系統(tǒng):
            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;

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

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


                /*
            判斷超級(jí)塊是否正確,不是就創(chuàng)建文件系統(tǒng) */
                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位圖中,每個(gè)位表示1個(gè)扇區(qū),也就是說(shuō)dmap中每個(gè)扇區(qū)可以標(biāo)識(shí)512 * 8扇區(qū)。

                    // 另外,我們把inode位圖大小固定,即使用1個(gè)扇區(qū)。
                    /* 
            初始化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 數(shù)組 */
                    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);

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

                    // 注意,和數(shù)據(jù)分區(qū)不同(每個(gè)塊占有1個(gè)扇區(qū)),

                    // 交互分區(qū)每個(gè)塊占有8個(gè)扇區(qū),即4096字節(jié),和內(nèi)存頁(yè)對(duì)齊

                    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]");   
                }
                /* 
            初始化數(shù)據(jù)位圖 */
                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
            編譯,運(yùn)行。

             

            root 根目錄

            最后一件事情建立根目錄。"/"是所有文件的根目錄,所以我們一開(kāi)始就必須設(shè)置好它。"/"文件永遠(yuǎn)使用inode 0,這樣skelix內(nèi)核才知道怎樣找到它。然后再讀取"/"文件的內(nèi)容,也就是DIR_ENTRY結(jié)構(gòu)數(shù)組。為了更方便的操作,我們先完成一些基礎(chǔ)函數(shù),用來(lái)操作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) {

                // 根據(jù)dmap位圖查找空閑的扇區(qū),返回LBA地址(從1開(kāi)始)
                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) {

                // 釋放一個(gè)扇區(qū):設(shè)置dmap位圖中對(duì)應(yīng)的位即可
                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表中中找一個(gè)空閑的項(xiàng)
                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項(xiàng)
                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個(gè)函數(shù)就是針對(duì)dmapimap的操作(申請(qǐng),釋放)

            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);
            }

            // 上面兩個(gè)函數(shù)分別完成讀/寫(xiě)磁盤(pán)指定下標(biāo)號(hào)對(duì)應(yīng)的inode節(jié)點(diǎn)到內(nèi)存中。

            // 需要注意的是,這些函數(shù)對(duì)競(jìng)態(tài)條件做處理,因?yàn)?span lang="EN-US">skelix僅內(nèi)核讀寫(xiě)硬盤(pán)。

            // 本文中暫時(shí)沒(méi)有用戶態(tài)的多任務(wù)。

            主流程如下:
            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扇區(qū),判斷"/"目錄有沒(méi)有創(chuàng)建
                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) {   //
            分配節(jié)點(diǎn)號(hào):即imap位圖中的一位
                        kprintf(KPL_PANIC, "\n/ must be inode 0!!!\n");
                        halt();
                    }

                    iroot.i_block[0] = alloc_blk(&sb);    //
            節(jié)點(diǎn)分配一個(gè)塊(一個(gè)扇區(qū))
                    iput(&sb, &iroot, 0);                 //
            寫(xiě)入節(jié)點(diǎn)

             

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

                    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();
                }
            }

             

            // 再來(lái)一個(gè)函數(shù)打印文件的相關(guān)信息
            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標(biāo)識(shí)使用的扇區(qū)
                    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;    //
            打印子目錄(只一個(gè)扇區(qū))
                    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;
                }
            }

            現(xiàn)在,我們把上面的函數(shù)整理到程序中
            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: 自己動(dòng)手寫(xiě)內(nèi)核(第7課:文件系統(tǒng))(原創(chuàng))  回復(fù)  更多評(píng)論   

            2007-10-26 16:57 by fggh gqx wq
            我干死了。。。。
            以后轉(zhuǎn)貼的時(shí)候。別那么大臉寫(xiě)原創(chuàng)。。。。不怕撞死。。。
            還有。。。轉(zhuǎn)帖也要有責(zé)任心。。拜托大哥排一下版。。。

            # re: 自己動(dòng)手寫(xiě)內(nèi)核(第7課:文件系統(tǒng))(原創(chuàng))  回復(fù)  更多評(píng)論   

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

            # re: 自己動(dòng)手寫(xiě)內(nèi)核(第7課:文件系統(tǒng))(原創(chuàng))  回復(fù)  更多評(píng)論   

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

            # re: 自己動(dòng)手寫(xiě)內(nèi)核(第7課:文件系統(tǒng))(原創(chuàng))  回復(fù)  更多評(píng)論   

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

            別聽(tīng)上邊那個(gè)SZ瞎說(shuō)...走自己的路...
            支持你的原創(chuàng)....

            # re: 自己動(dòng)手寫(xiě)內(nèi)核(第7課:文件系統(tǒng))(原創(chuàng))  回復(fù)  更多評(píng)論   

            2008-07-24 14:21 by 龐帆
            感覺(jué)你的翻譯對(duì)比原文還有很多細(xì)節(jié)沒(méi)能夠表達(dá)出來(lái)
            99久久这里只有精品| 美女久久久久久| 一97日本道伊人久久综合影院| 99久久精品毛片免费播放| 免费一级欧美大片久久网| 久久91精品国产91久久小草 | 亚洲国产精品无码久久九九| 久久香蕉国产线看观看猫咪?v| 狠狠88综合久久久久综合网| 奇米影视7777久久精品人人爽| 99久久精品免费国产大片| 久久99国产亚洲高清观看首页| 亚洲日韩中文无码久久| 久久久久久久久66精品片| 久久久久亚洲av无码专区| 中文字幕久久精品| 热RE99久久精品国产66热| 久久亚洲色一区二区三区| 国产 亚洲 欧美 另类 久久| 青青青青久久精品国产h| 久久香蕉国产线看观看99| 久久精品人人做人人爽电影蜜月 | 色综合色天天久久婷婷基地 | 2021久久精品国产99国产精品| 国产福利电影一区二区三区久久老子无码午夜伦不 | 青青青国产精品国产精品久久久久 | 99久久er这里只有精品18| 欧美一级久久久久久久大片| 久久久久久亚洲精品无码| 青春久久| 久久久亚洲AV波多野结衣| 日批日出水久久亚洲精品tv| 色综合久久88色综合天天 | 热久久最新网站获取| 97精品依人久久久大香线蕉97 | 国产精品久久自在自线观看| 伊人久久一区二区三区无码| 欧美精品乱码99久久蜜桃| 日产精品99久久久久久| 亚洲天堂久久精品| 日日狠狠久久偷偷色综合免费|