大香蕉一区二区三区,97午夜理论片在线观看,精品穿情趣制服国产精品,97香蕉久久夜色精品国产,亚洲精品xxxxx在线观看,欧美成人怡红院一区二区,69久久99精品久久久久婷婷,欧美一区二区三区四区在线观看,久久99精品视免费看,亚洲一区二区在线看

添加微信
立即線上溝通

客服微信

客服微信

×

詳情請咨詢客服

客服熱線

186-8811-5347、186-7086-0265

官方郵箱

contactus@mingting.cn

我知道了

無法讀取源文件或磁盤是什么原因

2022-05-15 來源:金山毒霸電腦優(yōu)化作者:電腦技巧&問題

Linux?下有?3?種“拷貝”,分別是?ln,cp,mv,這?3?個命令貌似都能?copy?出一個新的文件出來。

細心的小伙伴看到我給?“拷貝”?打上了雙引號?因為?Linux?的這?3?個命令有極大的區(qū)別,雖然用戶看起來是拷貝出了新文件。

你是否曾經遇到過以下問題,想通原因了嗎?:

ln?創(chuàng)建鏈接文件,軟鏈接可以跨文件系統(tǒng),硬鏈接跨文件系統(tǒng)會報錯,為什么?;

mv?好像有時候快,有時候非常慢,有些時候還會殘留垃圾,為什么?;

cp?拷貝數(shù)據(jù)有時快,有時候非常慢,源文件和目標文件所占物理空間竟然不一致?

本篇文章看完,希望你以上問題不再有疑問,從容使用?ln,mv,cp?命令。

溫馨提示:

以下我們只討論文件的簡單操作,關于目錄操作或者復雜參數(shù)的操作不在我們本次主題以內,我們忽略;

coreutils?庫的代碼版本用的是?8.3;

我們來看下簡單的?3?個命令操作。首先在執(zhí)行以下命令之前,準備一個不小的?test?的普通文件(比如?1G?)。

"拷貝"命令一:?ln

#?創(chuàng)建一個軟鏈接文件

ln?-s?./?test./test_soft_link

#?創(chuàng)建一個硬連接文件

ln?./?test./test_hard_link

你會發(fā)現(xiàn)當前目錄出現(xiàn)了兩個新文件?test_soft_link?,test_hard_link?。并且你會發(fā)現(xiàn)拷貝速度好快?為什么呢?

"拷貝"命令二:?mv

把?test?文件"拷貝"到?./backup/?目錄

mv?./?test./backup/

更神奇的是,好像?copy?一個?1?G?的文件,速度也賊快?

“拷貝”命令三:?cp

把?test?文件"拷貝"到?./backup/?目錄

cp?./?test./backup/

上面我們看到,好像?ln,mv,cp?這?3?個命令都是“拷貝”?好像都進行了數(shù)據(jù)復制出了新的文件?

答案:當然不是。這?3?個看起來都是復制出了新文件,但其實天壤之別。我們一個個來揭秘。

在揭秘這?3?個命令之前,我們必須先復習文件的基礎知識點,?Linux?的文件和目錄的關系。

Linux?的文件和目錄

在深度剖析?Linux?cp?的秘密一文中,我們詳細剖析了文件系統(tǒng)的形態(tài)。有幾個關鍵知識點:

文件系統(tǒng)內有?3?個關鍵區(qū)域:超級塊區(qū)域,inode?區(qū)域,數(shù)據(jù)?block?區(qū)域;

其中一個?inode?和一個文件對應,包含了文件的元數(shù)據(jù)信息;

一個?inode?有唯一的編號,可以理解成就是單調遞增的整數(shù)。比如?1,2,3,4,5,6,,,,;

關于上面,我們注意到?inode?其實標識的是一個平坦的結構,inode?索引到數(shù)據(jù)?data?區(qū)域,每個?inode?都有唯一編號。

問題來了:Linux?的目錄是一個倒掛的樹形結構呀,為什么上面說?inode?是平坦的結構?如下:

Linux?的文件確實是樹形結構,inode?也確實是平坦的結構。你會感覺到因為是因為之前故意忽略了一個幾個東西:目錄文件和?dentry?項。這是兩個非常重要的概念,我們逐個解釋下。

