日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > windows >内容正文

windows

我是如何学习写一个操作系统(九):文件系统

發(fā)布時(shí)間:2023/12/20 windows 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 我是如何学习写一个操作系统(九):文件系统 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

這個(gè)應(yīng)該是這個(gè)系列的尾聲了,一個(gè)完整的操作系統(tǒng)可能最主要的也就是分成這幾大模塊:進(jìn)程管理、內(nèi)存管理和文件系統(tǒng)。計(jì)算機(jī)以進(jìn)程為基本單位進(jìn)行資源的調(diào)度和分配;而與用戶的交互,基本單位則是文件

生磁盤(pán)

文件正是對(duì)生磁盤(pán)的抽象

磁盤(pán)的組成

一個(gè)磁盤(pán)由多個(gè)盤(pán)面串聯(lián)而成,而一個(gè)盤(pán)面又被分為磁道,磁道又由扇區(qū)組成。

磁盤(pán)的訪問(wèn)單元就是扇區(qū),一個(gè)扇區(qū)為512字節(jié)

磁盤(pán)的使用

  • CPU向磁盤(pán)的控制器發(fā)出一個(gè)指令
  • 控制器開(kāi)始進(jìn)行尋道、旋轉(zhuǎn)和傳輸
  • 最后完成后向CPU發(fā)送一個(gè)中斷

也就是向控制器發(fā)送柱面磁頭扇區(qū)等信息,然后等待回應(yīng)

盤(pán)塊

盤(pán)塊是對(duì)扇區(qū)的抽象

程序負(fù)責(zé)提高盤(pán)塊block,而磁盤(pán)驅(qū)動(dòng)負(fù)責(zé)從block計(jì)算出cyl,head,sec(CHS),最后傳遞到磁盤(pán)控制器上

磁盤(pán)訪問(wèn)時(shí)間 = 寫(xiě)入控制器時(shí)間 + 尋道時(shí)間 + 旋轉(zhuǎn)時(shí)間 + 傳輸時(shí)間

其中主要的時(shí)間都在尋道時(shí)間和旋轉(zhuǎn)時(shí)間,所以對(duì)扇區(qū)抽象成盤(pán)塊就可以一次訪問(wèn)多個(gè)扇區(qū)來(lái)節(jié)省磁盤(pán)的訪問(wèn)時(shí)間

磁盤(pán)調(diào)度

既然有多進(jìn)程交替執(zhí)行,就有可能多個(gè)進(jìn)程同時(shí)訪問(wèn)相同磁盤(pán)的情況,就需要一個(gè)請(qǐng)求隊(duì)列來(lái)處理所有請(qǐng)求,就會(huì)涉及到調(diào)度算法了

FCFS調(diào)度

FCFS是最公平最直觀的算法,也就是按照隊(duì)列順序來(lái)訪問(wèn)磁盤(pán),但是效率也很低下,磁頭會(huì)在不規(guī)律的磁道長(zhǎng)途奔襲

SSTF調(diào)度

SSTF算法就類似短作業(yè)優(yōu)先算法,先尋找更近距離的磁道,但是SSTF算法可能會(huì)產(chǎn)生饑餓問(wèn)題,過(guò)長(zhǎng)距離的磁道可能一直得不到處理

SCAN調(diào)度

SCAN算法就是SSTF算法的改良版,也就是進(jìn)行SSTF,但是在中途不會(huì)折返去尋找更短距離的磁道,這樣就避免了饑餓問(wèn)題

C-SCAN調(diào)度(電梯算法)

把掃描限定在一個(gè)方向,當(dāng)訪問(wèn)到某個(gè)方向的最后一個(gè)磁道時(shí),磁道返回磁盤(pán)相反方向磁道的末端,并再次開(kāi)始掃描。

文件和文件系統(tǒng)

文件是對(duì)磁盤(pán)的第三層抽象,扇區(qū)和盤(pán)塊分別是前兩層抽象。之所以有文件這層抽象是為了方便用戶的使用,在用戶的眼里,磁盤(pán)上的信息都可以看作是字符流。所以文件的抽象九可以看作是字符流到盤(pán)塊集合的映射關(guān)系

文件的邏輯結(jié)構(gòu)

