中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

編寫一個UNIX文件系統

發布時間:2020-06-10 02:31:49 來源:網絡 閱讀:521 作者:dog250 欄目:系統運維

近日有人求助,要寫一個UNIX文件系統作為暑假作業。這種事情基本是學操作系統的必須要做的或者是做過的,畢竟文件系統是操作系統課程的一個重要組成部分。要實現這個UNIX文件系統,很多人就扎進了UNIX V6的的系統源碼,以及《萊昂氏UNIX源代碼分析》和《返璞歸真:UNIX技術內幕》這兩本書,很多人出來了,很多人在里面迷失了...最終忘了自己只是要實現一個UNIX文件系統而已。
       為何會迷失,因為代碼不是自己寫的,而且年代久遠,編程理念不同了,作者為何那樣寫不一定就能理解,實際上對于任何別人寫的代碼,總是會有一些不易理解的地方,當然,如果作者水平超級高,那么代碼也就相對容易理解。因此,寫代碼永遠比讀代碼要容易!既然是要寫一個文件系統,為何要用現成的UNIX V6代碼呢?如果理解了UNIX文件的布局和結構,自己從零開始不參考任何現有的代碼做一個也不是什么難事,最根本的是UNIX文件系統本身,至于說代碼,僅僅是一個實現與用戶操作的一個接口而已。如果代碼是自己一點一點寫的,那么你肯定能徹底明白每一行的每一個語句的精確含義,至于為何這么寫,你當然及其明了!
       本文留下我倉促間幾個小時寫的一個類UNIX文件系統的代碼,不是讓別人看的,是為了自己留檔,因為本文已經說了,看別人的代嗎只能學習經驗,不能理解本質,更何況,你看的還得是超級一流的代碼,而我寫的,則是超級垃圾的代碼。我只想說,理解問題的本質要比代碼重要得多,代碼,碼,并不是很多人想象中的那般重要!本文的實現基于Linux系統,即在Linux系統上編寫一個用戶態程序,實現UNIX文件的IO接口以及操作。
       我一向堅持的原則,那就是任何東西的根本性的,本質上的原理以及背后的思想都是及其簡單的,所謂的復雜性都是優化與策略化的擴展帶來的,正如TCP一樣,UNIX的文件系統也不例外!我們必須知道,什么是最根本的,什么是次要的。對于UNIX文件系統,最根本的就是其布局以及其系統調用接口,一個處在最低層,一個在最上層開放給用戶,如下所示:
系統調用接口:open,write,read,close...
文件系統布局:引導快,超級塊,inode區表,數據塊區
所有的在二者中間的部分都是次要的,也就是說那些東西不要也行,比如高速緩沖,高速緩存,VFS層,塊層...因此在我的實現代碼中,并沒有這些東西,我所做到的,僅僅是UNIX文件系統所要求必須做到的最小集,那就是:
面對一個按照UNIX文件系統標準布局的“塊設備”,可以使用open,read,write等接口進行IO操作。
在實現中,我用一個標準的Linux大文件來模擬磁盤塊,這樣塊的操作基本都映射到了Linux標準的write,read等系統調用了。
首先定義必要的結構體:

//超級塊結構
struct filesys {
        unsigned int s_size;        //總大小                                           
        unsigned int s_itsize;        //inode表大小                                         
        unsigned int s_freeinodesize;    //空閑i節點的數量                           
        unsigned int s_nextfreeinode;    //下一個空閑i節點
        unsigned int s_freeinode[NUM];     //空閑i節點數組
        unsigned int s_freeblocksize;    //空閑塊的數量          
        unsigned int s_nextfreeblock;    //下一個空閑塊
        unsigned int s_freeblock[NUM];    //空閑塊數組
        unsigned int s_pad[];        //填充到512字節   
};
//磁盤inode結構
struct finode {
        int fi_mode;            //類型:文件/目錄
        int fi_uid_unused;        //uid,由于和進程無關聯,僅僅是模擬一個FS,未使用,下同
        int fi_gid_unused;
        int fi_nlink;            //鏈接數,當鏈接數為0,意味著被刪除
        long int fi_size;        //文件大小
        long int fi_addr[BNUM];        //文件塊一級指針,并未實現多級指針
        time_t  fi_atime_unused;    //未實現
        time_t  fi_mtime_unused;
};
//內存inode結構
struct inode {
        struct finode   i_finode;
        struct inode    *i_parent;    //所屬的目錄i節點
        int     i_ino;            //i節點號
        int     i_users;        //引用計數
};
//目錄項結構(非Linux內核的目錄項)
struct direct
{
        char d_name[MAXLEN];        //文件或者目錄的名字
        unsigned short d_ino;        //文件或者目錄的i節點號
};
//目錄結構
struct dir
{
        struct direct direct[DIRNUM];    //包含的目錄項數組
        unsigned short size;        //包含的目錄項大小     
};
//抽象的文件結構
struct file {
        struct inode *f_inode;        //文件的i節點
        int f_curpos;            //文件的當前讀寫指針
};

