Android C++系列:Linux文件IO操作(一)

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1.1 C標準函數與系統函數的區別","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/81/81d15d83c01fe9d1bfa7f92bfcee432e.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1.1.1 I/O緩衝區","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每一個FILE文件流都有一個緩衝區buffer,默認大小8192Byte。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1.1.2 效率","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1.1.3 程序的跨平臺性","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"事實上Unbuffered I/O這個名詞是有些誤導的,雖然write系統調用位於C標準庫I/O緩 衝區的底層,但在write的底層也可以分配一個內核I/O緩衝區,所以write也不一定是直接 寫到文件的,也可能寫到內核I/O緩衝區中,至於究竟寫到了文件中還是內核緩衝區中對於 進程來說是沒有差別的,如果進程A和進程B打開同一文件,進程A寫到內核I/O緩衝區中的數 據從進程B也能讀到,而C標準庫的I/O緩衝區則不具有這一特性(想一想爲什麼)。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1.2 PCB概念","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1.2.1 task_struct結構體","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" /usr/src/linux-headers/include/linux/sched.h\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1.2.2 files_struct結構體","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1.3 open/close","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/49/49b84d0a87075e40de1383c71a503c89.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1.3.1 文件描述符","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個進程默認打開3個文件描述符","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"STDIN_FILENO 0 STDOUT_FILENO 1 STDERR_FILENO 2\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"新打開文件返回文件描述符表中未使用的最小文件描述符。 open函數可以打開或創建一個文件。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"#include #include #include \nint open(const char *pathname, int flags);\nint open(const char *pathname, int flags, mode_t mode); 返回值:成功返回新分配的文件描述符,出錯返回-1並設置errno\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Man Page中open函數有兩種形式,一種帶兩個參數,一種帶三個參數,其實在C代碼 中open函數是這樣聲明的:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" int open(const char *pathname, int flags, ...);\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後的可變參數可以是0個或1個,由flags參數中的標誌位決定,見下面的詳細說明。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"pathname參數是要打開或創建的文件名,和fopen一樣,pathname既可以是相對路徑也 可以是絕對路徑。flags參數有一系列常數值可供選擇,可以同時選擇多個常數用按位或運 算符連接起來,所以這些常數的宏定義都以O_開頭,表示or。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" 必選項:以下三個常數中必須指定一個,且僅允許指定一個。\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"* O_RDONLY 只讀打開* O_WRONLY 只寫打開* O_RDWR 可讀可寫打開","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以下可選項可以同時指定0個或多個,和必選項按位或起來作爲flags參數。可選項有很多, 這裏只介紹一部分,其它選項可參考open(2)的Man Page:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"* O_APPEND 表示追加。如果文件已有內容,這次打開文件所寫的數據附加到文件的末尾 而不覆蓋原來的內容。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"* O_CREAT 若此文件不存在則創建它。使用此選項時需要提供第三個參數mode,表示該 文件的訪問權限。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"* O_EXCL 如果同時指定了O_CREAT,並且文件已存在,則出錯返回。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"* O_TRUNC 如果文件已存在,並且以只寫或可讀可寫方式打開,則將其長度截斷(Trun-","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"cate)爲0字節。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"* O_NONBLOCK 對於設備文件,以O_NONBLOCK方式打開可以做非阻塞I/O(Nonblock I/","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"O),非阻塞I/O在下一節詳細講解。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"注意open函數與C標準I/O庫的fopen函數有些細微的區別: 以可寫的方式fopen一個文件時,如果文件不存在會自動創建,而open一個文件時必須","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"明確指定O_CREAT纔會創建文件,否則文件不存在就出錯返回。 以w或w+方式fopen一個文件時,如果文件已存在就截斷爲0字節,而open一個文件時必","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"須明確指定O_TRUNC纔會截斷文件,否則直接在原來的數據上改寫。 第三個參數mode指定文件權限,可以用八進制數表示,比如0644表示-rw-r-r–,也可","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以用S_IRUSR、S_IWUSR等宏定義按位或起來表示,詳見open(2)的Man Page。要注意的是, 文件權限由open的mode參數和當前進程的umask掩碼共同決定。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":"none"},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"補充說明一下Shell的umask命令。Shell進程的umask掩碼可以用umask命令查看:","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" $ umask \n 0002\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用touch命令創建一個文件時,創建權限是0666,而touch進程繼承了Shell進程的umask 掩碼,所以最終的文件權限是0666&∼022=0644。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" $ touch file123\n $ ls -l file123\n -rw-rw-r-- 1 qingkouwei qingkouwei 0 9月 11 23:48 file123\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同樣道理,用gcc編譯生成一個可執行文件時,創建權限是0777,而最終的文件權限是","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"0777 & ∼022 = 0755。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" ubuntu:~$ umask\n 0002\n ubuntu:~$ gcc main.c\n ubuntu:~$ ls -l a.out\n -rwxrwxr-x 1 qingkouwei qingkouwei 7158 9月 11 23:51 a.out\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們看到的都是被umask掩碼修改之後的權限,那麼如何證明touch或gcc創建文件的權 限本來應該是0666和0777呢?我們可以把Shell進程的umask改成0,再重複上述實驗:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" $ rm file123 a.out\n $ umask 0\n $ touch file123\n $ ls -l file123\n -rw-rw-rw- 1 qingkouwei qingkouwei $ gcc main.c\n 0 9月 11 23:52 file123\n $ ls -l a.out\n -rwxrwxr-x 1 qingkouwei qingkouwei 7158 9月 11 23:52 a.out\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在我們自己寫一個程序,在其中調用open(“somefile”, O_WRONLY | O_CREAT, 0664);創建文件,然後在Shell中運行並查看結果:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"close函數關閉一個已打開的文件:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" #include \n int close(int fd); 返回值:成功返回0,出錯返回-1並設置errno\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"參數fd是要關閉的文件描述符。需要說明的是,當一個進程終止時,內核對該進程所有 尚未關閉的文件描述符調用close關閉,所以即使用戶程序不調用close,在終止時內核也會 自動關閉它打開的所有文件。但是對於一個長年累月運行的程序(比如網絡服務器),打開 的文件描述符一定要記得關閉,否則隨着打開的文件越來越多,會佔用大量文件描述符和系 統資源。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由open返回的文件描述符一定是該進程尚未使用的最小描述符。由於程序啓動時自動打 開文件描述符0、1、2,因此第一次調用open打開文件通常會返回描述符3,再調用open就會 返回4。可以利用這一點在標準輸入、標準輸出或標準錯誤輸出上打開一個新文件,實現重 定向的功能。例如,首先調用close關閉文件描述符1,然後調用open打開一個常規文件, 則一定會返回文件描述符1,這時候標準輸出就不再是終端,而是一個常規文件了,再調用 printf就不會打印到屏幕上,而是寫到這個文件中了。後面要講的dup2函數提供了另外一種 辦法在指定的文件描述符上打開文件。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1.3.2 最大打開文件個數","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"查看當前系統允許打開最大文件個數","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" cat /proc/sys/fs/file-max\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當前默認設置最大打開文件個數1024","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":" ulimit -a\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"修改默認設置最大打開文件個數爲4096","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"text"},"content":[{"type":"text","text":"ulimit -n 4096\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"1.4 總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文本介紹了Linux下的文件操作命令、系統調用、API接口等。並介紹了C標準函數與系統函數的區別,PCB概念等。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章