第四章--檔案和目錄__unix環境進階編程

來源:互聯網
上載者:User
1、檔案的屬性資訊:

1. 屬性結構體struct stat;

struct stat {            mode_t     st_mode;  //檔案類型與存取權限位            ino_t         st_ino;      //檔案i節點編號            dev_t        st_dev;      //檔案所屬的檔案系統的裝置號(包括主裝置、次裝置號)            dev_t        st_rdev;     //檔案是塊裝置、字元裝置時的裝置號(主裝置、次裝置)            nlink_t      st_nlink;    //            uid_t         st_uid;      //檔案所有者的使用者Id            gid_t         st_gid;      //檔案所有者的使用者組Id            off_t          st_size;     //檔案的大小(位元組單位)            time_t       st_atime;  //檔案內容的最新訪問時間            time_t       st_mtime; //檔案內容的最新修改時間            time_t       st_ctime;   //檔案屬性的最新修改時間            blksize_t    st_blksize;//檔案所在檔案系統的塊大小            blkcnt_t     st_blocks; //檔案在檔案系統中所佔的塊數     }

2. 檔案類型:
1、普通檔案
2、目錄檔案。檔案內容為其包括的檔案名稱與檔案i節點編號
3、塊特殊檔案。檔案為塊裝置
4、字元特殊檔案。檔案為字元裝置
5、FIFO。具名管道,用於處理序間通訊
6、通訊端。用於進程間的網路通訊,進程可以是同一台機器上的,也可以是不同機器上的。
struct stat結構體中的st_mode欄位,儲存有檔案的類型,有如下宏可以查看該檔案的類型:

    S_ISREG(stat.st_mode)。如果返回結果為真,則為普通檔案    S_ISDIR(stat.st_mode)。如果返回結果為真,則為目錄檔案    S_ISCHR(stat.st_mode)。如果返回結果為真,則為字元特殊檔案    S_ISBLK(stat.st_mode)。如果返回結果為真,則為塊特殊檔案    S_ISFIFO(stat.st_mode)。如果返回結果為真,則為具名管道檔案    S_ISLNK(stat.st_mode)。如果返回結果為真,則為符合連結檔案    S_ISSOCK(stat.st_mode)。如果返回結果為真,則為通訊端檔案

3. 擷取檔案屬性資訊的函數

