linux系統下文件的刪除機制

不少人曾經說過,linux下的病毒沒有windows多的緣由在於linux使用的人很少,人們不屑於開發linux下的病毒,看到這個言論我笑了,想必不少人都有過explorer被注入的經歷吧,explorer被替換而後怎麼也刪除不掉,或者一個system32下的一個頑固文件怎麼也刪除不掉,更不幸的是,它就是一個木馬,此時,網上流行的一些工具就顯得有用了,好比icesword,這些工具不得不動用內核驅動來靠內核的超高特權來刪除那些文件,windows的註冊表也是一個難纏的東西,一旦註冊表被篡改,後果將不堪設想,所以註冊表備份工具就顯得必要,這一切的關鍵在於windows內核的架構,windows的啓動過程很是的複雜,不只是內核的啓動,更有不少的用戶空間的服務是用戶所不能控制的,好比winlogon進程,在用戶能夠接手系統以前,不少的用戶空間服務已經啓動了,即使是安全模式也是這樣,既然用戶空間的東西能夠在用戶接手系統以前啓動,那麼若是這些是惡意程序的話,你的系統將乖乖的隨黑客的心而動,恐怖吧,十分恐怖!windows的文件映射是一個十分使人又愛又恨的機制,它可使你的重要文件在被訪問期間不能被刪除,固然也可使病毒被使用期間不能被刪除...這一切都是由於windows將文件的刪除和文件保護以及系統安全統一在了一塊兒,這是它微內核的架構所決定的,windows老是盡本身最大的努力保持系統的完整,而每每結構卻拔苗助長,windows做爲一個商品,它不但願初級用戶因爲本身的誤操做刪除了一個系統文件形成系統不能啓動而後把過錯歸於微軟,它須要考慮的問題太多了。linux呢,偏偏不是這樣,它對於文件的保護採用了懶惰的機制,而且將最大的自由交給用戶,它不阻止你刪除任何文件,由於系統的根本歸根結底都是磁盤的文件,因此它甚至容許你刪除內核自己,結果就是下次不能再啓動了,刪除了內核自己難道還能繼續運行下去,是的,能夠,這就是linux的vfs對文件刪除管理的懶惰機制,若是有人在用該文件,那麼系統是不會刪除它的,另一個緣由就是linux內核是大內核,而且所有映射到物理內存,對於內核而言沒有分頁內存的概念,即使磁盤的文件沒有了,只要內存中還有就不會出現問題。對於文件的刪除操做,不論是什麼文件一概平等對待,linux系統看到的只是一個引用計數,一旦引用計數爲0了,就要刪除該文件了,若是有人使用着該文件,那麼它的引用計數最起碼是1,最起碼也要等到該使用者再也不使用的時候才能夠刪除該文件,這樣的結果就是當你刪除一個文件的時候,你放心的進行刪除操做,系統等到沒有用該文件的時候執行最終的刪除,所以在linux中沒有刪除不了的文件,linux不對文件的刪除處理提供任何的保護,不耦合任何別的機制,刪除的語義很簡單,就是遞減一個引用計數。node

另一點就是,linux的內核和用戶空間分得很清晰,用戶甚至能夠在啓動時定義本身的init=XXX參數使得用戶空間的第一個進程是本身定義的,這種內核空間和內核空間的不耦合是十分重要的,內核在init內核線程中經過execve一個用戶進程讓用戶接手系統,這個進程是能夠本身定義的,不過通常是/sbin/init進程,這樣的結果就是即便用戶空間所有被注入了,那麼你第一,能夠刪除這些骯髒的文件;第二,能夠設置一個你本身定義的乾淨的init進程,須要作的就是從新啓動一下系統,一切就搞定了,linux中強大shell命令使得你能夠很簡單的備份一份乾淨的無病毒的根文件系統,所以在linux下殺毒將是一件很簡單的事情。用戶能夠自主控制用戶空間的第一個進程是這裏的要點,在windows下這是很難的,你想替換smss程序,試試看,系統會提示你「請肯定磁盤未滿或未被寫保護並且文件未被使用」,而且system32下的dllcache也是一個讓你又愛又恨的目錄,不信的話,請手動刪除一下IE試試看。linux

要點就是不要試圖保護文件不被刪除,操做系統的用戶空間應該提供系統保護機制而不是讓文件系統承擔這個責任,無論那個文件有多重要,系統也不要提供保護它不被刪除的機制,特別是內核更不能,由於若是良民能夠經過這種方式獲得保護,那麼強盜也能夠。shell

linux中經過兩個引用計數管理了文件的存在與刪除,這就是i_count以及i_nlink,前者的意義是當前的使用者,後者的意義是當前的介質連接數量,這兩者的意義也能夠理解成前者是文件在內存的引用計數,然後者是文件在磁盤的引用計數,只有在兩者都爲0的狀況下,介質的刪除操做才真正進行,也能夠說,i_nlink是文件被刪除的充分條件,而i_count是文件被刪除的必要條件,最後我會說明,若不是爲了別的管理須要,用一個引用計數就夠了而沒有必要用兩個。在刪除一個文件的時候,最終要調用iput,咱們看一下這個函數:windows

void iput(struct inode *inode)安全

{數據結構

if (inode) {架構

struct super_operations *op = inode->i_sb->s_op;函數

if (op && op->put_inode)工具

op->put_inode(inode);atom

if (atomic_dec_and_lock(&inode->i_count, &inode_lock)) //文件的內存的引用計數爲0的話才進行最終的刪除

iput_final(inode);

}

}

iput_final中主要調用一個generic_drop_inode,緊接着看一下這個generic_drop_inode:

static void generic_drop_inode(struct inode *inode)

{

if (!inode->i_nlink)

generic_delete_inode(inode);

else

generic_forget_inode(inode);

}

能夠看出,內存引用計數爲0是一個前提條件,畢竟內存引用計數表明着進程對文件的訪問,只有在沒有進程對該文件進行訪問的狀況下才能夠權衡接下來的刪除操做:

void generic_delete_inode(struct inode *inode)

{

struct super_operations *op = inode->i_sb->s_op;

list_del_init(&inode->i_list);

inode->i_state|=I_FREEING;

inodes_stat.nr_inodes--;

spin_unlock(&inode_lock);

if (inode->i_data.nrpages)

truncate_inode_pages(&inode->i_data, 0);

security_inode_delete(inode);

if (op->delete_inode) {

void (*delete)(struct inode *) = op->delete_inode;

if (!is_bad_inode(inode))

DQUOT_INIT(inode);

delete(inode); //這個文件系統相關的delete回調函數最終銷燬了磁盤的inode

} else

clear_inode(inode);

spin_lock(&inode_lock);

hlist_del_init(&inode->i_hash);

spin_unlock(&inode_lock);

wake_up_inode(inode);

if (inode->i_state != I_CLEAR)

BUG();

destroy_inode(inode);

}

以上函數能夠看出,銷燬介質inode的操做是文件系統的超級塊操做的一個回調函數,這是合理的,由於文件系統的超級塊就是管理全局的,全部常規文件的塊分配,空閒塊管理都要由它來進行,所以這理應是它的職責,具體例子就是有些文件系統是僞文件系統,並且還假裝成塊文件,好比內存文件系統,它根本就沒有非易失介質,所以也就沒有必要提供真實的delete回調函數了。總之,一個文件系統是什麼樣子,應該怎樣進行管理,它的超級塊最清楚。linux的vfs實際上是一幅很美麗的圖畫,不一樣的文件系統有着不一樣的行爲,vfs核心負責協調它們使它們更好的合做,已到達最終系統的安全,穩定和高效。

刪除文件的操做只有一個就是unlink,僅僅從名字上看看不出它是刪除的意思,實際上linux中任何的刪除文件的操做最終都要歸結到這個unlink,這個系統調用的實質就是遞減文件磁盤的引用計數,也就是遞減i_nlink計數器,按照道理來講文件的刪除和文件解除連接有着不一樣的意義,可是它們卻統一到了一個一致的系統調用,這兩者是從不一樣角度來說的同一個意思,刪除是從文件自己來講的,而解除連接是從用戶角度來說的,如今就看一下這個被兩個語義一塊兒使用的系統調用:

asmlinkage long sys_unlink(const char __user * pathname)

{

...

if (!IS_ERR(dentry)) {

if (nd.last.name[nd.last.len])

goto slashes;

inode = dentry->d_inode;

if (inode)

atomic_inc(&inode->i_count);

error = vfs_unlink(nd.dentry->d_inode, dentry); //調用具體文件系統的unlink函數

exit2:

dput(dentry);

}

up(&nd.dentry->d_inode->i_sem);

if (inode)

iput(inode);

...

}

static int ext2_unlink(struct inode * dir, struct dentry *dentry)

{

struct inode * inode = dentry->d_inode;

...

ext2_dec_count(inode); //遞減了該inode的i_nlink的引用計數

err = 0;

out:

return err;

}

剛纔說了其實用一個引用計數就能夠搞定一切,此話怎講呢?這裏姑且不討論太複雜的inode問題,僅以文件討論,咱們把文件的初始引用計數設置爲1,一旦打開一個文件就遞增引用計數,一旦關閉就遞減之,只有在unlink的狀況才遞增一次,遞減兩次,總的遞減一次,這裏也不討論文件映射,僅覺得凡是操做文件必須是先打開再訪問,這樣的話,若是一個進程訪問一個文件後關閉之,它遞減的該文件的引用計數僅僅是它爲了訪問該文件open時遞增的那一次,和打開前的引用計數相等,不管哪種狀況,只要該文件的引用計數爲0則刪除之,這種機制簡單的闡明瞭linux中文件刪除的懶惰機制,正是這種機制使得linux下的病毒無處藏匿。若是僅僅管理文件刪除,那麼一個引用計數固然就夠了,可是unix/linux通常不會讓一個實體身兼數職的,若是現實中須要別的意義,那麼就會抽象出一個新的新的數據結構用於此新意義,在文件的管理中,i_nlink的出現就是爲了管理文件的硬連接的。

相關文章
相關標籤/搜索