之所以叫做類UNIX文件系統,是因為我并沒有去精確確認當時的UNIX文件系統的超級塊以及inode表的結構,只是大致的模仿其布局,比如超級塊中字段,以及字段的順序可能和標準的UNIX文件系統并不完全一致。但是不管怎么說,當時的UNIX文件系統基本就是這個一個樣子。另外,可以看到file結構體內容及其少,因為本質上,我只是想表示“一個inode節點相對于一個讀寫者來說,就是一個file”,僅此而已。接下來就是具體的實現了,我的方式是自下而上的,這樣做的好處在于便于今后的擴展。那么首先要完成的就是i節點的分配和釋放了,我的實現中,是將文件i節點映射到了內存i節點,這樣或許違背了我的初衷,我不是說過不要那么多“額外”的東西來擾亂視聽的嗎?是的,然而比起那些所謂的額外的優化,我更不喜歡頻繁的調用read和write。反正,只要自己能控制住局面即可。
       在實現中,還有一個大事就是內存的分配與釋放,這些也不是本質的,記住,要實現的僅僅是一個UNIX文件系統,其它的能繞開則繞開!顯然malloc,free等也是我們要繞開的,于是我基本都使用預分配空間的東西-全局數組。以下是全局變量:

//內存i節點數組,NUM為該文件系統容納的文件數
struct inode g_usedinode[NUM];
//ROOT的內存i節點
struct inode *g_root;
//已經打開文件的數組
struct file* g_opened[OPENNUM];
//超級塊
struct filesys *g_super;
//模擬二級文件系統的Linux大文件的文件描述符
int g_fake_disk = -1;

在給出實現代碼之前,要說明的是,在刪除文件的時候,我并沒有實現文件塊區以及i節點的清除操作,眾所周知,那樣很耗時,和很多實現一樣,我只是記錄了一些信息,表示這個文件塊或者inode字段是可以隨時覆蓋的。

//同步i節點,將其寫入“磁盤”
void syncinode(struct inode *inode)
{
        int ino = -1, ipos = -1;
        ino = inode->i_ino;
    //ipos為inode節點表在文件系統塊中的偏移
        ipos = IBPOS + ino*sizeof(struct finode);
    //從模擬塊的指定偏移位置讀取inode信息
        lseek(g_fake_disk, ipos, SEEK_SET);
        write(g_fake_disk, (void *)&inode->i_finode, sizeof(struct finode));
}
//同步超級塊信息
int syncsuper(struct filesys *super)
{
        int pos = -1, size = -1;
        struct dir dir = {0};
        pos = BOOTBSIZE;
        size = SUPERBSIZE;
        lseek(g_fake_disk, pos, SEEK_SET);
        write(g_fake_disk, (void *)super, size);
        syncinode(g_root);
        breadwrite(g_root->i_finode.fi_addr[0], (char *)&dir, sizeof(struct dir), 0, 1);
        breadwrite(g_root->i_finode.fi_addr[0], (char *)&dir, sizeof(struct dir), 0, 0);
}
//關鍵的將路徑名轉換為i節點的函數,暫不支持相對路徑
struct inode *namei(char *filepath, char flag, int *match, char *ret_name)
{
        int in = 0;
        int repeat = 0;
        char *name = NULL;
        char *path = calloc(1, MAXLEN*10);
        char *back = path;
        struct inode *root = iget(0);
        struct inode *parent = root;
        struct dir dir = {0};
        strncpy(path, filepath, MAXLEN*10);
        if (path[0] != '/')
                return NULL;
        breadwrite(root->i_finode.fi_addr[0], &dir, sizeof(struct dir), 0, 1);
        while((name=strtok(path, "/")) != NULL) {
                int i = 0;
                repeat = 0;
                *match = 0;
                path = NULL;
                if (ret_name) {
                        strcpy(ret_name, name);
                }
                for (; i<dir.size; i++) {
                        if (!strncmp(name, dir.direct[i].d_name, strlen(name))) {
                                parent = root;
                                iput(root);
                                root = iget(dir.direct[i].d_ino);
                                root->i_parent = parent;
                                *match = 1;
                                if (root->i_finode.fi_mode == MODE_DIR) {
                                        memset(&dir, 0, sizeof(struct dir));
                                        breadwrite(root->i_finode.fi_addr[0], &dir, sizeof(struct dir), 0, 1);
                                } else {
                                        free(back);
                                        return root;
                                }
                                repeat = 1;
                        }
                }
                if (repeat == 0) {
                        break;
                }
        }
        if (*match != 1) {
                *match = 0;
        }
        if (*match == 0) {
                if (ret_name) {
                        strcpy(ret_name, name);
                }
        }
        free(back);
        return root;
}
//通過i節點號獲取內存i節點的函數
struct inode *iget(int ino)
{
        int ibpos = 0;
        int ipos = 0;
        int ret = 0;
    //傾向于直接從內存i節點獲取
        if (g_usedinode[ino].i_users) {
                g_usedinode[ino].i_users ++;
                return &g_usedinode[ino];
        }
        if (g_fake_disk < 0) {
                return NULL;
        }
    //實在不行則從模擬磁盤塊讀入
        ipos = IBPOS + ino*sizeof(struct finode);
        lseek(g_fake_disk, ipos, SEEK_SET);
        ret = read(g_fake_disk, &g_usedinode[ino], sizeof(struct finode));
        if (ret == -1) {
                return NULL;
        }
        if (g_super->s_freeinode[ino] == 0) {
                return NULL;
        }
    //如果是一個已經被刪除的文件或者從未被分配過的i節點,則初始化其link值以及size值
        if (g_usedinode[ino].i_finode.fi_nlink == 0) {
                g_usedinode[ino].i_finode.fi_nlink ++;
                g_usedinode[ino].i_finode.fi_size = 0;
                syncinode(&g_usedinode[ino]);
        }
        g_usedinode[ino].i_users ++;
        g_usedinode[ino].i_ino = ino;
        return &g_usedinode[ino];
}
//釋放一個占有的內存i節點
void iput(struct inode *ip)
{
        if (ip->i_users > 0)
                ip->i_users --;
}
//分配一個未使用的i節點。注意,我并沒有使用超級塊的s_freeinodesize字段,
//因為還會有一個更好更快的分配算法
struct inode* ialloc()
{
        int ino = -1, nowpos = -1;
        ino = g_super->s_nextfreeinode;
        if (ino == -1) {
                return NULL;
        }
        nowpos = ino + 1;
        g_super->s_nextfreeinode = -1;
    //尋找下一個空閑i節點,正如上述,這個算法并不好
        for (; nowpos < NUM; nowpos++) {
                if (g_super->s_freeinode[nowpos] == 0) {
                        g_super->s_nextfreeinode = nowpos;
                        break;
                }
        }
        g_super->s_freeinode[ino] = 1;
        return iget(ino);
}
//試圖刪除一個文件i節點
int itrunc(struct inode *ip)
{
        iput(ip);
        if (ip->i_users == 0 && g_super) {
                syncinode(ip);
                g_super->s_freeinode[ip->i_ino] = 0;
                g_super->s_nextfreeinode = ip->i_ino;
                return 0;
        }
        return ERR_BUSY;
}
//分配一個未使用的磁盤塊
int balloc()
{
        int bno = -1, nowpos = -1;
        bno = g_super->s_nextfreeblock;
        if (bno == -1) {
                return bno;
        }
        nowpos = bno + 1;
        g_super->s_nextfreeblock = -1;
        for (; nowpos < NUM; nowpos++) {
                if (g_super->s_freeblock[nowpos] == 0) {
                        g_super->s_nextfreeblock = nowpos;
                        break;
                }
        }
        g_super->s_freeblock[bno] = 1;
        return bno;
}
//讀寫操作
int breadwrite(int bno, char *buf, int size, int offset, int type)
{
        int pos = BOOTBSIZE+SUPERBSIZE+g_super->s_itsize + bno*BSIZE;
        int rs = -1;
        if (offset + size > BSIZE) {
                return ERR_EXCEED;
        }
        lseek(g_fake_disk, pos + offset, SEEK_SET);
        rs = type ? read(g_fake_disk, buf, size):write(g_fake_disk, buf, size);
        return rs;
}
//IO讀接口
int mfread(int fd, char *buf, int length)
{
        struct file *fs = g_opened[fd];
        struct inode *inode = fs->f_inode;
        int baddr = fs->f_curpos;
        int bondary = baddr%BSIZE;
        int max_block = (baddr+length)/BSIZE;
        int size = 0;
        int i = inode->i_finode.fi_addr[baddr/BSIZE+1];
        for (; i < max_block+1; i ++,bondary = size%BSIZE) {
                size += breadwrite(inode->i_finode.fi_addr[i], buf+size, (length-size)%BSIZE, bondary, 1);
        }
        return size;
}
//IO寫接口
int mfwrite(int fd, char *buf, int length)
{
        struct file *fs = g_opened[fd];
        struct inode *inode = fs->f_inode;
        int baddr = fs->f_curpos;
        int bondary = baddr%BSIZE;
        int max_block = (baddr+length)/BSIZE;
        int curr_blocks = inode->i_finode.fi_size/BSIZE;
        int size = 0;
        int sync = 0;
        int i = inode->i_finode.fi_addr[baddr/BSIZE+1];
    //如果第一次寫,先分配一個塊
        if (inode->i_finode.fi_size == 0) {
        int nbno = balloc();
                if (nbno == -1) {
                        return -1;
                }
                inode->i_finode.fi_addr[0] = nbno;
                sync = 1;
        }
    //如果必須擴展,則再分配塊,可以和上面的合并優化
        if (max_block > curr_blocks) {
                int j = curr_blocks + 1;
                for (; j < max_block; j++) {
            int nbno = balloc();
                    if (nbno == -1) {
                            return -1;
                    }
                        inode->i_finode.fi_addr[j] = nbno;
                }
                sync = 1;
        }
        for (; i < max_block+1; i ++,bondary = size%BSIZE) {
                size += breadwrite(inode->i_finode.fi_addr[i], buf+size, (length-size)%BSIZE, bondary, 0);
        }
        if (size) {
                inode->i_finode.fi_size += size;
                sync = 1;
        }
        if (sync) {
                syncinode(inode);
        }
        return size;
}
//IO的seek接口
int mflseek(int fd, int pos)
{
        struct file *fs = g_opened[fd];
        fs->f_curpos = pos;
        return pos;
}
//IO打開接口
int mfopen(char *path, int mode)
{
        struct inode *inode = NULL;
        struct file *file = NULL;
        int match = 0;
        inode = namei(path, 0, &match, NULL);
        if (match == 0) {
                return ERR_NOEXIST;
        }
        file = (struct file*)calloc(1, sizeof(struct file));
        file->f_inode = inode;
        file->f_curpos = 0;
        g_opened[g_fd] = file;
        g_fd++;
        return g_fd-1;
}
//IO關閉接口
void mfclose(int fd)
{
        struct inode *inode = NULL;
        struct file *file = NULL;
        file = g_opened[fd];
        inode = file->f_inode;
        iput(inode);
        free(file);
}
//IO創建接口
int mfcreat(char *path, int mode)
{
        int match = 0;
        struct dir dir;
        struct inode *new = NULL;
        char name[MAXLEN] = {0};;
        struct inode *inode = namei(path, 0, &match, name);
        if (match == 1) {
                return ERR_EXIST;
        }
        breadwrite(inode->i_finode.fi_addr[0], (char *)&dir, sizeof(struct dir), 0, 1);
        strcpy(dir.direct[dir.size].d_name, name);
        new = ialloc();
        if (new == NULL) {
                return -1;
        }
        dir.direct[dir.size].d_ino = new->i_ino;
        new->i_finode.fi_mode = mode;
        if (mode == MODE_DIR) {
        //不允許延遲分配目錄項
                int nbno = balloc();
                if (nbno == -1) {
                        return -1;
                }
                new->i_finode.fi_addr[0] = nbno;
        }
        new->i_parent = inode;
        syncinode(new);
        dir.size ++;
        breadwrite(inode->i_finode.fi_addr[0], (char *)&dir, sizeof(struct dir), 0, 0);
        syncinode(inode);
        iput(inode);
        syncinode(new);
        iput(new);
        return ERR_OK;
}
//IO刪除接口
int mfdelete(char *path)
{
        int match = 0;
        struct dir dir;
        struct inode *del = NULL;
        struct inode *parent = NULL;
        char name[MAXLEN];
        int i = 0;
        struct inode *inode = namei(path, 0, &match, name);
        if (match == 0 || inode->i_ino == 0) {
                return ERR_NOEXIST;
        }
        match = -1;
        parent = inode->i_parent;
        breadwrite(parent->i_finode.fi_addr[0], (char *)&dir, sizeof(struct dir), 0, 1);
        for (; i < dir.size; i++) {
                if (!strncmp(name, dir.direct[i].d_name, strlen(name))) {
                        del = iget(dir.direct[i].d_ino);
                        iput(del);
                        if (itrunc(del) == 0) {
                                memset(dir.direct[i].d_name, 0, strlen(dir.direct[i].d_name));
                                match = i;
                                break;
                        } else {
                                return ERR_BUSY;
                        }
                }
        }
        for (i = match; i < dir.size - 1 && match != -1; i++) {
                strcpy(dir.direct[i].d_name, dir.direct[i+1].d_name);
        }
        dir.size--;
        breadwrite(parent->i_finode.fi_addr[0], (char *)&dir, sizeof(struct dir), 0, 0);
        return ERR_OK;
}
//序列初始化接口,從模擬塊設備初始化內存結構
int initialize(char *fake_disk_path)
{
        g_fake_disk = open(fake_disk_path, O_RDWR);
        if (g_fake_disk == -1) {
                return ERR_NOEXIST;
        }
        g_super = (struct filesys*)calloc(1, sizeof(struct filesys));
        lseek(g_fake_disk, BOOTBSIZE, SEEK_SET);
        read(g_fake_disk, g_super, sizeof(struct filesys));
        g_super->s_size = 1024*1024;
        g_super->s_itsize = INODEBSIZE;
        g_super->s_freeinodesize = NUM;
        g_super->s_freeblocksize = (g_super->s_size - (BOOTBSIZE+SUPERBSIZE+INODEBSIZE))/BSIZE;
        g_root =  iget(0);
    //第一次的話要分配ROOT
        if (g_root == NULL) {
                g_root = ialloc();
                g_root->i_finode.fi_addr[0] = balloc();
        }
        return ERR_OK;
}