 #include <sys/stat.h> int stat(const char *restrict pathname, struct stat *restrict buf); int fstar(int filedes, struct stat *buf); int lstat(const char *restrict pathname, struct stat *restrict buf);
                                                                         函數傳回值:成功返回0,出錯返回-1;

pathname為檔案路徑、fieldes為檔案描述符,buf儲存檔案屬性資訊
lstat函數,在檔案為符號連結檔案類型時,只返回該符號連結檔案本身對應的檔案屬性,而不是該符號連結指向的檔案的屬性。

作業系統中各種檔案類型所佔比例 2、檔案存取權限

一個檔案的許可權資訊如下圖所示:

1、第一列的 -rwxr-xr-x:
-:表示該檔案是一個普通檔案
rwx:檔案所有者擁有的許可權,此處所有者的操作許可權為對讀、寫、執行
r-x:檔案所有組擁有的許可權,此處所有組的操作許可權為讀、執行
r-x:檔案其他使用者擁有的許可權,此處其他使用者的操作許可權為讀、執行

2、第三、四列的 liuxiaowu liuxiaowu:
第三列表示檔案所有者,struct stat結構裡的st_uid欄位
第四列表示檔案所有組,struct stat結構裡的st_gid欄位

3、第五列的 425:
檔案的大小,單位為位元組

4、第六、七、八列的 Nov 11 23:02
檔案內容最後被修改的時間

5、第九列的 index.html:
檔案的名稱

通過struct stat結構中的st_mode欄位,查看檔案的許可權資訊的宏:

S_IRUSR(stat.st_mode),結果為真,表示使用者可讀S_IWUSR(stat.st_mode),結果為真,表示使用者可寫S_IXUSR(stat.st_mode),結果為真,表示使用者可執行S_IRGRP(stat.st_mode),結果為真,表示組可讀S_IWGRP(stat.st_mode),結果為真,表示組可寫S_IXGRP(stat.st_mode),結果為真,表示組可執行S_IROTH(stat.st_mode),結果為真,表示其他可讀S_IWOTH(stat.st_mode),結果為真,表示其他可寫S_IXOTH(stat.st_mode),結果為真,表示其他可執行

執行許可權的意思:

為了開啟/usr/include/stdio.h檔案,需要對目錄/、/usr、/usr/include具有執行許可權,然後還需要對stdio.h檔案具有適當的許可權,這取決於以何種模式開啟它(唯讀、讀寫等)

目錄的執行許可權與讀許可權的區別:
目錄的讀許可權,是允許讀取目錄中包含的檔案清單資訊
目錄的執行許可權,是指當我們要訪問檔案的路徑名的一個組成部分時,對該目錄的執行許可權使得我們可以通過該目錄 3、設定使用者ID與設定組ID

相關的ID

實際使用者ID:    建立該進程的使用者實際組ID:    建立該進程的使用者所在的使用者組有效使用者ID:    進程所屬所有者有效組ID:    進程所屬所有組附加組ID:    進程所屬附加組。一個進程可以有一個有效組,但可以有多個附加組。附加組是對有效組的補充,可以擴大進程的操作許可權儲存的設定使用者ID:    進程在執行exec函數時,會將有效使用者ID儲存在此儲存的設定組ID:    進程在執行exec函數時,會將有效組ID儲存在此

檔案的設定使用者ID與設定使用者組ID

當執行一個程式檔案時,進程的有效使用者ID通常是實際使用者ID,有效組ID通常是實際組ID。但可以在檔案的st_mode欄位中設定一個特殊標誌,其含義是“當執行此檔案時,將進程的有效使用者ID設定為檔案所有者的使用者ID”,該特殊標誌就是檔案的設定使用者ID(設定組ID同理)。
有了此標誌,則一旦程式檔案的所有者為超級使用者,而且檔案的st_mode欄位設定了設定使用者ID標誌,則進程在執行該程式檔案時,進程的有效使用者ID就為檔案所有者,即超級使用者了,這樣進程具有超級使用者的執行許可權。如passwd檔案在執行時,允許任何使用者修改自己的密碼,就是利用的該原理。

進程對檔案進行操作時的許可權檢查:

1、若進程的有效使用者ID是0(超級使用者),則允許訪問。2、若進程的有效使用者ID等於檔案的所有者ID,則如果檔案所有者有對該檔案的適當許可權,則允許訪問。適當許可權是指,若進程為讀而開啟該檔案,則所有者讀位應為1;若進程為寫而開啟該檔案,則所有者寫位應為1;若進程將執行該檔案,則所有者執行位應為13、若進程的有效組ID或者附加組ID之一等於檔案所有組ID,則如果檔案所有組有對該檔案的適當許可權,則允許訪問。4、若其他使用者具有對該檔案的適當許可權,則允許訪問

在執行檔案操作時,會按順序執行以上四步。如果進程的有效使用者ID等於檔案的所有者ID,則當檔案所有者沒有對該檔案的適當許可權,那麼是不會繼續檢查第3步的。依次類推,只會從上至下檢查一個步驟。 4 新檔案的所有者ID與所有組ID

進程在建立新檔案時,檔案的所有者預設為進程的有效使用者ID。而檔案的所有組,在Linux系統下是有如下兩種情況:
1.檔案所屬目錄的設定組ID位被設定時,新檔案的所有組ID為所屬目錄的所有組ID
2.否則為進程的有效組ID 5 access函數

當程式檔案被執行時,如果檔案的設定使用者ID(設定組ID同理)位被設定,則執行程式檔案的進程的有效使用者ID為檔案的所有者ID,這時進程的操作許可權就變為了檔案的所有者ID。此時,如果進程想要判斷自己本身的許可權是否有對檔案的操作許可權,則可以用access函數來得到。access函數是使用進程的實際使用者ID來判斷,是否有對程式檔案的操作許可權。

#include <sys/unistd.h>int access(const char *name, int mode)    傳回值:成功返回0,出錯返回-1    mode取值範圍,R_OK:可讀,W_OK:可寫,X_OK:可執行,F_OK:測試檔案是否存在
#include <sys/unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int main(int argc, char *argv[]) {    for (int i = 1; i < argc; i++) {        if (access(argv[i], R_OK) == 0) {            printf("shiji can read\n");//實際使用者可讀        }        if (open(argv[i], O_RDONLY) > 0) {            printf("youxiao can read\n");//有效使用者可讀        }    }}

測試:
1.編譯檔案:

gcc -std=c99 testAccess.c -o testAccess 

2.程式檔案的許可權資訊:

-rwxrwxr-x 1 root liu  7415 11月 26 21:55 testAccess*

3./etc/shadow檔案的許可權資訊:

-rw-r----- 1 root shadow 1220  7月  2 21:59 /etc/shadow

4.測試執行程式檔案的進程是否有/etc/shadow的讀許可權:

$./testAccess /etc/shadow

可以看到,什麼也沒輸出,進程沒有相應的操作許可權

5.設定程式檔案的所有者為root使用者以及設定使用者ID位:

sudo chown root testAccesssudo chmod u+s testAccess

6.顯示程式檔案的許可權資訊:

-rwsrwxr-x 1 root liu  7415 11月 26 21:55 testAccess*

7.測試執行程式檔案的進程是否有/etc/shadow的讀許可權:

$./testAccess /etc/shadowyouxiao can read

可以看到進程可以以讀許可權開啟檔案,這表明進程的實際使用者ID沒有/etc/shadow檔案的操作許可權,而進程的有效使用者ID有許可權,因為此時進程的有效使用者ID為程式檔案的所有者root 6 umask函數

umask值叫做檔案模式建立屏蔽值。當建立新檔案時,會指定檔案的所有組/所有組/其他使用者的相應操作許可權。新檔案最終的操作許可權會在指定的許可權基礎上,減去umask設定值。比如umask為002時,如果指定新檔案的許可權為 777,則新檔案最終的許可權為775

#include <sys/stat.h>int umask(mode_t cmask);    傳回值,以前的檔案模式建立屏蔽值
7 chmod與fchmod函數
#include <sys/stat.h>int chmod(const char *name, mode_t mode);int fchmod(int fiedes, mode_t mode);    傳回值,成功返回0,出錯返回-1

chmod/fchmod函數,用於改變檔案的許可權位。
只有當進程的有效使用者ID等於檔案的所有者ID,或者進程具有超級使用者權限時,才可以進行修改。
chmod函數只會修改許可權資訊,不會修改檔案內容。這表示其只會更新i節點資訊。

mode取值

//所有者讀,寫,執行,讀寫執行S_IRUSRS_IWUSRS_IXUSRS_IRWXU//所有組讀,寫,執行,讀寫執行S_IRGRPS_IWGRPS_IXGRPS_IRWXG//其他讀,寫,執行,讀寫執行S_IROTHS_IWOTHS_IXOTHS_IRWXO//設定使用者ID位,設定使用者組ID位,設定儲存本文(粘住位)S_ISUIDS_ISGIDS_ISVTX

進程在建立新檔案時,如果檔案的所有組ID不為進程的有效組ID或附加組ID的某個(父目錄設定組ID位時是有可能出現這種情況),則如果新檔案設定組ID位,這時會自動將設定組ID位關閉。這就防止了使用者建立一個設定組ID檔案,而該檔案是由非該使用者所屬組擁有的。防止了使用者建立新檔案時,設定組ID位,然後以該檔案的父目錄組的許可權執行相應操作。 8 粘住位

粘住位的意思是,一旦檔案被載入到記憶體中,則它的資訊會一直儲存在交換區中,而不會被置換出去。這樣下次該檔案需要被執行時,可以很快地被載入。

目錄的粘住位:
當目錄設定了粘住位時,如果使用者對目錄有寫操作許可權,則在對該目錄下的檔案進行刪除或更名操作時,必須要滿足如下條件之一:
1.使用者為超級使用者
2.使用者為目錄所有者
3.使用者為檔案所有者
如/tmp目錄,其許可權資訊為:

drwxrwxrwt.  9 root root  4096 11月 26 23:13 tmp

可以看到,其所有者為root,所有組為root,其他使用者的許可權為讀寫執行(+粘住位)。每個使用者都可以在該目錄下建立屬於自己的檔案,但是每個使用者只可以對屬於自己的檔案進行刪除或更名操作。 9 chown,fchown,lchown函數

#include <sys/stat.h>int chown(const char *name, uid_t owner, gid_t gid);int fchown(int fiedes, uid_t owner, gid_t gid);int lchown(const char *name, uid_t owner, gid_t gid);        傳回值,成功返回0,出錯返回-1

如果檔案為符號連結,則chown,fchown函數修改的是符號連結指向的檔案,而lchown函數修改的是符號連結檔案自身。
在函數執行時,如果uid或gid有一個為-1,則所有者或所有組ID不變。

限制規則:
1.只有超級使用者進程才可以修改檔案的所有者ID
2.在對一個檔案調用上述函數執行所有者修改時,必須滿足如下兩個條件:

1.進程的有效使用者ID等於檔案的所有者ID2.owner為-1或為檔案的所有者ID時,且進程的有效組ID或附加組ID之一等於gid時,可以設定檔案的組ID

當非超級使用者修改了檔案的所有者ID或所有組ID後,檔案的設定使用者ID位與設定組ID位都會被清除。這是為了防止修改檔案的所有者ID或所有組ID之後,利用檔案的設定使用者ID位或設定組ID位來執行其他使用者的許可權。 10 檔案長度

struct stat結構中的st_size表示檔案內容的位元組長度,st_blksize表示檔案所處的儲存系統,最合適的緩衝區塊大小,st_blocks表示檔案所佔512位元組的塊數量。

空洞:
空洞是指檔案中未初始化的資料,比如當直接在檔案尾端的後5個位元組開始寫資料,則在檔案尾端後的5個位元組,就為空白洞,它並不佔用實際的儲存空間。
但是當把該檔案中的資料讀取,並寫入新的檔案時,會將空洞填充為0,這樣新檔案中,這些原來為空白洞的資料就會佔用實際的儲存空間了。

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <errno.h>#include <string.h>int main(int argc, char * argv[]) {        int fd;        if ((fd = open("kongdong.txt", O_WRONLY | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO)) < 0) {                printf("open error: %s\n", strerror(errno));        }        write(fd, "1234", 4);        lseek(fd, 100000, SEEK_END);        write(fd, "5678", 4);        return 0;}

上述程式執行後,會建立建立檔案kongdong.txt,其檔案大小為10008位元組:

-rwxrwxr-x 1 liuxiaowu liuxiaowu 10008 Dec  2 17:53 kongdong.txt 

通過du -s命令,顯示該檔案所佔的塊數量為:

8       kongdong.txt

而cat kongdong.txt > kongdong_copy.txt,顯示kongdong_copy.txtx檔案的大小也為10008位元組,但du -s命令,顯示該檔案所佔的塊數量為:

-rw-rw-r-- 1 liuxiaowu liuxiaowu 10008 Dec  2 17:54 kongdong_copy.txt12      kongdong_copy.txt

這說明上述程式產生的檔案kongdong.txt是存在空洞的。 11 檔案截斷

#include <unistd.h>#include <sys/types.h>int truncate(const char *path, off_t length);int ftruncate(int fd, off_t length);

當length長度比檔案原有長度短時,這時會將檔案截斷,length以後的檔案內容將不能再訪問。
當length長度比檔案原有長度長時,這時會擴充該檔案,擴充的檔案內容會被讀作0,也就是空洞。 12 檔案系統

上圖為檔案系統的結構圖。一個磁碟分為多個分區,每個分區都是一個檔案系統,包括自主塊、超級塊、柱面組資訊。每個柱麵包括超級塊的副本、i節點與資料區塊等資訊。

i節點在記憶體中按順序挨著存放,i節點中包括檔案檔案類型、檔案當前位置、檔案大小、檔案存取權限、指向檔案實際內容所在資料區塊的指標等資訊,struct stat結構中除了i節點編號、檔案名稱外,其餘資訊都來自i節點。

資料區塊是存放檔案實際內容的地方,按檔案類型不同,又可以細分為目錄塊與資料區塊。目錄塊存放的是目錄資訊,資料區塊存放的檔案的內容。目錄塊的格式如圖所示,包括i節點編號與檔案名稱。

每個i節點都有一個引用連結數,表示有多少個目錄項指向該i節點。

對於檔案來說,只有當其i節點的引用計數為0時,才可以釋放該檔案所佔用的資料空間。這也是為什麼刪除檔案的函數名是unlink,而不是delete對於目錄來說,不包含任何子目錄的目錄項,其引用連結數至少為2,一個是其目錄塊本身會有一個目錄項指向它自己(稱為.),一個是其父目錄的目錄塊會有一個目錄項指向其包含的目錄項。有包含子目錄的目錄項,則其引用連結數至少為3,除了上述的兩個,還有其子目錄的目錄塊中會有一個指向其父目錄的目錄項(稱為..)。

如上圖中的i節點1278,圖中顯示有兩個引用連結數,一個是目錄本身(.),一個是其父目錄的目錄塊中的目錄項。

由上述內容可知,對於mv命令,其操作過程並沒有移動檔案內容地址,只是在目前的目錄中刪除了一個目錄項,而在另一個目錄中增加了一個目錄項。

i節點編號是對於某一個檔案系統來說的,不同檔案系統中i節點編號可能會有重複。 13 unlink、link、rename、remove函數