文件系統(tǒng)中其實有兩種文件類型,分為:

普通文件(這里把鏈接文件包含在普通文件以內)

目錄文件

可以通過?inode->i_mode?字段,使用?S_ISREG,S_ISDIR?這兩個宏來判斷是哪個類型。普通文件很容易理解,就是普通的數(shù)據(jù)文件,inode?里面存儲元數(shù)據(jù),inode?可以索引到?block,block?里面存儲用戶的數(shù)據(jù)。目錄文件?inode?存儲元數(shù)據(jù),block?里面存儲的是目錄條目。目錄條目是什么樣子的東西?

舉個形象的例子:在當前?testdir?目錄下,有?dir1,dir2,dir3?這三個文件。假設?dir1?的?inode?編號是?1024,dir2?是?1025,dir3?是?1026。

那么現(xiàn)實是這樣的:

testdir?這個目錄首先會對應有一個?inode,inode->i_mode?的類型是目錄,并且還會有?block?塊,通過?inode->i_blocks?能索引到這些?block;

block?里面存儲的內容很簡單,是一個個目錄條目,內核的名字縮寫為?dirent,每一個?dirent?本質就是一個?文件名字?到?inode?編號的映射,所以,testdir?這個目錄文件的?block?里存了?3?條記錄?[dir1,?1024],[dir2,?1025],[dir3,?1026];

所以,目錄到底是什么呢?就存儲形態(tài)而已,目錄也是文件,存儲的是?名字?到?inode?number?的映射表。dirent?其實就是?directory?entry?的縮寫。

好像還沒講到樹形結構?

其實已經講了一半了,樹形結構的數(shù)據(jù)結構基礎已經有了,就是目錄文件和?dirent?的實現(xiàn)。

假設葉子結點的為普通文件

針對開篇的圖,其實磁盤上存儲了?3?個目錄文件

這個時候,讀者朋友你是不是都可以用筆畫出一個樹形結構了,內存的樹形結構也是這么來的。通過磁盤的映射數(shù)據(jù)構造出來。在內存中,這個樹形結構的節(jié)點用?dentry?來表示(通常翻譯成目錄項,但是筆者認為這個翻譯很容易讓人誤解)。

以下是筆者從內核精簡出來的?dentry?結構體,通過這個總結到幾個信息:

dentry?綁定到唯一一個?inode?結構體;

dentry?有父,子,兄弟的索引路徑,有這個就足夠在內存中構建一個樹了,并且事實也確實如此;

structdentry{

//?...

structdentry*?d_parent;?/*?父節(jié)點?*/

structqstrd_name;?//?名字

structinode*?d_inode;?//?inode?結構體

structlist_headd_child;?/*?兄弟節(jié)點?*/

structlist_headd_subdirs;?/*?子節(jié)點?*/

};

所以,看到現(xiàn)在理解了嗎?父、子?指針,這就是經典的樹形結構需要的字段呀。目錄文件類型為樹形結構提供了存儲到磁盤持久化的一種形態(tài),是一種?map?表項的形態(tài),每一個表項我們叫做?dirent?。文件樹的結構在內存中以?dentry?結構體體現(xiàn)。

劃重點:仔細理解下?dirent?和?dentry?的概念和形態(tài),仔細理解磁盤的數(shù)據(jù)形態(tài)和內存的數(shù)據(jù)結構形態(tài),后面要考的。

ln?命令

ln?是?Linux?的基礎命令之一,是?link?的縮寫,顧名思義就是跟鏈接文件相關的一個命令。一般語法如下:

ln?[OPTION]...?TARGET?LINK_NAME

ln?可以用來創(chuàng)建一個鏈接文件,有趣的是,鏈接文件有兩個不同的類別:

軟鏈接文件

硬鏈接文件

1?什么是軟鏈接文件?

無論是軟鏈接還是硬鏈接都是“鏈接”文件,也就是說,通過這個鏈接文件都能找到背后的那個“源文件”。首先說結論:

軟鏈接文件是一個全新的文件,有獨立的?inode,有自己的?block?,而這個文件類型是“鏈接文件”的類型而已;

這個軟鏈接文件的內容是一段?path?路徑,這個路徑直接指向源文件;

