我們都知道,在Linux關機的之前都會要運行一個命令那就是sync,這個命令是同步的意思,那為什么要運行這個?而且之前的數據改變我們已經看見了,為什么還要運行這個命令?要回答這個問題就要說一下Linux在這方面的執行機制。
首先我們要從buffer和cache說起,如下圖:
buffer和cache都可以翻譯成緩存,但是到底有什么區別呢?
cache:
目的是為了數據重復使用,在一定程度上解決讀的效率,這里就是用來存放經常用到的數據,而不用每次都去磁盤上面讀取,如果本次操作用到的數據沒有,則會到磁盤上去尋找。這樣就可以在一定程度上協議快速和慢速設備(比如:CPU和硬盤),另外cache也有換進換出機制,就是把原來經常用到,現在不常用的清除掉,這樣就可以有空間放最近訪問的數據了,以達到下次訪問就直接從cache中讀取。
buffer:
為了提高寫如磁盤時的效率,其實也是為了協調快慢設備,避免造成把數據都提交到寫入磁盤隊列造成擁堵,因為內核把數據提交到寫入隊列不可能不管,它必須要等到有返回值才行。所以buffer的作用是先把數據寫入(Linux中的write()函數)到buffer中,然后后台再去根據其他機制,把buffer中的數據提交到隊列,最終完整同步到磁盤的過程。
我們知道用戶發起的程序都會運行在內存中的用戶空間內,這時候數據是放在內存中的,如果我們此時需要保存數據,這時候系統其實是先調用一個write()函數,然后再調用sync()或者fsync()函數(對於任何程序來說只要想把數據寫入磁盤其過程都一樣,有些也有例外)。
順便說一句,這也就是為什么有人說Linux比Windows消耗內存的原因。
用戶空間:常規進程所在區域,用戶發起的,此區域的代碼不能直接訪問硬件
內核空間:操作系統所在區域,能訪問硬件
當調用了write()函數時,該函數一旦返回正常值,我們可能就認為數據已經寫入到了磁盤,但實際上,操作系統在實現磁盤文件的IO時,為了保證IO的效率,會在內存中使用一段專門的地址空間,該空間叫做內核空間,而內核空間之內又會有一段是用作IO的數據緩沖區(這個緩沖區就是buffer),write()函數的作用就是把數據寫入到內核空間的IO緩沖區中。
內核空間的IO緩沖區也有一定大小,當該緩沖區沒有寫滿時或者沒有到一個同步周期時,會持續的把write()函數傳遞的數據寫入到該緩沖區中,而當該緩沖區寫滿或者到了一個同步周期,則會把該緩沖區的內容提交到輸出隊列,當需要數據到達隊列隊首的時候,開始執行真正的磁盤IO操作,把數據寫入磁盤(這里雖然用了寫入磁盤,但是真正的動作不是移動而是復制,復制完成之后,內核空間的IO緩沖區才會釋放該數據占用的空間)。這種方式叫做延遲寫入。
所以這就會出現一個問題,當調用了write()函數后並不等於數據真的保存到了磁盤,但是這里又會有一個錯覺,就是你再次請求該文件的時候,可以顯示你最后一次更新的內容,其實這個內容並不是從磁盤上讀取過來的,而是從用戶空間的緩沖區讀取的。接着剛才提到的問題,如果數據在內核空間的IO緩沖區內,而此時操作系統出現故障、斷電等異常情況就會造成數據丟失。
為了解決數據丟失問題,Unix系統提供了sync、fsync和fdatasync三個函數。
函數 | 功能 |
sync | 函數返回0表示成功,該函數負責把所有內核空間中IO緩沖區內修改過的內容推送到輸入隊列,然后就返回,它並不等待所有磁盤IO操作完成。所以即使調用了sync函數,也不等於成功保存到磁盤了。 |
fsync | 函數返回0表示成功,與sync不同,它只會對指定文件描述符的單一文件生效,強制與該文件相連的所有修改過的數據傳送到磁盤上,並且等待磁盤IO完畢,然后返回。當該函數返回0時,才真正表示成功保存到磁盤。數據庫會在調用了write()之后調用fsync()。 |
fdatasync | 它與fsync類似,它只影響文件數據部分,不涉及數據屬性,比如inode信息。所以相對於fsync它需要較少的寫磁盤操作。 |
看了上面的內容你就應該明白為什么關機前要運行一下sync命令了。