 #include <unistd.h>int link(const char *oldpath, const char *newpath);int unlink(const char *pathname);#include <stdio.h>int rename(const char *oldpath, const char *newpath);int remove(const char *pathname);

link函數:
新建立一個目錄項newpath,其目錄項中的i節點編號與oldpath目錄項中的i節點一樣。

因i節點編號只在同一個檔案系統內有效,link函數不能跨檔案系統建立連結。

如果newpath是一個目錄,則只有具有超級使用者權限時,才可以建立成功。因為對於目錄的永久連結,有可能會產生無限迴圈。

unlink函數
unlink函數,將pathname所在目錄項指向i節點的引用連結數減一。

只有當一個檔案的引用連結數為0且沒有進程開啟它時,它的檔案空間才能被釋放。所以很多臨時檔案的建立過程如下:先open一個不存在的檔案,然後馬上unlink它,這時該檔案的引用連結數為0,但是該進程還開啟它。當進程執行結束時,核心會檢查該檔案的進程開啟數是否為0,如果為0,則繼續檢查該檔案的引用連結數,如果連結數為0,則釋放該檔案所佔用的儲存空間。

remove函數
如果pathname是一個檔案,則該函數的作用與unlink一樣。如果pathname是一個目錄,則該函數的作用與rmdir函數一樣。

從其標頭檔stdio.h來看,該函數是ISO C定義的。

rename函數
將oldpath檔案更名為newpath。對於檔案與目錄,作用分別如下:

oldpath與newpath都為檔案:

如果newpath已經存在,則會先刪除newpath。刪除oldpath目錄項,建立newpath目錄項。如果檔案為符號連結,則操作的是符號連結本身。  

oldpath與newpath都為目錄:

如果newpath已存在,則會先刪除newpath,這隻能是空目錄的情況下。刪除oldpath目錄項,建立newpath目錄項。newpath目錄項的路徑,不能是oldpath目錄項的子目錄。如不能將/usr/foo更名為/usr/foo/foo1,因為這樣會刪除/usr/foo目錄,再建立/usr/foo/foo1目錄,這時是不會成功的,因為foo目錄已不存在

因為需要刪除oldpath目錄項,建立newpath目錄項,所以需要同時對oldpath的父目錄、newpath的父目錄有寫、執行許可權。

而如果newpath已經存在時,進程還需要對newpath有寫入權限。(還沒明白為什麼需要有寫入權限14 符號連結

永久連結具有下面兩個缺點:
1、不能跨檔案系統
2、只有超級使用者才能建立指向目錄的永久連結(因為目錄永久連結可能造成無限迴圈)
基於此,又增加了符號連結。符號連結是一種檔案類型,它的內容為指向的實際檔案的檔案路徑。
一般對檔案進行操作的函數,都是對符號連結指向的實際檔案進行操作,除了特殊的幾個函數會對符號連結本身進行操作:

 #include <sys/stat.h> int lstat(const char *restrict path, struct stat *restrict buf);
#include <unistd.h>ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize);

符號連結在檔案系統中引入迴圈:

cd APUE/4mkdir testSymbolcd testSymbolmkdir fooln -s foo foo/testdir

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.