[POSIX]文件系統(概述)


1.文件名由除系統目錄分隔符(unix是/,windows是\)和空字符“\0”外的任意ASCII字符組成,現代系統很多還可以包含UNICODE字符,但是還是推薦使用傳統的ASCII碼命名.

2.目錄不能創建硬鏈接.

3.文件描述符是一個非負數.

4.文件描述符(fd)是一個非負數,每個進程的fd之間不存在聯系,每個進程都有一個注1|進程表項,每個進程調用打開文件系統調用時,進程表項就會增加一條,每條的id就是fd,fd只會按照“從0開始(每個進程從各自的0開始),返回最小的未用的fd”的這個規則返回fd(通常0到2會被系統標准輸出輸入占用),不同進程打開同一個文件,會有不同的fd返回.

5.一般shell程序會把0、1、2三個文件描述符標准化為shell程序的輸入輸出和錯誤輸出,換句話說文件描述符0到2已經被這3個標准輸入輸出占用了.

POSIX也定義了三個常量代表了這三個數字包含在unistd.h頭文件上

STDIN_FILENO 0
STDOUT_FILENO 1
STDERR_FILENO 2

 

代碼示例:

#include <fcntl.h>
#include <unistd.h>

int main(void){
    char s_1[] = "my error.\n";
    write(STDERR_FILENO, s_1, 10);
    return 0;
}

此代碼片段會在控制台,輸出"my error.\n".

6.在shell中啟動的進程寫入STDOUT_FILENO和STDERR_FILENO會輸出到屏幕,但是STDOUT_FILENO是帶緩沖的IO,而STDERR_FILENO是即刻響應的.

7.由open和openat返回的文件描述符,一定是最小未用的文件描述符,利用這一特性,應用程序可以關閉掉標准的輸入輸出從而啟動自行的輸入輸出.

代碼示例:

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>


int main(void){
    #define LEN 3
    char p_1[] = "./abc.txt";
    close(STDOUT_FILENO);
    int fd_1 = open(p_1, O_RDWR|O_APPEND|O_CREAT);
    
    printf("%d\n", fd_1);

    return 0;
}

以上代碼關閉了標准輸出,再把它重定向到了進程當前工作目錄下的"./abc.txt"文件,這時進程的所有輸出,都會被記錄到"./abc.txt"上.

 8.具有緩沖的I/O,當輸入或輸出還不至於填滿緩沖區時,實際只是I/O到緩沖區,只有在兩種情況帶緩沖的函數才會輸出到設備:

     ---緩沖區被填滿,緩沖區內容自動被刷出.

     ---程序正確退出(exit(0)),就算緩沖區不被填滿也會被刷出.

 9.在shell終端,當鍵入回車鍵時終端進程實際上會執行兩個動作.

    ---輸入當前系統回車字符,至於輸入什么字符通常是終端運行系統環境決定的,Unix體系為\n,Windows體系為\r\n.

    ---發送輸入內容,也就是把輸入內容寫進STDIN.

10.一些帶緩沖的,操作字符串的I/O函數,會在讀取的返回內容長度n的位置自動填充\0,所以它讀取的字符長度實際上只有n-1,例如fgets.

11.一些帶緩沖的,操作字符串的I/O函數,在每次調用的時候會自動刷出緩沖區,就像調用fflush(fd)一樣,例如fgets.

13.事實上,就算是不帶緩沖的I/O函數,如read,write,也不會直接操作存儲設備,傳統unix系統實現在內核中設有緩沖區高速緩存或頁高速緩存,大多數磁盤I/O都是通過緩沖區進行.舉個例子,當進程A向文件A寫入一個字符,內核並不會馬上啟動硬件把這一個字節寫入存儲設備,而是先填充在高速緩沖區,當進程B恰好向文件A讀取此字符,內核會在緩沖區返回這個字符,同時會刷新緩沖區,這些操作,對於應用程序本身是感知不到的,除非你把存儲設備同時綁定兩個系統,如虛擬機和宿主共同訪問一個存儲設備,你會發現這個細節。

14.Unix還提供了一些函數來操作I/O高速緩存,通常還有一個守護進程定時將緩沖區的內容刷出緩沖區,詳情請參考《Unix高級環境編程》3.13 函數sync、fsync和fdatasync.