所以,你明白了嗎?軟鏈接文件就是一個文件而已,文件里面存儲的是一個路徑字符串。所以軟鏈接文件可以非常靈活,鏈接文件本身和源解耦,只通過一段路徑字符串尋路。

所以,?軟鏈接文件是可以跨文件系統(tǒng)創(chuàng)建的。

有興趣的小伙伴可以去看源碼實現(xiàn),在?coreutils?庫里,調用棧如下:

main?->?do_link?->?force_symlinkat?->?symlinkat

也就是說最終調用的是系統(tǒng)調用?symlinkat?來完成創(chuàng)建,而這個?symlinkat?系統(tǒng)調用在內核由不同的文件系統(tǒng)實現(xiàn)。舉個例子,如果是?minix?文件系統(tǒng),那么對應的函數(shù)就是?minix_symlink。minix_symlink?這個函數(shù)上來就是新建一個?inode?,然后在對應的目錄文件中添加一個?dirent?。來來來,我們看一眼?minix_symlink?的主干代碼:

staticintminix_symlink(struct?inode?*?dir,?struct?dentry?*dentry,

constchar*?symname)

{

//?...

//?新建一個?inode,inode?類型為?S_IFLNK?鏈接類型

inode?=?minix_new_inode(dir,?S_IFLNK?|?0777,?&err);

if(!inode)

gotoout;

//?填充鏈接文件內容

minix_set_inode(inode,?0);

err?=?page_symlink(inode,?symname,?i);

if(err)

gotoout_fail;

//?綁定?dentry?和?inode

err?=?add_nondir(dentry,?inode);

//...

}

劃重點:軟鏈接文件是新建了一個文件,文件類型是鏈接文件,文件內容就是一段字符串路徑。分配新的?inode,內存對應新的?dentry?,當然了,也新增了一個?dirent?。軟鏈文件可以跨越不同的文件系統(tǒng)。

2?什么是硬鏈接文件?

現(xiàn)在我們知道了,軟鏈接文件怎么找到源文件的?通過路徑找到的,路徑就存儲在軟鏈接文件中。硬鏈接文件又怎么辦到的呢?

硬鏈接很神奇,硬鏈接其實是新建了一個?dirent?而已。下面是重點:

硬鏈接文件其實并沒有新建文件(也就是說,沒有消耗?inode?和?文件所需的?block?塊);

硬鏈接其實是修改了當前目錄所在的目錄文件,加了一個?dirent?而已,這個?dirent?用一個新的?name?名字指向原來的?inode?number;

重點來了,由于新舊兩個?dirent?都是指向同一個?inode,那么就導致了一個限制:?不能跨文件系統(tǒng)。因為,不同文件系統(tǒng)的?inode?管理都是獨立的。

感興趣的同學可以試下,跨文件系統(tǒng)創(chuàng)建硬鏈接就會報告如下錯誤:?Invalid?cross-device?link

sh-4.4?#?ln?/dev/shm/source.txt?./dest.txt

ln:?failed?to?create?hard?link?'./dest.txt'=>?'/dev/shm/source.txt':?Invalid?cross-device?link

有興趣的小伙伴可以去看源碼實現(xiàn),在?coreutils?庫里,調用棧如下:

main?->?do_link?->?force_linkat?->?linkat

也就是說最終調用的是系統(tǒng)調用?linkat?來完成創(chuàng)建,而這個?linkat?系統(tǒng)調用在內核由不同的文件系統(tǒng)實現(xiàn)。舉個例子,如果是?minix?文件系統(tǒng),那么對應的函數(shù)就是?minix_link。這個函數(shù)從內存上來講是把一個?dentry?和?inode?關聯(lián)起來。從磁盤數(shù)據(jù)結構上來講,會在對應目錄文件中增加一個?dirent?項。

劃重點:硬鏈接只增加了一個?dirent?項,只修改了目錄文件而已。不涉及到?inode?數(shù)量的變化。新的?name?指向原來的?inode。

mv?命令

mv?是?move?的縮寫,從效果上來看,是把源文件搬移到另一個位置。

你是否思考過?mv?命令內部是怎么實現(xiàn)的呢?

是把源文件拷貝到目標位置,然后刪除源文件嗎?所以,說?mv?貌似也是“拷貝”?

其實,并不是,準確的說不完全是。

