青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

天衣有縫

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

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

聲明:轉載請保留

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

原作者:xiaoming.mo at skelix dot org

MSN & Email: jinglexy at yahoo dot com dot cn

 

 

目標

 

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




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

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

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

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


另一種硬盤訪問模式叫LBALinear Block Addressing:線性塊尋址模式),它是物理硬盤的邏輯尋址方式。和線性內存尋址一樣,LBA用線性地址來定位扇區(qū)(這樣,柱面和磁頭就不會用到了)。這種方式非常簡單,但是驅動使用的是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^32CHS空間很小2^24(不考慮sector不能取0的問題)

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

下面三個demo對應的源程序分別為dpt(分區(qū)表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端口來訪問硬盤。

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


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端口讀取數據,

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

0x1f20x1f3端口設置扇區(qū)號,

0x1f40x1f5端口設置柱面號,

0x1f6端口設置磁頭號。

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


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

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

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

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


07/hd.c

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

Bit 7

控制器忙

Bit 6

驅動器準備就緒

Bit 5

寫出錯

Bit 4

尋道結束

Bit 3

數據請求復位

Bit 2

ECC效驗錯誤

Bit 1

硬盤已收到索引

Bit 0

命令執(zhí)行錯誤

    outb(sects_to_access, HD_PORT_SECT_COUNT); // 準備讀或寫多少個扇區(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
是第一塊硬盤

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循環(huán)等待,而減少內核浪費的時間。

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

 

磁盤分區(qū)表disk partitiontable,以下簡稱dpt

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

每個分區(qū)項的格式如下:

Byte  0

引導標識

Bytes 1-3

CHS 信息

Byte  4

分區(qū)類型

Bytes 5-7

CHS 信息

Bytes 8-11

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

Bytes 12-16

扇區(qū)數量

 

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

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

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

 

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

    sect[0x1be + 0x04] = FST_FS;    //
自定義的數據分區(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);    //
寫入磁盤

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

現在,我們在啟動的過程中把分區(qū)表信息打印出來:
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ū)號和扇區(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字節(jié)偏移,32字節(jié)長度

            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!(最好使用一個未分區(qū)的磁盤映象來測試)


文件系統(tǒng)

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

 

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

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

    char de_name[MAX_NAME_LEN];
    int de_inode;
};

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

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

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

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ū)數量 */

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

 

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

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

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

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

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

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

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

為了管理這個85M分區(qū),我們額外花了 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)存放的是扇區(qū)使用位圖

      imapinode map)存放inode使用位圖。

      inodes存放節(jié)點表。

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

07/fs/fs.c

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

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

init.c在調用verify_DPT();創(chuàng)建分區(qū)表后,緊接著調用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;

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

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


    /*
判斷超級塊是否正確,不是就創(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位圖中,每個位表示1個扇區(qū),也就是說dmap中每個扇區(qū)可以標識512 * 8扇區(qū)。

        // 另外,我們把inode位圖大小固定,即使用1個扇區(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 數組 */
        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) {

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

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

        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位圖查找空閑的扇區(qū),返回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) {

    // 釋放一個扇區(qū):設置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節(jié)點到內存中。

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

// 本文中暫時沒有用戶態(tài)的多任務。

主流程如下:
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ū),判斷"/"目錄有沒有創(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é)點號:即imap位圖中的一位
            kprintf(KPL_PANIC, "\n/ must be inode 0!!!\n");
            halt();
        }

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

 

        de = (struct DIR_ENTRY *)sect;
        strcpy(de->de_name, ".");
        de->de_inode = 0;                     //
節(jié)點號為0
        ++de;
        strcpy(de->de_name, "..");
        de->de_inode = -1;                    //
節(jié)點號為-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標識使用的扇區(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;    //
打印子目錄(只一個扇區(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;
    }
}

現在,我們把上面的函數整理到程序中
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課:文件系統(tǒng))(原創(chuàng))  回復  更多評論   

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

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

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

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

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

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

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

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

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

