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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux+不同分区mv,mv操作深入浅出

發布時間:2024/8/5 linux 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux+不同分区mv,mv操作深入浅出 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

業務背景:存在兩個進程,進程A生成文件file,然后將其mv到一個新的文件fileA,進程B在需要的時候會取讀取文件fileA。如果在進程B讀取文件的時候,進程A在進行mv的操作,那么這個操作是否對進程B讀取文件生成影響呢?會產生什么結果?

如下圖所示:

在闡述該問題之前,首先講解下mv的操作和fopen()函數等對文件的操作的基本原理。那么首先需要初步的了解下Linux的文件系統中所涉及到的一些基礎知識,比如說Linux文件系統組件的體系結構,VFS, i節點,元數組等概念。

1.Linux文件系統體系結構

如圖1為Linux文件系統組件的體系結構:

其中用戶空間包含一些應用程序(例如,文件系統的使用者)和 GNU C 庫(glibc),它們為文件系統調用(打開、讀取、寫和關閉)提供用戶接口。系統調用接口的作用就像是交換器,它將系統調用從用戶空間發送到內核空間中的適當端點。

VFS 是底層文件系統的主要接口。這個組件導出一組接口,然后將它們抽象到各個文件系統,各個文件系統的行為可能差異很大。有兩個針對文件系統對象的緩存(inode 和 dentry)。它們緩存最近使用過的文件系統對象。文件系統實現(比如 ext2、JFS 等等)導出一組通用接口,供 VFS 使用。緩沖區緩存會緩存文件系統和相關塊設備之間的請求。例如,對底層設備驅動程序的讀寫請求會通過緩沖區緩存來傳遞。這就允許在其中緩存請求,減少訪問物理設備的次數,加快訪問速度(緩存的目的)。以最近使用(LRU)列表的形式管理緩沖區緩存。注意,可以使用 sync 命令將緩沖區緩存中的請求發送到存儲媒體(迫使所有未寫的數據發送到設備驅動程序,進而發送到存儲設備)。具體的信息可以參考IBM develop的《Linux 文件系統剖析》,鏈接為http://www.ibm.com/developerworks/cn/linux/l-linux-filesystem/

Linux文件系統下如何讀取文件:VFS采用了一組數據結構來描述文件系統,這些數據有超級塊、inode、dentry和數據塊。Linux版本較多,文件系統也不同,但是對于linux系統,其基本結構還是一致的,都會包含引導塊、超級快,目錄項i節點表、數據區等幾個部分。

1) 引導塊:位于文件卷最開始的第一扇區,這512字節是文件系統的引導代碼,為根文件系統所特有,其他文件系統這512字節為空。

2) 超級塊:位于文件系統第二扇區,緊跟引導塊之后,用于描述本文件系統的結構。如i節點長度、文件系統大小等。

3) 目錄項:Unix所有文件均存放于目錄中,目錄本身也是一個文件。目錄存放文件的機制如下: 首先,目錄文件本身也象普通文件一樣,占用一個索引節點; 其次,由這個索引節點得到目錄內容的存放位置; 再次,從其內容中取出一個個的文件名和它對應的節點號,從而訪問一個文件。

4) i節點:i節點表存放在超級塊之后,其長度是由超級塊中的s_isize字段決定的,其作用是用來描述文件的屬性、長度、屬主、屬組、數據塊表等

Linux會為每一個文件分配一個唯一的inode節點。而dentry是實現了文件名和inode編號的映射,當然還有其他的功能。在linux中,文件的文件名、文件屬性、文件內容是分別存儲的:文件名存放在目錄項(即dentry)中,文件屬性存放在inode中,文件內容存放在數據塊中。Linux在查找操作文件系統中的文件時,首先先讀取超級塊信息,找到文件名對應的inode,然后根據inode找到磁盤中的文件,進而根據inode中的信息來完成文件的各種操作。也就是說,Linux通過inode來尋找磁盤中的文件,而不是通過文件名來尋找的。這就是Linux操作文件時的一個大致過程,當然具體情況要比這復雜。

inode的結構:? inode 編號

? 用來識別文件類型,以及用于 stat C 函數的模式信息

? 文件的鏈接數目

? 屬主的 UID

? 屬主的組 ID (GID)

? 文件的大小

? 文件所使用的磁盤塊的實際數目

? 最近一次修改的時間

? 最近一次訪問的時間

? 最近一次更改的時間

需要注意的是:inode本身并不記錄文件名,而是記錄文件的相關的屬性(在上文提到過的那些屬性),文件名則記錄在目錄所屬的塊區域。正因為這個原因,使得如果Linux讀取一個文件的內容,就要先由根目錄/獲取該文件的上層目錄所在的inode,再由該目錄所記錄的的文件關聯性獲取該文件的inode,最后通過inode內提供的塊指針來獲取最終的文件內容。

可以看到i節點中包含了大多數于文件有關的信息:文件的類型,文件的訪問權限,文件所占用的數據塊的指針等。接下來我們可以認識下這個常聽說的文件的inode節點,并且闡述mv操作對文件的inode影響。

如圖2為磁盤、分區和文件系統的結構圖

通用文件模型由下列對象類型組成:

? 超級塊(superblock)對象: 存放系統中已安裝文件系統的有關信息。對于基于磁盤的文件系統,這類對象通常對應于存放在磁盤上的文件系統控制塊,也就是說,每個文件系統都有一個超級塊對象.

? 索引節點(inode)對象: 存放關于具體文件的一般信息。對于基于磁盤的文件系統,這類對象通常對應于存放在磁盤上的文件控制塊(FCB),也就是說,每個文件都有一個索引節點對象。每個索引節點對象都有一個索引節點號,這個號唯一地標識某個文件系統中的指定文件。

? 目錄項(dentry)對象: 存放目錄項與對應文件進行鏈接的信息。VFS把每個目錄看作一個由若干子目錄和文件組成的常規文件。例如,在查找 路徑名/tmp/test時 , 內核為 根目錄“/ ”創建一個目錄項對象, 為根目錄下的 tmp項創建一個第二級目錄項對象,為 /tmp 目錄下的test項創建一個第三級目錄項對象。

? 文件(file)對象: 存放打開文件與進程之間進行交互的有關信息。這類信息僅當進程訪問文件期間存在于內存中詳細信息可以參考《UNIX環境高級編程》的第四章《文件和目錄》和《深入分析Linux內核源碼》。

2. mv在同一個分區之內是執行的rename的操作,不會更改i節點的信息。

首先可以通過常用的命令,如ls -li,stat等命令來認識下inode節點。

2.1)首先可以通過,df命令來查看磁盤的分區,如下操作:

//用df -i來查一下磁盤空間

# df -i

Filesystem Inodes IUsed IFree IUse% Mounted on

/dev/sda1 7913472 126186 7787286 2% /

2.2)然后可以通過ls -li查看i節點的信息,如以下操作中會在第一列中顯示文件的inode的編號。

//ls -li來查一個文件的inumber

#ls -i /bin/ping

1032194 -rwsr-xr-x 1 root root 33272 Apr 14 2006 /bin/ping

2.3)進一步,可以通過stat來查文件的信息。該顯示的信息會比較多,其中stat結構中大多數的信息都是來自i節點,只有兩項數據是存放在目錄項當中:文件名和i節點的編號。

# stat /bin/ping

File: `/bin/ping'

Size: 33272 Blocks: 80 IO Block: 4096 regular file

Device: 801h/2049d Inode: 1032194 Links: 1

Access: (4755/-rwsr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)

Access: 2009-08-04 11:13:08.000000000 +0800

Modify: 2006-04-14 21:16:26.000000000 +0800

Change: 2009-07-01 14:04:44.000000000 +0800

2.4)mv對inode的影響:

如果mv命令的目標和源文件所在的分區相同:

1)使用新文件名建立目錄項(dentry),將新文件名稱對應到inode 編號;

2)解除與舊目錄項的鏈接;

需要注意的是:該操作對inode表沒有影響(除時間戳),對數據的位置也沒有影響,不移動任何數據。只需要構造一個現有i節點的新目錄項,并解除和舊目錄項的鏈接。在實踐中也能得知:不論文件有多大,執行mv的時間都是“瞬間”完成。

2.5)查看到i節點的信息并未改變:

通過ls –li或者stat filename來查看具體的信息,如實際的例子:如下圖3所示,通過mv操作之后,并不會改變i節點的信息。

2.6)通過strace mv a b來追蹤mv的實現機制。如下圖4所示,其中該例子中,原有的a,b文件都是存在。

2.7)strace mv a b,如下圖5所示,其中b文件是不存在的。

從上述圖中可以得出,mv操作并不改變i節點的編號,并且其實現是通過rename的機制來實現的。

2.8) 通過mv的源碼來查看其信息。可以查看(coreutils-8.9)的源碼。

mv操作是針對cp_options這個結構體,其中該結構體中的move_mode決定了方式。即判斷是否在一個分區內,如下示意圖為調用的信息。

在mv.c中movefile->do_move->copy,在movefile中傳入了cp_options的結構體,并通過move_mode來決定是進行rename還是read和write的方式。

結論:由以上對inode的實踐,并通過strace來追蹤mv的實現機制、mv的源碼,可以得出在同一個分區中,mv實際進行的是rename操作。接下來本文將講述rename的實現。

3. rename是一種原子操作

“Rename是一種原子操作”,如果要將這個問題講述清楚,則需要講到的是“Linux文件系統中元數據的加鎖機制與組織方式”。

在Linux系統中,需要對元數據進行加鎖,元數據操作是一種事務操作,需要滿足原子性,一致性,獨立性和持久性。為了解決這種元數據操作帶來的一致性的問題和多個元數據操作的交互和重疊的問題,采用加鎖的方式。

如:操作1,在目錄a下創建了b, 遞增目錄 a 的 nlink 值,操作2刪除目錄a,如果并發的進行就會出問題,但是如果通過加鎖的方式,先對a進行加鎖,再解鎖,就是一種串行的執行,則不會出現問題。

如圖6元數據操作的死鎖所示:如果此時系統中沒有相應的加鎖機制對元數據操作進行互斥,那么當操作①創建了對象 b 以后,接下來操作②有可能就將目錄 a 刪除了,當操作①要遞增目錄 a 的 nlink 值時,就會發現沒有可操作的對象了,于是操作出錯。

加鎖能夠帶來一定的優勢,但是會引發新的問題:即死鎖的問題。需要注明的一個問題是元數據的加鎖操作基本上都是由其虛擬文件系統(VFS)來規定的,這樣做的好處是可以統一管理所有元數據操作的加鎖機制,底層的具體文件系統可以不理會這些問題,只需要按照 VFS 的調用來執行對元數據的操作。

為了防止死鎖的問題:Linux也做了一些規定:即在VFS中,對于大多數的元數據操作,可以通過制定統一的加鎖順序來避免死鎖的發生。這個順序是:先對父目錄加鎖,再對要操作的對象(目錄或文件)加鎖。但是rename和link是除外的。如圖7所示,仍然不能夠解決mv操作的死鎖問題。

為了解決這個問題:Linux規定同一時間只能有一個rename的操作,當然這個是針對同一個文件系統內的,該實現機制也是通過加鎖來實現的。具體信息可以參考IBM develop上的《Linux文件系統中元數據的加鎖機制與組織方式》,鏈接為:http://www.ibm.com/developerworks/cn/linux/l-cn-fsmeta/

繼續看mv的操作實現原理,如圖8所示,解釋剛才出現的問題:

總結mv操作的實現過程:

1) 如果通過stat返回與此命名文件有關的信息結構,lstat函數類似于stat,但是當命名的文件是一個符號鏈接時,lstat返回該符號鏈接的有關信息,而不是由該符號鏈接引用文件的信息。

2) 通過access文件b(W_OK),查看文件b是否有寫入的權限。對于access,它會檢查是否可以讀/寫某一已存在的文件。參數mode有幾種情況組合, R_OK,W_OK,X_OK 和F_OK。R_OK,W_OK與X_OK用來檢查文件是否具有讀取、寫入和執行的權限。由于access只作權限的核查,并不理會文件形態或文件內容。

3) 進行rename的重命名操作。

注意:rename操作可以根據所操作的為文件還是目錄項,分為幾種情況,由于本文的業務背景,目前只是討論所操作的為文件的情況,所以不將詳細闡述,具體的可以參考《UNIX環境高級編程》中第四章的內容,對于rename操作,其原型為:

int rename(const char * oldname,const char * newname)

需要說明的是:如果newname已經存在,且為文件的,則先將該目錄項刪除,然后將oldname更名為newname,若newname不存在,則只需要進行更名即可。

4. fopen()和fclose()操作

在Linux中,進程是通過文件描述符(file descriptors,簡稱fd)而不是文件名來訪問文件的,文件描述符實際上是一個整數。文件名的信息是存在文件的目錄項當中。其中文件位置是比較重要的一個參數,每個文件都有一個32位的數字來表示下一個讀寫的字節位置,根據這個位置來決定了文件的寫入和讀出。為了描述打開的位置,Linux中專門用了一個數據結構file來保存打開文件的文件位置,這個結構稱為打開的文件描述(open file description)。這個數據結構的與進程的聯系非常緊密。

file結構中主要保存了文件位置,此外,還把指向該文件索引節點的指針也放在其中。

file結構在include\linux\fs.h中定義如下:

struct file

{

struct list_head f_list; /*所有打開的文件形成一個鏈表*/

struct dentry *f_dentry; /*指向相關目錄項的指針*/

struct vfsmount *f_vfsmnt; /*指向VFS安裝點的指針*/

struct file_operations *f_op; /*指向文件操作表的指針*/

mode_t f_mode; /*文件的打開模式*/

loff_t f_pos; /*文件的當前位置*/

unsigned short f_flags; /*打開文件時所指定的標志*/

unsigned short f_count; /*使用該結構的進程數*/

unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin;

/*預讀標志、要預讀的最多頁面數、上次預讀后的文件指針、預讀的字節數以及

預讀的頁面數*/

int f_owner; /* 通過信號進行異步I/O數據的傳送*/

unsigned int f_uid, f_gid; /*用戶的UID和GID*/

int f_error; /*網絡寫操作的錯誤碼*/

unsigned long f_version; /*版本號*/

void *private_data; /* tty驅動程序所需 */

};

fopen在打開文件時是根據文件名,之后的fread,fwrite等操作都是根據文件描述符進行的。Linux中對文件的加鎖,一般有flock(),fcntl()等機制,單純的fopen和fread和fwrite是不會對文件進行加解鎖的。fclose函數關閉了文件的流,實際是進行了一個寫文件的操作。 文件內容寫入失敗,這時fclose函數就會出錯,如果寫權限不存在或者是超過了大小才會失敗,所以一般情況下,fclose失敗的概率也是很小的。而在調用fclose關閉一個打開的流,在文件被關閉之前,沖洗緩沖區中的輸出的數據。丟棄緩沖區中的任何輸入數據,如果標準I/O庫已經為該流自動分配了一個緩沖區,則釋放此緩沖區。

5.擴展:在業務中存在的各種操作的結果現象以分析

通過實驗查看一下四種情況,該實現是基于進程見的。

操作1

1) 進程A:fopen fileA, sleep 10

2) 進程B:mv fileB fileA

3) 進程A:fclose fileA

結果:在mv完成之后,立即可以看到fileA中的文件已經變為了fileB,fclose成功。

操作2

1)進程A:fopen fileA, sleep 10

2)進程B:mv fileA fileB

3)進程A:fclose fileA

結果:在mv完成之后,fileA文件已不存在,僅有fileB文件,且fileB文件的內容為原來fileA文件的內容。關閉文件fclose成功。

操作3

1)進程A:fopen fileA, 通過fwrite寫入到文件的一行之后進行sleep

2)進程B:mv fileB fileA

3)進程A:fclose fileA

結果:在mv完成之后,可以立即看到fileA中的內容變為了fileB,而且當寫文件操作完成之后,也可以看到fileA中文件仍然為fileB,且關閉文件fileB依然是成功的。

原因: mv僅僅是rename操作。fopen寫入的是磁盤位置,所以雖然mv使得文件名變了但是磁盤位置并沒有變還是可以寫入并關閉正常。在mv fileB fileA之后。fopen還是打開的原來的fileA文件(已經不能被文件系統索引到)的磁盤位置,寫入操作也還是在那塊進行,fclose由于是可以寫入文件就是成功故其返回值也是正確的。但在這之后查看fileA文件實際上是查看的原來的fileB文件,而并不是原來的fileA文件。

以上描述的原因可以參考下圖對于mv的解釋

操作4

1)fopen fileA, 寫入到文件的一行后,sleep

2)mv fileA fileB

3)fclose fileA

結果:當mv完成之后,可以看到文件fileA已經不存在,當fclose之后,可以看到fileB當中的內容為寫入的內容。

原因: fopen()和fclose()都是對磁盤空間做操作的,并不記錄文件名。Mv僅是rename操作,并沒有對磁盤做實際意義的操作。問題點:fopen(), fwrite(), fclose()都是帶緩存,比如在執行文件讀操作的時候,從磁盤文件將數據先讀入內存“緩沖區”,裝滿之后再從內存“緩沖區”依次讀入接收的變量。執行文件寫操作的時候,先將數據寫入到內存的“緩沖區”中。待緩沖區裝滿之后再寫入到文件中。

于是對上述的3,4實驗,希望每次寫入文件時候,及時的刷新到磁盤上,并重新進行了實驗。沒寫入一行進行fflush(fp)進行刷新。發現結果和上述一致,不同之處是在于由于刷新之后會及時將文件的內容寫入到文件中,可以看到在寫的過程當中文件的大小是逐漸遞增的。而不帶緩沖的時候,知道flose執行完畢才將寫入的內容一次性寫入到磁盤當中。

著,做了如下的實驗,采用open()和write()等unix標準的函數進行。

區別:fopen()和open()等函數區別,fopen等是ANSIC標準中的C語言庫函數,在不同的系統中調用不同的內核API, open(),close(),write(),read()是標準的UNIX函數,參數不是指向文件的指針,而是指向文件的句柄,并且是不帶緩沖的。因此以上的實驗將fopen換成open,fclose換成close,在實驗中可以看到,在通過write寫入的時候,可以看到文件的大小是遞增的,說明是直接寫入到磁盤空間上的。

重新嘗試上述的3,4實驗,將fwrite()改為write(),fclose()改為close()。

操作3.1

1)open fileA, 寫入到文件的一行之后進行sleep

2)mv fileB fileA

3)close fileA

結果:執行完mv fileB fileA之后,文件fileA的內容立即為fileB文件的內容,而且close文件也是成功的。

操作4.2

1)open fileA, 寫入到文件的一行之后進行sleep

2)mv fileA fileB

3)close fileA

結果:執行完mv fileB fileA之后,由于A中已經有了部分的內容,可以看到fileB中文件的內容成為fileA文件的內容,文件關閉也是成功的。

由上述的實驗當中可以看到:是否帶緩沖的機制不會影響上述的結果。

原因:緩沖機制的作用是提高IO的讀寫速度,但是fopen之后文件的寫入還是根據i節點的位置來實現的,故還是會產生如上的結果。

6.結論

就之前提到的業務情況,進程B在讀取文件的時候,進程A在進行mv的操作,那么這個操作不會對進程B讀取文件生成影響。由于讀取文件都是根據文件名操作,并且mv操作是原子操作,故每次mv之后,都讀得是“新的”文件。

總之,分析此類問題的關鍵在于理解mv和fopen操作的區別和本質。mv是對文件名操作的,文件名屬性記錄在目錄項中。fopen雖然打開是關聯文件名的,單在打開之后是對真正的磁盤做操作,和文件名無關。

作者:zjchao

總結

以上是生活随笔為你收集整理的linux+不同分区mv,mv操作深入浅出的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。