對于?mv?的討論,要拆分成源和目的文件是否在同一個文件系統(tǒng)。

1?源?和?目的?在同一個文件系統(tǒng)

mv?命令的核心操作是系統(tǒng)調用?rename?,rename?從內核實現(xiàn)來說只涉及到元數(shù)據(jù)的操作,只涉及到?dirent?的增刪(當然不同的文件系統(tǒng)可能略有不同,但是大致如是)。通常操作是刪除源文件所在目錄文件中的?dirent,在目標目錄文件中添加一個新的?dirent?項。

劃重點:inode?number?不變,inode?不變,不增不減,還是原來的?inode?結構體,所以數(shù)據(jù)完全沒有拷貝。

mv?的調用棧如下,感興趣的可以自己調試。

main?->?renameat2

main?->?movefile?->?do_move?->?copy?->?copy_internal?->?renameat2

我們用例子來直觀看下,首先準備好一個?source.txt?文件,用?stat?命令看下元數(shù)據(jù)信息:

sh-4.4?#?stat?source.txt

File:?source.txt

Size:?0?Blocks:?0?IO?Block:?4096?regular?empty?file

Device:?78h/120d?Inode:?3156362?Links:?1

Access:?(0644/-rw-r--r--)?Uid:?(?0/?root)?Gid:?(?0/?root)

sh-4.4?#?mv?source.txt?dest.txt

然后?stat?看下?dest.txt?文件的信息:

sh-4.4?#?stat?dest.txt

File:?dest.txt

Size:?0?Blocks:?0?IO?Block:?4096?regular?empty?file

Device:?78h/120d?Inode:?3156362?Links:?1

Access:?(0644/-rw-r--r--)?Uid:?(?0/?root)?Gid:?(?0/?root)

2?源和目的在不同的文件系統(tǒng)

還記得之前我們提過,由于硬鏈接是直接在目錄文件中添加一個?dirent,名字直接指向源文件的?inode?,不同文件系統(tǒng)都是獨立的一套?inode?管理系統(tǒng),所以硬鏈接不能跨文件系統(tǒng)。

那么問題來了,mv?遇到跨文件系統(tǒng)的場景呢,怎么處理?是否還是?rename??

舉個例子,如下命令,源和目的是不同的文件系統(tǒng)。我虛擬機的掛載點如下:

sh-4.4?#?df?-h

Filesystem?Size?Used?Avail?Use%?Mounted?on

overlay?59G?3.5G?52G?7%?/

tmpfs?64M?0?64M?0%?/dev

shm?64M?0?64M?0%?/dev/shm

我故意挑選?/home/qiya/testdir?和?/dev/shm/?,這兩個目錄分別對應了?"/"?和?"/dev/shm/"?的掛載點的文件系統(tǒng),分屬兩個不同的文件系統(tǒng)。我們先提前看下源文件的信息(主要是?inode?信息):

sh-4.4?#?stat?/dev/shm/source.txt

File:?/dev/shm/source.txt

Size:?0?Blocks:?0?IO?Block:?4096?regular?empty?file

Device:?7fh/127d?Inode:?163990?Links:?1

Access:?(0644/-rw-r--r--)?Uid:?(?0/?root)?Gid:?(?0/?root)

我們執(zhí)行以下?mv?命令:

sh-4.4?#?mv?/dev/shm/source.txt?/home/qiya/testdir/dest.txt

然后看下目的文件信息:

sh-4.4?#?stat?dest.txt

File:?dest.txt

Size:?0?Blocks:?0?IO?Block:?4096?regular?empty?file

Device:?78h/120d?Inode:?3155414?Links:?1

Access:?(0644/-rw-r--r--)?Uid:?(?0/?root)?Gid:?(?0/?root)

對比有沒有發(fā)現(xiàn),inode?的信息是不一樣的,inode?number?是不一樣的(是不是跟上面同一文件系統(tǒng)下的?mv?現(xiàn)象不一致)什么原因呢?我下面一一道來,從原理出剖析。

當系統(tǒng)調用?rename?的時候,如果?源和?目的不在同一文件系統(tǒng)時,會報告?EXDEV?的錯誤碼,提示該調用不能跨文件系統(tǒng)。

#?defineEXDEV?18?/*?Cross-device?link?*/