從文件到盤(pán)塊的映射來(lái)看,一般有這幾種組織方式

  • 順序文件

    記錄是定長(zhǎng)的且按關(guān)鍵字順序排列??梢皂樞虼鎯?chǔ)或以鏈表形勢(shì)存儲(chǔ),在訪問(wèn)時(shí)需要順序搜索文件。順序文件有以下兩種結(jié)構(gòu):

  • 第一種是串結(jié)構(gòu),各記錄之間的順序與關(guān)鍵字無(wú)關(guān)。通常的辦法是由時(shí)間來(lái)決定,即按存入時(shí)間的先后排列,最先存入的記錄作為第一個(gè)記錄,其次存入的為第二個(gè)記錄,以此類推。

  • 第二種是順序結(jié)構(gòu),指文件中所有記錄按關(guān)鍵字順序排列。
    在對(duì)記錄進(jìn)行批量操作時(shí),即每次要讀或?qū)懸淮笈涗?#xff0c;對(duì)順序文件的效率是所有邏輯文件中最高的;此外,也只有順序文件才能存儲(chǔ)在磁帶上,并能有效的工作。但順序文件對(duì)查找、修改、增加或刪除單個(gè)記錄的操作比較困難。

  • 索引文件

    對(duì)于可變長(zhǎng)記錄的文件只能順序查找,系統(tǒng)開(kāi)銷較大,為此可以建立一張索引表以加快檢索速度,索引表本身是順序文件。在記錄很多或是訪問(wèn)要求高的文件中,需要引入索引以提供有效的訪問(wèn),實(shí)際中,通過(guò)索引可以成百上千倍的提高訪問(wèn)速度。

  • 索引順序表

    索引順序表是順序和索引兩種組織形勢(shì)的結(jié)合。索引順序文件將順序文件中所有記錄分為若干個(gè)組,為順序文件建立一張索引表,在索引表中為每組中的第一個(gè)記錄建立一個(gè)索引項(xiàng),其中含有該記錄的關(guān)鍵字值和指向該記錄的指針。

在實(shí)際的操作系統(tǒng)實(shí)現(xiàn)中,一般是采用多級(jí)索引

目錄和文件系統(tǒng)

文件系統(tǒng)或者說(shuō)目錄是對(duì)磁盤(pán)的第四個(gè)抽象,也就是抽象了整個(gè)磁盤(pán)

操作系統(tǒng)為了實(shí)現(xiàn)文件目錄,引入了稱為文件控制塊的數(shù)據(jù)結(jié)構(gòu)。

文件控制塊。

文件控制塊(FCB)是用來(lái)存放控制文件需要的各種信息的數(shù)據(jù)結(jié)構(gòu),以實(shí)現(xiàn)“按名存取”。FCB的有序集合稱為文件目錄,一個(gè)FCB就是一個(gè)文件目錄項(xiàng)。為了創(chuàng)建一個(gè)新文件,系統(tǒng)將分配一個(gè)FCB并存放在文件目錄中,稱為目錄項(xiàng)。

FCB主要包含以下信息:

  • 基本信息,如文件名、文件的物理位置、文件的邏輯結(jié)構(gòu)、文件的物理結(jié)構(gòu)等。
  • 存取控制信息,如文件的存取權(quán)限等。
  • 使用信息,如文件建立時(shí)間、修改時(shí)間等。
  • 文件目錄樹(shù)

    在多級(jí)目錄下一般對(duì)磁盤(pán)就可以抽象為

    • FCB數(shù)組

      FCB數(shù)組就是將所有盤(pán)塊的FCB信息都集中到一個(gè)數(shù)組中

    • 數(shù)據(jù)盤(pán)塊集合

      在每個(gè)數(shù)據(jù)盤(pán)塊里都包含一些目錄項(xiàng)用來(lái)找到子目錄,目錄項(xiàng)也就是文件名+對(duì)應(yīng)的FCB的“地址”,也就是去之前的FCB數(shù)組中找到相應(yīng)的FCB

    在磁盤(pán)進(jìn)行格式化的時(shí)候,會(huì)存放一些信息用來(lái)知道一些磁盤(pán)信息和找到根目錄

    • inode位圖: 哪些inode空閑,哪些被占用

    • 超級(jí)塊:記錄兩個(gè)位圖有多大等信息

    • 盤(pán)塊位圖: 哪些盤(pán)塊是空閑的,硬盤(pán)大小不同這個(gè)位圖的大小也不同

    文件的實(shí)現(xiàn)

    在我之前實(shí)現(xiàn)的FragileOS里文件系統(tǒng)非常簡(jiǎn)陋,基本沒(méi)有什么好說(shuō)的。這其實(shí)也是為什么之前把這個(gè)系列改了一個(gè)方向來(lái)結(jié)合的看Linux0.11的代碼。所以來(lái)看一下Linux0.11里是怎么使用和實(shí)現(xiàn)文件系統(tǒng)的,

    讀取文件

    • 這是讀取文件的系統(tǒng)調(diào)用
    • 函數(shù)首先對(duì)參數(shù)有效性進(jìn)行判斷
    • 之后對(duì)文件的類型進(jìn)行判斷
    • 如果是目錄文件或者是常規(guī)文件就執(zhí)行讀取操作
    int sys_read(unsigned int fd,char * buf,int count) {struct file * file;struct m_inode * inode;if (fd>=NR_OPEN || count<0 || !(file=current->filp[fd]))return -EINVAL;if (!count)return 0;verify_area(buf,count);inode = file->f_inode;if (inode->i_pipe)return (file->f_mode&1)?read_pipe(inode,buf,count):-EIO;if (S_ISCHR(inode->i_mode))return rw_char(READ,inode->i_zone[0],buf,count,&file->f_pos);if (S_ISBLK(inode->i_mode))return block_read(inode->i_zone[0],&file->f_pos,buf,count);if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode)) {if (count+file->f_pos > inode->i_size)count = inode->i_size - file->f_pos;if (count<=0)return 0;return file_read(inode,file,buf,count);}printk("(Read)inode->i_mode=%06o\n\r",inode->i_mode);return -EINVAL; }
    • 根據(jù)i節(jié)點(diǎn)和文件結(jié)構(gòu),讀取文件中數(shù)據(jù)。
    • 首先判斷參數(shù)的有效性
    • 之后循環(huán)的調(diào)用bread來(lái)讀取數(shù)據(jù)
    • 之后復(fù)制chars字節(jié)到用戶緩沖區(qū)buf中
    • 最后是修改該i節(jié)點(diǎn)的訪問(wèn)時(shí)間為當(dāng)前時(shí)間和返回讀取的字節(jié)數(shù)
    int file_read(struct m_inode * inode, struct file * filp, char * buf, int count) {int left,chars,nr;struct buffer_head * bh;if ((left=count)<=0)return 0;while (left) {if ((nr = bmap(inode,(filp->f_pos)/BLOCK_SIZE))) {if (!(bh=bread(inode->i_dev,nr)))break;} elsebh = NULL;nr = filp->f_pos % BLOCK_SIZE;chars = MIN( BLOCK_SIZE-nr , left );filp->f_pos += chars;left -= chars;if (bh) {char * p = nr + bh->b_data;while (chars-->0)put_fs_byte(*(p++),buf++);brelse(bh);} else {while (chars-->0)put_fs_byte(0,buf++);}}inode->i_atime = CURRENT_TIME;return (count-left)?(count-left):-ERROR; }

    文件寫(xiě)入

    • 根據(jù)i節(jié)點(diǎn)和文件結(jié)構(gòu)信息,將用戶數(shù)據(jù)寫(xiě)入文件中
    • 首先確定數(shù)據(jù)寫(xiě)入文件的位置
    • 然后算出對(duì)應(yīng)的盤(pán)塊
    • 然后用戶緩沖區(qū)buf中復(fù)制c個(gè)字節(jié)到告訴緩沖塊中p指向的開(kāi)始位置處
    • 最后是修改該i節(jié)點(diǎn)的訪問(wèn)時(shí)間為當(dāng)前時(shí)間和返回讀取的字節(jié)數(shù)
    int file_write(struct m_inode * inode, struct file * filp, char * buf, int count) {off_t pos;int block,c;struct buffer_head * bh;char * p;int i=0;/** ok, append may not work when many processes are writing at the same time* but so what. That way leads to madness anyway.*/if (filp->f_flags & O_APPEND)pos = inode->i_size;elsepos = filp->f_pos;while (i<count) {if (!(block = create_block(inode,pos/BLOCK_SIZE)))break;if (!(bh=bread(inode->i_dev,block)))break;c = pos % BLOCK_SIZE;p = c + bh->b_data;bh->b_dirt = 1;c = BLOCK_SIZE-c;if (c > count-i) c = count-i;pos += c;if (pos > inode->i_size) {inode->i_size = pos;inode->i_dirt = 1;}i += c;while (c-->0)*(p++) = get_fs_byte(buf++);brelse(bh);}inode->i_mtime = CURRENT_TIME;if (!(filp->f_flags & O_APPEND)) {filp->f_pos = pos;inode->i_ctime = CURRENT_TIME;}return (i?i:-1); }

    文件目錄的實(shí)現(xiàn)

    打開(kāi)創(chuàng)建文件

    • 首先對(duì)參數(shù)進(jìn)行處理,然后搜索進(jìn)程結(jié)構(gòu)中文件結(jié)構(gòu)指針數(shù)組一個(gè)空閑的文件句柄
    • 接著為打開(kāi)文件在文件表中尋找一個(gè)空閑結(jié)構(gòu)項(xiàng)
    • 然后調(diào)用函數(shù)open_namei()執(zhí)行打開(kāi)操作,若返回值小于0,則說(shuō)明出錯(cuò),就釋放剛申請(qǐng)到的文件結(jié)構(gòu)
    • 然后為不同的文件類型做一些特殊的處理
    • 最后初始化打開(kāi)文件的文件結(jié)構(gòu),然后返回文件句柄
    int sys_open(const char * filename,int flag,int mode) {struct m_inode * inode;struct file * f;int i,fd;mode &= 0777 & ~current->umask;for(fd=0 ; fd<NR_OPEN ; fd++)if (!current->filp[fd])break;if (fd>=NR_OPEN)return -EINVAL;current->close_on_exec &= ~(1<<fd);f=0+file_table;for (i=0 ; i<NR_FILE ; i++,f++)if (!f->f_count) break;if (i>=NR_FILE)return -EINVAL;(current->filp[fd]=f)->f_count++;if ((i=open_namei(filename,flag,mode,&inode))<0) {current->filp[fd]=NULL;f->f_count=0;return i;} /* ttys are somewhat special (ttyxx major==4, tty major==5) */if (S_ISCHR(inode->i_mode)) {if (MAJOR(inode->i_zone[0])==4) {if (current->leader && current->tty<0) {current->tty = MINOR(inode->i_zone[0]);tty_table[current->tty].pgrp = current->pgrp;}} else if (MAJOR(inode->i_zone[0])==5)if (current->tty<0) {iput(inode);current->filp[fd]=NULL;f->f_count=0;return -EPERM;}} /* Likewise with block-devices: check for floppy_change */if (S_ISBLK(inode->i_mode))check_disk_change(inode->i_zone[0]);f->f_mode = inode->i_mode;f->f_flags = flag;f->f_count = 1;f->f_inode = inode;f->f_pos = 0;return (fd); }

    解析目錄

    • 先根據(jù)當(dāng)前路徑的第一個(gè)字符來(lái)判斷當(dāng)前路徑是絕對(duì)路徑還是相對(duì)路徑
    • 然后進(jìn)循環(huán)處理過(guò)程,分割每個(gè)目錄名
    • 得到這個(gè)目錄名后,調(diào)用查找目錄項(xiàng)函數(shù)find_entry()在當(dāng)前處理的目錄中尋找指定名稱的目錄
    • 如果找到這個(gè)目錄,就設(shè)置一些信息,然后繼續(xù)以該目錄項(xiàng)為當(dāng)前目錄繼續(xù)循環(huán)處理路徑名中的下一目錄名部分(或文件名)
    static struct m_inode * get_dir(const char * pathname) {char c;const char * thisname;struct m_inode * inode;struct buffer_head * bh;int namelen,inr,idev;struct dir_entry * de;if (!current->root || !current->root->i_count)panic("No root inode");if (!current->pwd || !current->pwd->i_count)panic("No cwd inode");if ((c=get_fs_byte(pathname))=='/') {inode = current->root;pathname++;} else if (c)inode = current->pwd;elsereturn NULL; /* empty name is bad */inode->i_count++;while (1) {thisname = pathname;if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) {iput(inode);return NULL;}for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++)/* nothing */ ;if (!c)return inode;if (!(bh = find_entry(&inode,thisname,namelen,&de))) {iput(inode);return NULL;}inr = de->inode;idev = inode->i_dev;brelse(bh);iput(inode);if (!(inode = iget(idev,inr)))return NULL;} }

    轉(zhuǎn)載于:https://www.cnblogs.com/secoding/p/11442659.html

    總結(jié)

    以上是生活随笔為你收集整理的我是如何学习写一个操作系统(九):文件系统的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。