(1)軟連接配接可以 跨檔案系統 ,硬連接配接不可以 。實踐的方法就是用共享檔案把windows下的 aa.txt文本文檔連接配接到linux下/root目錄 下 bb,cc . ln -s aa.txt /root/bb 連接配接成功 。ln aa.txt /root/bb 失敗 。
(2)關于 i節點的問題 。硬連接配接不管有多少個,都指向的是同一個i節點,會把結點連接配接數增加 ,隻要結點的連接配接數不是 0,檔案就一直存在 ,不管你删除的是源檔案還是連接配接的檔案 。隻要有一個存在 ,檔案就存在 (其實也不分什麼源檔案連接配接檔案的 ,因為他們指向都是同一個 i節點)。 當你修改源檔案或者連接配接檔案任何一個的時候 ,其他的 檔案都會做同步的修改 。軟連結不直接使用i節點号作為檔案指針,而是使用檔案路徑名作為指針。是以 删除連接配接檔案對源檔案無影響,但是 删除 源檔案,連接配接檔案就會找不到要指向的檔案 。軟連結有自己的inode,并在磁盤上有一小片空間存放路徑名.
(3)軟連接配接可以對一個不存在的檔案名進行連接配接 。(看到了,上面的那個閃爍的圖示)
(4)軟連接配接可以對目錄進行連接配接。而硬連結不能.
(5)硬連結和軟連接配接可類比c++中的引用和指針的差別,前者相當于多了一個名字,直接通路,後者是間接通路,本身也占用空間(指針空間,inode節點);
概述:
在linux系統中有一種比較特殊的檔案,我們稱之為連結(link),通俗地說,連結就是從一個檔案指向另外一個檔案的路徑。linux中連結分為倆種,硬連結和軟連結。簡單來說,硬連結相當于源檔案和連結檔案在磁盤和記憶體中共享一個inode,是以,連結檔案和源檔案有不同的dentry,是以,這個特性決定了硬連結無法跨越檔案系統,而且我們無法為目錄建立硬連結。軟連結和硬連結不同,首先軟連結可以跨越檔案系統,其次,連結檔案和源檔案有着不同的inode和dentry,是以,兩個檔案的屬性和内容也截然不同,軟連結檔案的檔案内容是源檔案的檔案名。
看完前面的關于硬連結和軟連結的介紹以後,接下來我們仔細考究下linux核心中對硬連結和軟連結的實作。
使用strace工具,可以發現建立硬連結調用的函數是link(),該函數的核心入口為syscall_define2(),其實就是sys_link()。我們就從這個入口開始一步步跟蹤sys_link()的實作原理。
syscall_define2(link, const char __user *, oldname, const char __user *, newname)
{
return sys_linkat(at_fdcwd, oldname, at_fdcwd, newname, 0);
}
sys_link()其實調用了函數sys_linkat(at_fdcwd, oldname, at_fdcwd, newname, 0)。而sys_linkat()的源代碼如下(隻保留最主要代碼):
syscall_define5(linkat, int, olddfd, const char __user *, oldname,
int, newdfd, const char __user *, newname, int, flags)
struct dentry *new_dentry;
struct nameidata nd;
struct path old_path;
int error;
char *to;
//1.這個是用來查找目的連結名的父目錄的dentry
error = user_path_parent(newdfd, newname, &nd, &to);
//2.如果源和目的不是同一個檔案系統,則傳回錯誤
if (old_path.mnt != nd.path.mnt)
goto out_release;
//3.為連結檔案建立一個dentry結構
new_dentry = lookup_create(&nd, 0);
error = mnt_want_write(nd.path.mnt);
error = security_path_link(old_path.dentry, &nd.path, new_dentry);
error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry);
其實,我們仔細思考+上面的圖示可以明白,建立硬連結所做的事情主要包含:為連結檔案建立一個dentry,初始化(主要是指初始化建立dentry的inode号);将連結檔案的dentry寫入父目錄的資料塊中。是以,上面的代碼頁就顯得一目了然,代碼主要做的事情有:
1. 合法性檢查,前面我們說硬連結不可跨越檔案系統,這是因為連結檔案和源檔案共用一個inode,而inode号在同一個檔案系統内才有意義;
2. 擷取連結檔案父目錄的inode結構;
3. 為連結檔案建立一個dentry結構;
4. 等到一切準備工作就緒以後,初始化連結檔案dentry結構中的inode号,并添加到父目錄的資料塊中。
上述步驟中的1,2,3在上面的函數中均有對應,而4的主要工作則是在vfs_link()中進行,其傳入的實參的意義也在代碼中作了較為詳細的說明,vfs_link()的實作如下(簡化代碼):
int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{ //該函數的第一個參數是原始檔案的dentry,第二個參數原始檔案所在目錄的inode,第三個參數是新連結檔案的dentry(包含硬連結名字)
struct inode *inode = old_dentry->d_inode; //inode指向原始檔案的inode
//檢查是否有建立檔案目錄項權限
error = may_create(dir, new_dentry);
if (error)
return error;
//inode中的i_sb指向所在檔案系統的超級塊對象
if (dir->i_sb != inode->i_sb)
return -exdev;
//是否實作了具體檔案系統的link,如ext3_link()
if (!dir->i_op->link)
return -eperm;
if (s_isdir(inode->i_mode)) //如果原始檔案是目錄則出錯(是以不能建立目錄的硬連結)
error = security_inode_link(old_dentry, dir, new_dentry);
//調用具體檔案系統的link,如ext3_link()
error = dir->i_op->link(old_dentry, dir, new_dentry);
if (!error)
fsnotify_link(dir, inode, new_dentry);
return error;
vfs_link()中主要完成一些參數檢查的任務,最終調用的是具體檔案系統的link方法,如ext3檔案系統的ext3_link()。
static int ext3_link (struct dentry * old_dentry,
struct inode * dir, struct dentry *dentry)
{ //第一個參數是原始檔案的dentry,第二個參數原始檔案所在目錄的inode,第三個參數是新連結檔案的dentry(包含硬連結名字),對應inode對象中的link方法
handle_t *handle;
int err, retries = 0;
//如果檔案上的連結數過多,傳回too many links
if (inode->i_nlink >= ext3_link_max)
return -emlink;
dquot_initialize(dir);
retry:
handle = ext3_journal_start(dir, ext3_data_trans_blocks(dir->i_sb) +
ext3_index_extra_trans_blocks);
inode->i_ctime = current_time_sec;
//将源檔案inode上的連結數 + 1
inc_nlink(inode);
atomic_inc(&inode->i_count); //原子操作
//将連結檔案的dentry寫入到其父目錄的資料塊中*********
/*之前一直有一個疑問,硬連結的建立就是dentry的建立,而dentry是一個表示關系的動态對象,在硬碟上沒有對應資料,那麼當系統重新開機時候已經建立的硬連結資訊是怎麼維護的呢?看到這裡明白了,硬連結的dentry資訊被寫到了父目錄的資料塊中*/
err = ext3_add_entry(handle, dentry, inode);
if (!err) {
ext3_mark_inode_dirty(handle, inode);
//将源檔案的inode号記錄在連結檔案dentry中
d_instantiate(dentry, inode);
}
return err;
在ext3_link()中完成連結的具體工作,抛開一些與日志相關的内容,我們可以看到主要調用了ext3_add_entry()來将連結檔案的dentry添加到父目錄的資料塊中,與此同時也會将源檔案的inode号記錄在連結檔案dentry中,這樣便達到了源檔案和連結檔案有着不同的dentry結構,卻共享inode的目的。
使用strace工具,可以發現建立軟連結調用的函數是symlink(),該函數的核心入口為syscall_define2(symlink,...),其實就是sys_symlink()。我們就從這個入口開始一步步跟蹤sys_symlink()的實作原理。
syscall_define2(symlink, const char __user *, oldname, const char __user *, newname)
return sys_symlinkat(oldname, at_fdcwd, newname);
sys_symlink()其實調用了函數sys_symlinkat(at_fdcwd, oldname, at_fdcwd, newname, 0)。而sys_symlinkat()的源代碼(簡化後)如下:
syscall_define3(symlinkat, const char __user *, oldname,
int, newdfd, const char __user *, newname)
char *from;
struct dentry *dentry;
from = getname(oldname);
//查找建立軟連結父目錄結構,存于nd之中
//在上面查找的父目錄下建立軟連接配接dentry,作為傳回值
dentry = lookup_create(&nd, 0); //dentry為建立軟連接配接父目錄的dentry
error = security_path_symlink(&nd.path, dentry, from);
//實參意義:
//d_inode:連結檔案父目錄inode結構
//dentry:連結檔案的dentry結構
//from:源檔案名
error = vfs_symlink(nd.path.dentry->d_inode, dentry, from);
通過代碼可以看到,其基本的函數調用流程和sys_linkat()一模一樣,隻是最後調用的是vfs_symlinkat()。而且,參數的意義稍有不談,可參見代碼注釋,vfs_symlinkat()代碼如下:
//建立軟連結
//@dir:軟連接配接父目錄inode
//@dentry:軟連接配接的dentry
//@oldname:源檔案或目錄的名字
int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname)
int error = may_create(dir, dentry);
//建立軟連接配接所在目錄對應的inode是否實作了具體檔案系統的symlink方法
if (!dir->i_op->symlink)
error = security_inode_symlink(dir, dentry, oldname);
//調用具體檔案系統的symlink方法(對應inode對象中的symlink方法)
error = dir->i_op->symlink(dir, dentry, oldname);
最終還是調用了具體檔案系統的symlink函數,如ext3_symlink()。
//ext3建立軟連接配接函數
//@dir:軟連接配接的父目錄的inode
//@dentry:軟連接配接的dentry結構
//@symname:源檔案名稱
static int ext3_symlink (struct inode * dir, struct dentry *dentry, const char * symname)
struct inode * inode;
int l, err, retries = 0;
l = strlen(symname)+1; //l為軟連接配接名字長度
if (l > dir->i_sb->s_blocksize) //軟連接配接名字超過對應檔案系統支援的最大名字長度
return -enametoolong;
retry:
ext3_index_extra_trans_blocks + 5 +
ext3_maxquotas_init_blocks(dir->i_sb));
//為軟連接配接建立一個新的inode結構
inode = ext3_new_inode (handle, dir, s_iflnk|s_irwxugo);
//如果軟連接配接名字過長則需要存放在單獨的資料塊中
if (l > sizeof (ext3_i(inode)->i_data)) {
inode->i_op = &ext3_symlink_inode_operations;
ext3_set_aops(inode);
err = __page_symlink(inode, symname, l, 1);
} else {
//如果源檔案名稱不夠長
//那麼直接将其儲存在inode的i_data中
inode->i_op = &ext3_fast_symlink_inode_operations;
memcpy((char*)&ext3_i(inode)->i_data,symname,l);
inode->i_size = l-1;
}
ext3_i(inode)->i_disksize = inode->i_size;
//将連結檔案的inode和dentry關聯并與其父目錄建立關聯
err = ext3_add_nondir(handle, dentry, inode);
分析ext3_symlink()的代碼,抛開日志等子產品不談,我們知道:
1. 代碼中會為連結檔案建立一個inode結構,這在函數ext3_new_inode()中實作,這也是硬連結和軟連結的最大不同;
2. 連結檔案的檔案内容是源檔案的檔案名,而且,如果檔案名不是很長(小于60位元組),會将檔案名直接儲存在inode中,無需為其配置設定資料塊;
3. 最後會将連結檔案的inode與dentry建立關聯,并将連結檔案的dentry寫入到父目錄的資料塊中,調用的是函數ext3_add_nondir()。