2008-07-24 14:21 by 龐帆
感覺你的翻譯對比原文還有很多細節(jié)沒能夠表達出來
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            久久蜜桃资源一区二区老牛 | 亚洲成色www8888| 日韩视频免费观看高清在线视频| 亚洲一区二区综合| 欧美国产一区二区| 亚洲精品一二| 欧美高清在线一区二区| 久久久99精品免费观看不卡| 国产精品亚洲第一区在线暖暖韩国| 99xxxx成人网| 亚洲三级网站| 美日韩精品免费观看视频| 亚洲一区二区三区高清不卡| 国产精品久久二区二区| 亚洲私人影院| 一区二区三区福利| 国产精品爽黄69| 久久精品九九| 久久亚洲综合| 99www免费人成精品| 99视频一区二区| 国产精品久久久久久av福利软件 | 国产精品亚洲一区| 欧美怡红院视频| 欧美伊人久久大香线蕉综合69| 国产欧美一区在线| 狼人天天伊人久久| 欧美国产1区2区| 亚洲尤物视频网| 小嫩嫩精品导航| 亚洲国产小视频在线观看| 亚洲国产精品va在线看黑人动漫| 欧美精品一区二区在线播放| 亚洲一区二区av电影| 欧美亚洲综合网| 亚洲日本va午夜在线电影| 日韩一级黄色大片| 国产乱码精品1区2区3区| 久久综合婷婷| 欧美日韩免费| 久久久91精品国产一区二区精品| 麻豆九一精品爱看视频在线观看免费| 9色porny自拍视频一区二区| 亚洲少妇最新在线视频| 激情欧美日韩| 一区二区毛片| 亚洲国产精品电影| 亚洲视频综合| 亚洲青色在线| 欧美一区二区三区在线免费观看| 亚洲国产精品一区二区www在线 | 国产精品久久一级| 久久久久久久久伊人| 欧美激情综合色综合啪啪| 欧美亚洲免费高清在线观看| 老司机免费视频久久| 性做久久久久久久久| 欧美大片在线看| 久久国产乱子精品免费女| 欧美国产日韩在线| 麻豆国产精品777777在线| 国产精品免费视频观看| 亚洲日本中文| 在线观看视频亚洲| 亚洲欧美中文日韩v在线观看| 亚洲精品影院在线观看| 久久激情视频久久| 欧美亚洲一级| 国产精品初高中精品久久| 亚洲高清视频在线观看| 欧美成人激情视频| 欧美一区二区三区在线视频| 欧美大片专区| 欧美成人中文字幕在线| 国内成+人亚洲+欧美+综合在线| 在线综合亚洲| 一区二区三欧美| 欧美久久影院| 最新日韩在线| 99国内精品久久| 欧美mv日韩mv亚洲| 免费欧美日韩国产三级电影| 国产日韩精品一区二区三区在线 | 国产麻豆午夜三级精品| 亚洲精品三级| 一本色道久久99精品综合| 免费亚洲网站| 欧美激情导航| 亚洲精品黄色| 欧美成人一品| 亚洲区欧美区| 一区二区三区免费在线观看| 欧美国产一区二区在线观看| 亚洲国产精品免费| 亚洲看片免费| 欧美日韩一区二区视频在线| 亚洲黄色在线视频| 99re66热这里只有精品3直播 | 亚洲一级在线观看| 国产精品青草综合久久久久99| 在线天堂一区av电影| 亚洲综合不卡| 国产日产欧美精品| 久久成人免费电影| 欧美国产在线电影| av成人毛片| 国产精品美女久久久浪潮软件| 亚洲综合日韩中文字幕v在线| 香蕉av777xxx色综合一区| 国产欧美婷婷中文| 香蕉久久国产| 欧美韩日视频| 亚洲一级在线观看| 国产午夜精品视频| 久热精品在线视频| 亚洲精品久久嫩草网站秘色 | 久久蜜桃精品| 亚洲日本精品国产第一区| 欧美日韩在线看| 欧美在线视频一区二区三区| 欧美成年人网站| 亚洲一级黄色av| 激情久久五月天| 欧美日韩成人综合在线一区二区| 亚洲视频一区二区| 女人天堂亚洲aⅴ在线观看| 最新国产精品拍自在线播放| 国产精品久久久久7777婷婷| 久久久久9999亚洲精品| 亚洲欧洲精品一区二区| 欧美一区在线视频| 国内精品久久久久影院 日本资源| 欧美成年人视频网站欧美| 亚洲综合精品自拍| 最新国产精品拍自在线播放| 久久精品免费电影| 亚洲美女啪啪| 麻豆国产精品777777在线| 99视频一区| 激情六月综合| 国产精品草草| 欧美sm极限捆绑bd| 久久er精品视频| 亚洲天堂成人| 亚洲激情亚洲| 你懂的国产精品| 欧美在线视频免费播放| 亚洲精品日韩一| 韩日精品在线| 欧美国产成人精品| 亚洲午夜激情网页| 美女免费视频一区| 99国产精品久久久久久久成人热| 欧美三级网址| 久久精品综合网| 亚洲精品综合精品自拍| 亚洲国产一区二区三区a毛片| 欧美成人资源| 亚洲欧美日韩国产一区二区三区 | 亚洲欧洲精品一区二区精品久久久 | 亚洲一区bb| 免费永久网站黄欧美| 一区二区日韩伦理片| 国产一区二区三区网站| 欧美a级理论片| 久久三级福利| 久久久久国产精品麻豆ai换脸| 欧美一二三区在线观看| 亚洲男同1069视频| 亚洲一区视频在线观看视频| 99av国产精品欲麻豆| 亚洲精品在线视频| 亚洲精品国产视频| 亚洲精品美女| 亚洲最新在线视频| av成人动漫| 亚洲深夜福利视频| 亚洲一区在线观看视频 | 欧美亚洲一区| 久久精品中文字幕免费mv| 欧美一级片久久久久久久| 午夜精品视频在线观看| 午夜精彩国产免费不卡不顿大片| 亚洲欧美日韩国产另类专区| 亚洲欧美在线一区| 久久精彩免费视频| 久久综合伊人77777蜜臀| 欧美成人激情视频免费观看| 欧美精品亚洲精品| 欧美日韩在线播放一区| 国产精品视频自拍| 国内外成人免费激情在线视频网站 | 免费观看欧美在线视频的网站| 久久亚洲午夜电影| 麻豆9191精品国产| 欧美激情视频在线播放| 亚洲精品乱码久久久久久黑人 | 久热精品在线| 欧美福利电影在线观看| 亚洲国产mv|