?? linux.txt
字號:
當一個進程打開一個字符設備文件時(open.c,163),其實質是將該設備賦予此進程(將該設備的tty_struct中的進程組設為當前進程),以后此進程可操作該設備(如某進程打開了顯示器鍵盤設備文件,就等于進程獲得了顯示器鍵盤的使用權,之后進程即可通過直接讀寫顯示器鍵盤設備文件來進行顯示即輸入操作),具體(open.c,167)。
一個需要進行控制臺操作的進程的文件表內的第1,第2項為終端設備文件(顯示器鍵盤,即傳說中的標準輸入輸出句柄)。當要進行輸入或顯示操作時,直接讀寫終端設備文件即可。首先調用sys_write文件寫系統調用,而sys_write則根據文件類型而調用rw_char從而調用tty_write顯示數據(見19)。
一個tty_struct對應一個顯示器、一個鍵盤、一個字符設備文件及一個進程組,當用戶按鍵時鍵盤中斷將鍵值存入tty_struct的讀隊列(這和進程無關),當某一進程欲讀入字符時才會到tty_struct的讀隊列中去區;當某一進程要顯示信息時將信息存入tty_struct的寫隊列,并調用終端顯示con_write將數據顯示在屏幕上(實質是將欲顯示的數據寫入顯存中)。
讀:
tty_read:讀取輔助隊列中的字符并存到用戶數據緩沖區中并返回實際的讀取數,當隊列為空或不滿一行或數據態少時進程都將掛起,整個操作有一個時間上限,超過上限時無論是否讀取了指定數目的字符都將返回(若有信號需處理也將立即返回)(由進程調用)。
copy_to_cooked:每當鍵盤收到一個字符時就會將字符存入read_q中并調用copy_to_cooked,該函數將read_q中的字符轉換為規范格式并存入輔助隊列中(若回現標志置位則同事將字符存入write_q隊列中并顯示之),結束時喚醒等待輔助隊列的進程(注:此處純屬中斷處理和具體進程無關)(注:每當在鍵盤上按下一個按鍵就會執行一遍上述過程)
寫:
tty_write:將用戶緩沖區中的數據寫入write_q中,并調用con_write將數據顯示在屏幕上,若寫隊列滿則等待(并不掛起,僅調度其它進程運行)
con_write:將write_q中的數據經過轉換顯示到屏幕上(實質是將欲顯示的數據寫入顯存中)
16.串行終端
有4種情況都會引起串口中斷:1,modem狀態發生變化;2,線路發生變化;3,收到中斷輸入的字符;4,發送保持寄存器被打開(該寄存器只要為空就會發出中斷)。串口中斷處理程序首先取中斷表示寄存器中的值以判斷中斷源,對于前兩種情況只需讀取對應狀態寄存器的值即可復位該中斷,對于第三種情況對緩沖器執行讀操作即可復位該中斷(具體中斷程序類似控制臺,僅僅將讀入字符存入read_q中并調用copy_to_cooked進行格式轉換),對于第四種情況,中斷處理程序會從write_q中取出一個字符并發送置發送保持寄存器中,隨后終端會自動從中讀出該字符并顯示之,而此時為空的保持寄存器又會發出中斷,直到write_q為空時程序才會關閉發送保持寄存器并喚醒等待該隊列的進程。
17.緩沖
若要讀入塊設備上的一個塊:調用bread,而bread將調用getblk新分配一塊緩沖塊。若新分配的緩沖塊即為所要讀入的塊則直接返回該塊;否則調用讀設備塊操作ll_rw_block從設備上讀入指定的塊并存入新分配的緩沖塊中。在從設備上讀塊的時候,進程將掛起待完成讀操作后再喚醒(以指定的緩沖塊是否上鎖為進程掛起和喚醒的信號,因為ll_rw_block在進行讀操作時首先將會鎖定緩沖塊,待操作完成后再解鎖緩沖塊)。
ll_rw_block:將讀寫塊設備操作制作成一個請求項并掛入相應設備的請求隊列中。若請求隊列為空則觸發設備的驅動程序執行。
getblk:(分配一個新的緩沖塊),首先從hash表中去搜索,看待讀入的塊是否在緩沖區中,若在則直接返回,否則進入一般的緩沖塊申請流程。
一般的緩沖塊申請流程:找空閑塊(b_count=0),若沒有則將進程掛起(掛在空閑緩沖區等待隊列中,當執行完某一設備塊讀寫操作時會喚醒該隊列);若找到空閑塊(若有多個則取權重最小的)但該塊被鎖定,則掛起進程(掛在該緩沖塊的等待對流中,并且喚醒后若該塊又被別的進程改過將重新執行getblk);若找到空閑塊但該塊為臟則掛起當前進程(掛在該緩沖塊的等待對流中,并且喚醒后若該塊又被別的進程改過將重新執行getblk)并執行寫盤操作;當通過上述檢測后,最后再判斷一次該塊是否已在緩沖塊中(有可能在執行上述檢測過程中,該塊被其它進程讀入緩沖區);至此才找到了一塊不葬,空閑,未被鎖定的塊,然后設置該塊并返回。
引用計數為0的塊也可能是臟的或被鎖定的。
18.i節點
若要操作文件首先需把文件的i節點讀入內存。i節點操作(p450)
從設備上讀入i節點置內存i節點表的流程:首先搜索內存i節點表,若其中不含有欲讀入的i節點,則從設備上讀入指定的i節點;若其中含有欲讀入的i節點則需判斷其是否是另一文件系統的安裝點,若不是則返回該i節點,若是則將欲讀入的i節點重設為該文件系統的根i節點。
當某一i節點的引用次數為0時(內存i節點結構中)該i節點表項可另做它用,不過此時若其標志i_dirty為1則需要先將其寫入設備。
一般調用iget都會將所指定的i節點的引用計數加1,故使用完時。需調用iput將其釋放。
19.文件系統
一個文件系統由其超級塊與之對應;一個設備由一個設備文件i節點與之對應(其中存放著設備號)。文件系統無名,一個設備對應一個文件系統。
加載一個文件系統:由設備名獲得設備文件i節點從而獲得設備號,由安裝到的目錄名獲得安裝到目錄的i節點,讀入超級塊置內存超級塊數組中,讀入位圖置緩沖塊中,將文件系統的安裝到目錄設為上面剛才獲得的i節點(即超級塊的i_mount=inode)
根據路徑名搜索指定文件:即找到路徑名末端文件的i節點。在當前進程的root字段中存放著搜多的起始i節點,于是直接在此i節點中搜索子目錄項,隨后由子目錄項獲得對應的i節點,重復此過程直至找到指定的i節點為止。
在指定目錄i節點中搜索子目錄的一般情況:首先讀入一塊該i節點的數據塊(i_zone[],其中存放著目錄項)置緩沖區,然后在其中匹配子目錄名,若沒找到,則釋放該緩沖塊并再讀入一塊。若找到了子目錄項則根據設備號及目錄項中的i節點號讀入該i節點。
特殊情況:在某一目錄下搜索子目錄的i節點,若該子目錄為文件系統安裝點則直接返回該文件系統的根i節點;在某一文件系統的根目錄下搜索".."目錄項,則直接返回該文件系統安裝到i節點的".."目錄項。
文件路徑中某一文件系統的根i節點與文件系統安裝到的i節點可視為重合,如:
設備1根目錄\設備1目錄\設備2安裝到目錄(設備2根目錄)\設備2目錄
只有超級用戶才有新建文件的權限。
i節點中的i_nlink對于目錄i節點而言為其中目錄項的個數。
一個文件可能同時有幾個目錄指向它。
新建的文件只有一個i節點,其數據塊是空的;新建一個目錄,其數據塊中將含有".",".."兩項。
刪除一個目錄時,不允許刪除自己或某個文件系統的根目錄或非空的目錄,具體步驟是直接從父目錄中刪除對應的目錄項并置i_nlink為0,待執行iput時實際刪除;刪除一個文件時,直接從父目錄中刪除對應的目錄項,但僅僅將i_nlink-1(因為可能有多個目錄指向一個文件)待i_nlink為0時才會實際刪除。
文件讀寫:若要寫入整塊數據則可直接申請一緩沖塊;若寫入的數據不滿一塊考慮到原數據則必須從設備上讀入該塊。
管道讀寫:p503
創建管道:包括新建一個管道i節點、令當前進程的文件表中的兩項指向該i節點(一項設為讀,一項設為寫),將進程文件表中那兩項的索引值賦予fildes(返回值)。
sys_read(p513):讀當前進程文件表中指定文件的數據,若該文件為管道文件則調有read_pipe;若文件為字符設備文件(代表顯示器鍵盤等)則調用rw_char;若文件為塊設備文件則調用block_read,若為普通的目錄或文件則調用file_read。(以上讀寫函數都在fs目錄下)
rw_char:根據不同設備號來調用相應的字符設備讀寫函數(如終端讀寫tty_read,tty_write,見15)
使用文件前需打開文件:獲得指定路徑名的i節點,在內存文件表中申請一項使該項指向該i節點,使當前進程的文件表中的一項指向剛才申請到的文件表項,并返回該項的索引(句柄)。
申請內存文件表項時的依據是引用計數為0,出現內存文件表項引用計數>1是由于創建子進程時增加了文件引用計數。
20.用戶權限
一個文件的權限由其i節點中的i_mode及uid,guid三個字段來處理。
i_mode的6-8位代表其宿主用戶對該文件的操作權限(即進程的uid=i節點的uid,說明當前進程為該文件的宿主用戶)
i_mode的3-5位代表其組用戶對該文件的操作權限(即進程的guid=i節點的guid,說明當前進程與該文件的宿主用戶同組)
i_mode的0-2位代表其它用戶對該文件的操作權限(即同時不滿足上述兩情況)
注:超級用戶擁有所有文件的所有訪問權限
21.exective
ls -l/home/john/
當用戶在提示符下鍵入上述命令時,shell進程會創建一個新進程并在其中執行/bin/ls命令,在加載/bin/ls執行文件時,三個命令行參數ls、-l、/home/john/將被新進程繼承下來(被保存于進程用戶堆棧的頂端,當程序需要明確用到環境變量時這些環境變量也會被存放到用戶堆棧的頂端,去掉這些空間再往左將是一個用來指向上述參數及環境變量的指針表,再往左即為進程的用戶太堆棧頂,p524認真看)(以上為新程序在進程邏輯空間中的初始化)
execve(文件名,命令行參數,環境變量):某一進程調用execve時,首先根據文件名獲得其i節點,然后讀入該文件的第一個數據塊(其中存放著文件執行頭結構),判斷該頭結構:
(A)若是可執行文件(linux0.11只能執行可執行文件,即無重定位信息)則將命令行參數及環境變量參數存入新申請的內存頁(空,可執行文件命令行參數,可執行文件環境變量);
(B)若是腳本文件(腳本文件以#!開頭后接解釋程序名,如:#! erc\sh,腳本文件需由解釋程序來執行)則放回腳本文件i節點,并讀入解釋文件的i節點,隨后將命令行參數及環境變量參數存入新申請的內存頁(空,解釋程序名,解釋程序參數,腳本名,腳本參數,腳本環境變量)
然后重設進程PCB,具體如下:
executable字段設為執行文件的i節點
復位所有信號處理句柄
根據close_on_exec位圖關閉指定文件
釋放原來的頁表及頁,
根據執行文件的執行頭結構重設ldt
將內存頁中128k的環境變量和命令行參數放入進程邏輯空間末端(即此時進程的邏輯空間的分布為:0 空 命令行參數 環境變量 64m)
重設進程用戶態堆棧頂(64m-環境變量和命令行參數的空間)
使進程返回用戶態時返回到新的執行文件開始處
pcb中的executable字段為進程執行文件的i節點
a.out文件格式(p539)
某進程在內存中的分布圖(p554)
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -