關於linux目錄刪除的問題?
在~/dir目錄下,用$ rm -r ~/dir刪除目錄,用$ pwd命令顯示還是在~/dir下,但是$ cd ..後$ cd dir會顯示dir目錄不存在,既然不存在,那為什麼剛刪除的時候還會顯示當前目錄是dir呢?
Linux刪除文件的時候,如果文件正在被使用,系統只會刪除文件標識,磁碟數據會等待文件被釋放時再被刪除。目錄文件也是文件。
這個問題有點意思,前面有幾個答主反對vczh的答案,而我經過一些實驗以及查看代碼,我覺得雙方其實都沒有錯。
實驗代碼如下:
$ mkdir test cd test
$ pwd
/tmp/test
$ /bin/pwd
/tmp/test
$ /usr/bin/stat .
File: "."
Size: 4096 Blocks: 8 IO Block: 4096 directory
Device: fe01h/65025d Inode: 136752 Links: 2
Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2017-04-11 09:21:19.032054838 +0000
Modify: 2017-04-11 09:21:19.032054838 +0000
Change: 2017-04-11 09:21:19.032054838 +0000
Birth: -
$ rm -rf ../test
$ pwd
/tmp/test
$ /bin/pwd
/bin/pwd: couldn"t find directory entry in ".." with matching i-node
$ /usr/bin/stat .
File: "."
Size: 0 Blocks: 8 IO Block: 4096 directory
Device: fe01h/65025d Inode: 136752 Links: 0
Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2017-04-11 09:21:49.896384217 +0000
Modify: 2017-04-11 09:21:19.032054838 +0000
Change: 2017-04-11 09:21:49.896384217 +0000
Birth: -
這個實驗中要注意, bash 有內建的 pwd 命令,而系統里還有一個外部的 /bin/pwd 程序。stat 是一個外部程序。
下面分別對 內建pwd、外部/bin/pwd、外部/usr/bin/stat 這三個命令在刪除目錄前後的現象進行解釋。
內建pwd
通過閱讀bash的代碼,bash程序中有一個全局變數 the_current_working_directory (代碼:http://git.savannah.gnu.org/cgit/bash.git/tree/builtins/common.c#n564 ),每次執行 cd 之後,都會更新這個變數,而內建pwd命令則是直接輸出這個變數的值(代碼:cd.defuiltins - bash.git - bash 代碼比較長,簡化後就是,只要 the_current_working_directory 不為空,就直接輸出)。因此,從這個角度看,vczh的結論是對的,wd只是一個字元串。
外部pwd
外部pwd 沒有這個全局變數,所以需要自己去分析,起點是自己的 cwd (/proc/self/cwd,從父進程繼承),而分析的過程中會檢查路徑的每一級是否是符號鏈接,因此需要從父目錄中訪問自己,而此時父目錄中已經沒有test這個目錄了,因此就報錯了。(但是我們可以自己寫一個程序,直接輸出 readlink /proc/self/cwd 的結果,是不會報錯的)
外部stat
首先注意一下刪除目錄前後stat的輸出,改變的有兩項,Size變了(之前是4096,之後為0),Links變了(之前是2,之後是0)。尤其注意,Inode 號在刪除前後都沒變。說明,此時這個目錄的 inode 仍然未被釋放(這個目錄的訪問時間、許可權位、屬主等信息都存在inode中)。其實,除了stat外,其他外部程序訪問 . 都正常(當然,嘗試在這個目錄中創建文件的行為會失敗)。比如:
$ /usr/bin/touch .
$ stat .
File: "."
Size: 0 Blocks: 8 IO Block: 4096 directory
Device: fe01h/65025d Inode: 136752 Links: 0
Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2017-04-11 09:44:40.646831148 +0000
Modify: 2017-04-11 09:44:40.646831148 +0000
Change: 2017-04-11 09:44:40.646831148 +0000
Birth: -
注意觀察,touch 這個目錄後,這個目錄的訪問時間被更新了,這就證明了這個目錄的 inode 是存在的,還沒有被釋放。所以反對vczh的答案也沒有錯誤。
vczh 的描述,wd 確實只是一個全局字元串變數,這是沒有問題的。其他答主回答的是Linux刪除文件的機制,即一個inode只有當所有引用都釋放後才會被真正刪除,這本身沒有錯誤,不過就題主的問題中描述到的操作而言,並沒有涉及到對cwd的inode的訪問,僅僅涉及到列印 the_current_working_directory 這個全局變數的值。
--------------------------------------------------------------------
更新:今天查看了一下Linux內核源碼,確認了一下 cwd 這個目錄inode的引用計數是在 chdir() 這個系統調用中增加的,在 chdir() 到其他路徑時,舊目錄的inode的計數會減少。
每個進程(task_struct)都有自己的 fs_struct (https://github.com/torvalds/linux/blob/v4.10/include/linux/sched.h#L1719),這個進程的pwd、pwdmount 是記錄在這個 fs_struct 中的。
chdir() 系統調用的代碼在:https://github.com/torvalds/linux/blob/v4.10/fs/open.c#L435-L459 其中調用了 set_fs_pwd() 這個函數,這個函數的代碼在 https://github.com/torvalds/linux/blob/v4.10/fs/fs_struct.c#L32-L46 ,這裡面對新目錄調用了 path_get(),對舊目錄調用了 path_put() ,path_get() 用於增加引用計數,path_put() 用於減少引用計數。path_get() 的代碼在:https://github.com/torvalds/linux/blob/v4.10/fs/namei.c#L479-L490 , path_put() 的代碼在:https://github.com/torvalds/linux/blob/v4.10/fs/namei.c#L492-L503 。
從代碼中也可以看出,chdir() 除了會增加對當前目錄(pwd)的引用,還會增加對當前掛載(pwdmount)的引用。當需要umount 一個掛載時,會檢查這個掛載的引用計數,所以開一個bash,cd到某個掛載的路徑下,然後嘗試umount這個掛載時,會報錯 target is busy。
因為你shell正在用這個文件,所以沒刪,你不用了以後就刪了
*nix的這個設定和windows是不一樣的,windows是不能刪除/修改被鎖的文件,而linux在刪除/修改被鎖文件時會單獨為鎖定這個文件的進程保留這個之前版本文件,直到鎖定解除。輪子哥的說法基本就是錯的。請看代碼。
# mkdir t1
# cd t1
# fuser ./
./: 522980c
# cd ..
# fuser ./t1
# echo 上一個命令無輸出。
可以看到shell進入到目錄,確實是佔用了目錄。還有另一個有意思的範例。
# cd t1
# touch f1
# ls
f1
# rm -rf ../t1
# ls
# echo 上一條命令無輸出^C
# fuser ./
./: 522980c
但是,你也只能用`./`訪問當前目錄了,包括`ls`在內的其他命令,用絕對路徑訪問已經不可行。
# fuser `pwd`
無法分析 /root/t1: 沒有那個文件或目錄
不僅是目錄,你也可以在DE下面試試用播放器打開一個視頻文件,然後刪除視頻文件,文件可以刪除,同時播放器不會受到任何影響可以完整播放完。
其實只是Linux上面主流文件系統都是這麼實現的而已。
cwd是環境變數,環境變數是不會因為你在其他進程里幹了什麼而改變的
證明wd只是個字元串而不是個系統資源唄。你要是在windows下干這事,你會發現你怎樣都刪不掉這個文件夾,直到你cd走為止。用git checkout的時候就經常會忘記cd走,然後傻逼(逃
都沒說到點上。。你刪除的是filename.但是系統是通過filename連到inode上的。所以刪除filename,inode暫時還未釋放。文件還可以使用,但是你無法通過filename 找到inode了。【實際上刪除的是filename 到 inode 的映射。】一個文件被打開後,系統是根據iNode來識別文件的。
我感覺跟緩存機制有關,我試過刪除一個正在使用的文件,居然沒問題
推薦閱讀:
※Linux 伺服器有必要開啟 iptables 防火牆么?
※Linux文件系統的核心結構?
※為什麼把部分蘋果設備的系統時間調整為1970.1.1,重啟後就會變磚?
※QT5 中的.pro 文件中為何要加入QT += widgets,而不能在包頭文件的時候就包一個widgets/QApplication呢?
※linux下如何刪除文件夾而不刪除文件夾里的內容?
TAG:Linux |