一个操作系统的实现(3)
文件系統(tǒng)
文件系統(tǒng)是建立在硬盤上的一個程序,所以由2部分組成:驅(qū)動和管理文件系統(tǒng)的進程FS。
首先看一下驅(qū)動是如何工作的:
通常,主板上面有2個IDE插槽,分別叫做IDE0/IDE1。每個IDE通道又可以接2個設(shè)備。驅(qū)動進程的目的就是要隱藏硬件細(xì)節(jié),向FS進程提供統(tǒng)一的接口,具體到這里,驅(qū)動為FS提供的接口就是打開,讀取,寫入,關(guān)閉等接口。下面是硬盤驅(qū)動程序:
Task_hd()
{
???????Recv(any, msg) //接受任何進程傳來的消息
???????If(msg == DEV_OPEN)??//如果是打開設(shè)備消息,則對硬盤寄存器做一些操作,比如獲取硬盤信息等。
???????{
??????????????Out_byte(***)???//向硬盤寄存器寫入數(shù)據(jù)
??????????????Recv(interrupt, msg) //阻塞等待硬盤中斷發(fā)生(表示需要的數(shù)據(jù)已經(jīng)得到)
???????}
???????Send(src, msg) //將獲取的硬盤信息返回給發(fā)送者進程
}
上面的程序之添加了一個讀取硬盤信息的消息,其他比如讀取寫入數(shù)據(jù)的消息和這個都是類似的。
有一點需要注意,驅(qū)動程序task_hd在讀取硬盤信息或者寫入數(shù)據(jù)到硬盤時候的操作其實比上面的情況略微復(fù)雜,下面來看一個完成的調(diào)用過程:
1 task_fs?文件系統(tǒng)進程?需要操作硬盤,給硬盤驅(qū)動進程task_hd()?發(fā)送一個msg。
2?假設(shè)這時,task_hd()處于空閑狀態(tài),即等待在recv(any,msg)處,則會接受這個消息,并向硬盤發(fā)送命令。
3?硬盤完成工作后,會觸發(fā)中斷, Recv(interrupt, msg)返回,并給task_fs?文件系統(tǒng)進程發(fā)送硬盤數(shù)據(jù)消息。
4 task_fs?文件系統(tǒng)進程?得到CPU時間時,會收到硬盤數(shù)據(jù)消息。
?
?
最簡單的硬盤驅(qū)動看來是告一段落了。下面就可以在此基礎(chǔ)上實現(xiàn)一個文件系統(tǒng),首先需要明白一個文件系統(tǒng)的幾個基本要素:
1)??有地方存放metadata(一般為硬盤第二個扇區(qū),因為第一個扇區(qū)為引導(dǎo)分區(qū))
2)??有地方記錄扇區(qū)使用情況(位圖法)
3)??有地方記錄任一文件信息,包括文件名,修改時間,占用哪些扇區(qū)等(一個i-node數(shù)組,每個元素包含了文件名,屬性等信息。同時也需要一個位圖來表示i-node數(shù)組的使用情況)
4)??文件索引
以后就按照這個方法來組織硬盤結(jié)構(gòu),創(chuàng)建/刪除/寫入/讀取文件不過是按照這種格式來組織硬盤而已
?
為了實現(xiàn)多系統(tǒng)在硬盤上共存,必須將硬盤劃分為多個分區(qū)。每個系統(tǒng)占用一個分區(qū)。通過一個硬盤的引導(dǎo)扇區(qū)可以設(shè)置硬盤分區(qū),硬盤最多可以分為4個物理分區(qū)。其中每個分區(qū)還可以繼續(xù)劃分為多個邏輯分區(qū)。
一般Linux的設(shè)備分為主設(shè)備號和次設(shè)備號,主設(shè)備號表示不同的物理設(shè)備(硬盤,軟盤);次設(shè)備號表示物理設(shè)備上的分區(qū)。歸納一下就是:主設(shè)備號告訴OS用哪個驅(qū)動程序來處理,次設(shè)備號告訴驅(qū)動程序這是哪個設(shè)備。
?
下面看一下硬盤驅(qū)動讀取和寫入數(shù)據(jù)的過程:
1?首先還是一樣task_fs()?文件系統(tǒng)進程通過前面實現(xiàn)的IPC機制將讀取(寫入)硬盤數(shù)據(jù)消息發(fā)送(即復(fù)制)給task_hd()
2 task_hd()
{
???????…
???????Case:DEV_OPEN //如果某用戶進程需要打開一個文件
??????????????為文件內(nèi)容分配扇區(qū)
??????????????分配一個i-node
??????????????分配inode-map
??????????????分配sector-map
??????????????創(chuàng)建文件索引
?
???????Case: DEV_READ //讀文件
??????????????首先找到將要讀取的文件的文件描述符和位置
??????????????根據(jù)文件描述符通過驅(qū)動獲取的該文件在硬盤的具體位置(即i-node)。
??????????????將硬盤內(nèi)容復(fù)制到緩存
}
這一部分寫的不是很清楚,理一下思路,我的理解就是硬盤首先有一個文件系統(tǒng)結(jié)構(gòu)(fat32,ntfs,ext2等等),相對于的就有一個硬盤驅(qū)動程序用來管理硬盤使用的文件系統(tǒng)并對上層提供文件操作的接口。上層用戶需要讀取硬盤數(shù)據(jù)的時候,首先將消息發(fā)送給FS,然后FS會將該消息發(fā)送給硬盤驅(qū)動程序,硬盤驅(qū)動程序根據(jù)硬盤所使用的文件格式來找到相應(yīng)的數(shù)據(jù)并吧數(shù)據(jù)復(fù)制到OS提供的一個緩存中拱用戶進程使用。
?
?
????????????????????????????????????????????????????????內(nèi)存管理
到了現(xiàn)在,還沒有完成了就是和內(nèi)存相關(guān)的一些東西,首先看看如何創(chuàng)建一個進程(fork),每個shell就是一個子進程。
一個新的進程需要的條件有:
1?代碼,數(shù)據(jù),堆棧(從父進程復(fù)制過來)
2?在進程表中占據(jù)一個位置
3?在GDT中有一個位置,指向了該進程對應(yīng)的LTD,也就是指向了進程的數(shù)據(jù)和堆棧等
?
看一下linux中fork用法:
First_proc()
{
???????Int id = fork();
???????If(pid != 0) //這是父進程
???????Else?子進程運行
}
父子進程公用一個代碼段
?
可以看到,內(nèi)存管理主要需要在OS中增加2個進程,分別為所有用戶的祖先進程(INIT0)和MM進程(memory Management process)。
Init0進程有些特殊,首先一般的進程的虛擬內(nèi)存空間對用戶來說是0-4GB,但是init0的內(nèi)存在這里大致等于內(nèi)核占用內(nèi)存的大小。
Fork的大概步驟如下:
1?用戶調(diào)用fork
2?通過消息機制發(fā)送FORK消息給MM
3 MM主循環(huán)完成創(chuàng)建進程的工作,主要流程為:找一個空閑的進程表項作為新進程的進程表項;讀取父進程的表示內(nèi)存占用的LDT,從中取出父進程的代碼,數(shù)據(jù)和堆棧段內(nèi)存首地址和范圍。
3?根據(jù)父進程的內(nèi)存占用情況,分配相同大小的內(nèi)存給子進程。(分配內(nèi)存需要注意的就是內(nèi)存不要重復(fù)使用),然后將父進程的內(nèi)存中的內(nèi)容復(fù)制給子進程。
4?如果父進程有打開文件,要給FS進程發(fā)送一個消息來處理父子進程間的共享文件。(FS利用計數(shù)器實現(xiàn))
?
銷毀一個進程也差不多,唯一需要注意的就是MM在銷毀一個進程的時候,需要首先銷毀他的所有子進程。
?
?
?
接下來說明一下幾個概念,大多是復(fù)制過來的:
CRT:一個應(yīng)用程序只能調(diào)用2種東西:屬于自己的函數(shù),以及中斷(系統(tǒng)調(diào)用就是軟中斷)。但是實際上,OS還為普通應(yīng)用程序提供了一個CRT,這個庫里面有已經(jīng)編譯好的庫函數(shù)代碼。用戶應(yīng)用程序可以很方便的使用CRT,而免去了很多中斷調(diào)用。
?
???????OS自帶應(yīng)用程序:每個OS都可以附帶和安裝很多應(yīng)用程序,簡單來說目的就是為了把應(yīng)用程序從光盤中安裝到OS認(rèn)識的硬盤中并知道位置,這樣在需要用時,OS可以自己找到該應(yīng)用程序。例如,一個最簡單的應(yīng)用程序:
_start
Main(){printf(***);}
把他安裝到OS中的步驟大致如下:
1?編譯連接該應(yīng)用程序并打包(設(shè)為inst.tar)
2?將打包的應(yīng)用程序二進制文件寫入OS安裝盤的某扇區(qū)(設(shè)該扇區(qū)號為X)。
3?啟動系統(tǒng)時,在mkfs()創(chuàng)建文件系統(tǒng)時建立一個新的文件cmd.tar,他對應(yīng)的起始文件扇區(qū)號為X。
4?在init進程中,將cmd.tar解壓,將其中包含的文件(即為inst.tar)存入文件系統(tǒng)。
?
?
Exec才等同于windows里面的CreartProcess.
First_proc()
{
???????Int id = fork();
???????If(pid != 0) //這是父進程
???????Else?子進程運行
???????{
??????????????Exec()???//參數(shù)即為新的代碼段
???????}
}
?
Exec的執(zhí)行流程:
1?檢查參數(shù)個數(shù)并將它們依次存放到某連續(xù)內(nèi)存處
2?向MM發(fā)送消息,消息體就是上面存放參數(shù)的連續(xù)內(nèi)存。
3 MM消息接收到這個消息,將參數(shù)取出(由于exec所在進程和MM不是同一個進程,所有他們的虛擬地址不一樣,MM需要通過物理地址復(fù)制的方式來獲取消息)
4?根據(jù)exec傳來的參數(shù),MM將要執(zhí)行文件復(fù)制到自己的緩沖區(qū)中
5?被執(zhí)行文件是elf格式,MM根據(jù)格式將被執(zhí)行文件的各個段放置到合適的空閑內(nèi)存中。
6?建立棧
?
?
OK all done with simplest mode..?
2011.1.15 AM8:45 ?
i love joliet - teddy_xiong ?in ZNUFE
sylar MAIL: cug@live.cn轉(zhuǎn)載于:https://www.cnblogs.com/xumaojun/p/8544130.html
總結(jié)
以上是生活随笔為你收集整理的一个操作系统的实现(3)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 非线性系统 知识梳理
- 下一篇: 回数