15.當open/openat以O_APPEND標志打開文件時,調用lseek將不能改變write的偏移位,此時write的偏移位始終會在結尾開始,但是可以改變read的偏移位.

16.當文件不支持lseek時,可以調用lseek(STDIN_FILENO, 0, SEEK_CUR) == -1來判斷,切忌一定要看是否等於-1,判斷小於0沒用,因為有些系統lseek支持設置為負值.

17.lseek僅將當前的文件偏移量記錄在內核中,它並不引起任何I/O操作,因此在打開一個空文件,並設置偏移位,並不會為該文件寫入任何東西,直到真正調用寫入操作為止.

18.O_APPEND具有一定的特殊性,調用操作偏移位函數對write無效的根本原因是,每次write都會從v(i)-node獲取文件長度作為自己的偏移量的特性,這就意味着你不能用無O_APPEND標志的fd設置一個跟文件長度對等的偏移位去模擬O_APPEND的行為.

19.當存在多個進程打開同一個文件,那么每個進程的這個fd會各自維護一個注2|文件表項,這個表包括文件狀態標志(讀,寫,添,同步,非阻塞等信息)和當前文件偏移位,還有指向v(i)-node的指針。所以每個進程對於這個文件的操作偏移位不是共享的,這將會涉及到數據同步的問題,詳情參考《Unix高級環境編程》3.10 文件共享 章節.

20.有些例外的情況,不同的進程或同進程不同的fd會引用同一個文件表,例如用來復制fd的dup函數和fork子進程時會發生這樣的情況.

21.除root用戶外,其余用戶只能編輯屬於自己名下的文件權限位,詳情參考《Unix高級環境編程》4.9 章節.

22.只有root用戶可以更新文件的所有者UID,而關於組GID,只有文件所有者可以修改組GID,詳情參考《Unix高級環境編程》4.11 章節.

23.目錄擁有者可以刪除該目錄下不屬於自己或者不具備權限的文件.

24.一些文件系統不允許用戶對目錄創建硬鏈接(就算允許,也僅限於超級管理員),這是因為需要避免循環訪問的原因,詳情參考《Unix高級環境編程》4.15 章節.

25.一個文件只有一個iNode.

26.目錄文件的block保存的是該目錄下的目錄項,目錄項包含一個文件名和一個指向該文件iNode的指針.

27.重命名(或者改變文件路徑),實際上是刪除原有的目錄項,新建一個指向該iNode的目錄項.

28.硬鏈接會在block中創建目錄項,目錄項指向和這個文件的其他硬鏈接(如果有)同一個iNode上,關於這個iNode,每增加一個硬鏈接,就會在它的引用次數(stat.st_nlink)上加1,當刪除該文件時,其實只是刪除引用這個iNode的目錄項,並且會在這個iNode的引用次數上減1,直到引用次數為0時,系統調用才會真正刪除這個iNode,並回收它指向的block.

29.軟連接則是一個單獨的文件,擁有單獨的iNode,這個文件的iNode表項中S_IFLNK表明這個文件是一個符號鏈接,iNode指向block,而block中包含了需要指向的文件的完整文件名(參考《Unix高級環境編程》4.14 章節),刪除它並不影響它指向的文件.

30.硬鏈接要求文件在同一個文件系統,而軟連接可以跨文件系統。只有超級用戶才有權限為目錄增加硬鏈接.

31.mkdir創建新文件目錄時,會自動創建.和..這兩個目錄項,它們分別指向自己和自己的上一級,詳情參考《Unix高級環境編程》4.21 章節.

 

 

注1|進程表項

標志 說明
close_on_exec 通常0(系統默認,exec時不關閉)或1(在exec時關閉),但是標准起見,POSIX定義了FD_CLOEXEC這個常量,代表在exec時關閉
文件指針 指向文件表項的指針

 

*如表格所示,Unix系統目前只定義了一種文件描述符標志

 

 

 

注2|文件表項

標志 說明
文件狀態標志 打開文件的方式標志位
當前文件偏移量  
v(i)-node指針 指向v(i)-node的指針

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM