任何一個文件可以有多個目錄項指向其i節點。創建一個指向現有文件的鏈接的方法是使用link函數。
#include <unistd.h> int link( const char *existingpath, const char *newpath ); 返回值:若成功返回0,若出錯返回-1
此函數創建一個新目錄項newpath,它引用現有的文件existingpath。如若newpath已經存在,則返回出錯。只創建newpath中的最后一個分量,路徑中的其他部分應當已經存在。
創建新目錄項以及增加鏈接計數應當是個原子操作。雖然POSIX.1允許實現支持跨文件系統的鏈接,但是大多數實現要求這兩個路徑名在同一個文件系統中。如果實現支持創建指向一個目錄的硬鏈接,那么也僅限於超級用戶才可以這樣做。其理由是這樣做可能在文件系統中形成循環,大多數處理文件系統的實用程序都不能處理這種情況。因此很多文件系統實現不允許對目錄的硬鏈接。
為了刪除一個現有的目錄項,可以調用unlink函數。
#include <unistd.h> int unlink( const char *pathname ); 返回值:若成功則返回0,若出錯則返回-1
此函數刪除目錄項,並將由pathname所引用文件的鏈接計數減1。如果還有指向該文件的其他鏈接,則仍可通過其他鏈接訪問該文件的數據。如果出錯,則不對該文件做任何更改。
為了解除對文件的鏈接,必須對包含該目錄項的目錄具有寫和執行的權限。如果對該目錄設置了粘住位,則對該目錄必須具有寫權限,並且具備下面三個條件之一:
擁有該文件。
擁有該目錄。
具有超級用戶特權。
只有當鏈接計數達到0時,該文件的內容才可被刪除。另一個條件也會阻止刪除文件的內容——只要有進程打開了該文件,其內容也不能刪除。關閉一個文件時,內核首先檢查打開該文件的進程數。如果該數達到0,然后內核檢查其鏈接數,如果這個數也是0,那么就刪除該文件的內容。
程序清單4-5 打開一個文件,然后unlink它
[root@localhost apue]# cat prog4-5.c #include "apue.h" #include <fcntl.h> int main(void) { if(open("tempfile", O_RDWR) < 0) err_sys("open error"); if(unlink("tempfile") < 0) err_sys("unlink error"); printf("file unlinked\n"); sleep(15); printf("done\n"); exit(0); }
運行該程序,其結果是:
[root@localhost apue]# ls -l tempfile -rw-r--r-- 1 root root 38117937 01-02 18:18 tempfile [root@localhost apue]# df /home 文件系統 1K-塊 已用 可用 已用% 掛載點 /dev/sda3 15289132209228
14290712 2% /home [root@localhost apue]# ./prog4-5 & [1] 5152 [root@localhost apue]# file unlinked [root@localhost apue]# ls -l tempfile ls: tempfile: 沒有那個文件或目錄 此時,目錄項已刪除 [root@localhost apue]# df /home 文件系統 1K-塊 已用 可用 已用% 掛載點 /dev/sda3 15289132 209228 14290712 2% /home 從已用/可用空間可以看出該文件內容還沒有刪除 [root@localhost apue]# done [root@localhost apue]# df /home 文件系統 1K-塊 已用 可用 已用% 掛載點 /dev/sda3 15289132 171956 14327984 2% /home 睡眠結束后,進程結束,此時才刪除了該文件內容
unlink的這種性質經常被程序用來確保即使是在該程序崩潰時,它所創建的臨時文件也不會遺留下來。進程用open或creat創建一個文件,然后立即調用unlink。因為該文件仍舊是打開的,所以不會將其內容刪除。只有當進程關閉該文件或終止時(在這種情況下,內核會關閉該進程打開的全部文件),該文件的內容才會被刪除。
如果pathname是符號鏈接,那么unlink刪除該符號鏈接,而不會刪除由該鏈接所引用的文件。給出符號鏈接名情況下,沒有一個函數能刪除由該鏈接所引用的文件。
超級用戶可以調用unlink,其參數pathname指定一個目錄,但是通常應當使用rmdir函數,而不是使用這種方式。
我們也可以用remove函數解除對一個文件或目錄的鏈接。對於文件,remove的功能與unlink相同。對於目錄,remove的功能與rmdir相同。
#include <stdio.h> int remove( const char *pathname); 返回值:若成功則返回0,若出錯則返回-1
文件或目錄用rename函數更名。
#include <stdio.h> int rename( const char *oldname, const char *newname ); 返回值:若成功則返回0,若出錯則返回-1
根據oldname是指文件還是目錄,有幾種情況要加以說明。我們也應說明如果newname已經存在將會發生什么。
(1)如果oldname指的是一個文件而不是目錄,那么為該文件或符號鏈接更名。在這種情況下,如果newname已存在,則它不能引用一個目錄。如果newname已存在,而且不是一個目錄,則先將該目錄項刪除然后將oldname更名為newname。對包含oldname的目錄以及包含newname的目錄,調用進程必須具有寫權限,因為將更改這兩個目錄。
(2)如果oldname指的是一個目錄,那么為該目錄更名。如果newname已存在,則它必須引用一個目錄,而且該目錄應當是空目錄(空目錄指的是該目錄中只有.和..項)。如果newname存在(而且是一個空目錄),則先將其刪除,然后將oldname更名為newname。另外,當為一個目錄更名時,newname不能包含oldname作為其路徑前綴。例如,不能將/usr/foo更名為/usr/foo/testdir,因為舊名字(/usr/foo)是新名字的路徑前綴,因而不能將其刪除。
(3)如若oldname或newname引用符號鏈接,則處理的是符號鏈接本身,而不是它所引用的文件。
(4)作為一個特例,如果oldname和newname引用同一文件,則函數不做任何更改而成功返回。
如若newname已經存在,則調用進程對它需要有寫權限(如同刪除情況一樣)。另外,調用進程將刪除oldname目錄項,並可能要創建newname目錄項,所以它需要對包含oldname以及newname的目錄具有寫和執行權限。
本篇博文內容摘自《UNIX環境高級編程》(第二版),僅作個人學習記錄所用。關於本書可參考:http://www.apuebook.com/。