所以,rename?是不能用于跨文件系統(tǒng)的,這個時候怎么辦?

劃重點:這個時候操作分成兩步走,先?copy?,后?remove?。

第一步:走不了?rename?,那么就退化成?copy?,也就是真正的拷貝。讀取源文件,寫入目標位置,生成一個全新的目標文件副本;

這里調用的?copy_reg?的函數(shù)封裝(要知道這個函數(shù)是?cp?命令的核心函數(shù),在?深度剖析?Linux?cp?的秘密有深入剖析過?);

ln,mv,cp?是在?coreutils?庫里的命令,公用函數(shù)本身就是可以復用的;

第二步:刪除源文件,使用?rm?函數(shù)刪除;

思考問題:mv?跨文件系統(tǒng)的時候,如果第一步成功了,第二步失敗了(比如沒有刪除權限)會怎么樣?

會導致垃圾。也就是說,目標處創(chuàng)建了一個新文件,源文件并沒有刪除。這個小實驗有興趣的可以試下。

cp?命令

cp?命令才是真正的數(shù)據(jù)拷貝命令,即拷貝元數(shù)據(jù),也會拷貝數(shù)據(jù)。cp?命令也是我之前花了萬字篇幅分析的命令,詳細可見:深度剖析?Linux?cp?的秘密。這里就不再贅述,下面提煉出關于拷貝的?3?種模式。

涉及到數(shù)據(jù)拷貝的,關鍵有個?--sparse?參數(shù),可以控制拷貝數(shù)據(jù)的?IO?次數(shù)。

1?auto?模式

重點:跳過文件空洞。是?cp?默認的模式

cp?src.txt?dest.txt

2?always?模式

重點:跳過文件空洞,還會跳過全?0?數(shù)據(jù),是空間最省的模式。

cp?--sparse=always?src.txt?dest.txt

3?never?模式

cp?--sparse=never?src.txt?dest.txt

復用之前畫的這?3?張圖,很形象的體現(xiàn)了?cp?的行為。

總結

目錄文件是一種特殊的文件,可以理解成存儲的是?dirent?列表。dirent?只是名字到?inode?的映射,這個是樹形結構的基礎;

常說目錄樹在內存中確實是一個樹的結構,每個節(jié)點由?dentry?結構體表示;

ln?-s?創(chuàng)建軟鏈接文件,軟鏈接文件是一個獨立的新文件,有一個新的?inode?,有新的?dentry,文件類型為?link,文件內容就是?一條指向源的路徑,所以?軟鏈的創(chuàng)建可以無視文件系統(tǒng),跨越山河;

ln?默認創(chuàng)建硬連接,硬鏈接文件只在目錄文件里添加了一個新?dirent?項?<新name:原inode>,文件?inode?還是和原文件同一個,?所以硬鏈接不能跨文件系統(tǒng)(因為不同的文件系統(tǒng)是獨立的一套?inode?管理方式,不同的文件系統(tǒng)實例對?inode?number?的解釋各有不同);

ln?命令貌似創(chuàng)建出了新文件,但其實不然,ln?只跟元數(shù)據(jù)相關,涉及到?dirent?的變動,?不涉及到數(shù)據(jù)的拷貝,起不到數(shù)據(jù)備份的目的;

mv?其實是調用?rename?調用,?在同一個文件系統(tǒng)中不涉及到數(shù)據(jù)拷貝,只涉及到元數(shù)據(jù)變更(?dirent?的增刪?),所以速度也很快。但如果?mv?的源和目的?在不同的文件系統(tǒng),那么就會退化成真正的?copy?,會涉及到數(shù)據(jù)拷貝,這個時候速度相對慢一些,慢成什么樣子?就跟?cp?命令一樣;

cp?命令才是真正的數(shù)據(jù)拷貝命令,速度可能相對慢一些,但是?cp?命令有?--spare?可以優(yōu)化拷貝速度,針對空洞和全?0?數(shù)據(jù),可以跳過,從而?針對稀疏文件可以節(jié)省大量磁盤?IO;

最后,小編給您推薦,金山毒霸“文檔修復大師”,支持.doc/.ppt/.xls等格式的文檔的修復,修復后再也不用為文檔的數(shù)據(jù)丟失而煩惱。