下面是一個測試程序:

int main()
{
        int fd = -1,ws = -1;
        char buf[16] = {0};
        initialize("bigdisk");
        mfcreat("/aa", MODE_FILE);
        fd = mfopen("/aa", 0);
        ws = mfwrite(fd, "abcde", 5);
        mfread(fd, buf, 5);
        mfcreat("/bb", MODE_DIR);
        mfcreat("/bb/cc", MODE_FILE);
        fd = mfopen("/bb/cc", 0);
        ws = mfwrite(fd, "ABCDEFG", 6);
        mfread(fd, buf, 5);
        mflseek(0, 4);
        ws = mfwrite(0, "ABCDEFG", 6);
        mflseek(0, 0);
        mfread(0, buf, 10);
        mfclose(0);
        mfdelete("/aa");
        fd = mfopen("/aa", 0);
        mfcreat("/aa", MODE_FILE);
        fd = mfopen("/aa", 0);
        syncsuper(g_super);
}

這個文件系統實現得超級簡單,除去了很多額外的非本質的東西,并且也繞開了煩人的內存管理問題!于是,我的這個實現也就顯示了UNIX文件系統的本質。那么再看一下,還有什么東西雖然是額外的,但是卻是必不可少或者起碼說是很有意思的?答案很顯然,那就是空閑塊或者空閑inode的組織以及分配算法,然而這個算法可以單獨抽象出來。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

临海市| 噶尔县| 区。| 庆阳市| 安龙县| 稻城县| 兰考县| 松原市| 银川市| 和龙市| 定州市| 竹山县| 新宾| 宝山区| 南溪县| 临泉县| 弥勒县| 永泰县| 腾冲县| 岚皋县| 大城县| 林口县| 安义县| 昌乐县| 琼海市| 呈贡县| 宜章县| 从化市| 桑植县| 泰来县| 哈巴河县| 马关县| 谷城县| 太康县| 灌南县| 梅河口市| 页游| 黑水县| 左云县| 邻水| 淳化县|