C語言之文件系統編程

(一)系統調用
所有的操作系統都提供多種服務的入口點,程序由此向內核請求服務。這些可直接進入內核的入口點被稱爲系統調用。
爲什麼用戶程序不能直接訪問內核提供的服務?
在Linux中,爲了更好地保護內核空間,程序的運行空間分爲內核空間和用戶空間(也就是常稱的內核態和用戶態),它們分別運行在不同的級別上,在邏輯上是相互隔離的。因此,用戶進程在通常情況下不允許訪問內核數據,也無法使用內核函數,它們只能在用戶空間操作用戶數據,調用用戶空間的函數。
(二)文件的概念
文件”這個名詞不陌生,什麼是文件?
系統資源(內存、硬盤、一般設備、進程間通信的通道等)的一個抽象
對系統資源進行訪問的一個通用接口。
採用這種“文件”的方式有什麼好處?
對資源提供通用的操作接口,可以極大地簡化系統編程接口的設計。
 常見的文件類型(可以通過文件來訪問的系統資源)有:
 普通文件:
  一般意義上的文件,作爲數據存儲在磁盤中,可以隨機訪問文件的內容。Linux系統中的文件是面向字節的,文件的內容以字節爲單位進行存儲和訪問。
 目錄:
 目錄是一種特殊的文件,目錄可以像普通文件一樣打開、關閉以及進行相應的操作。
 管道:
 管道是Linux中的一種進程間通信的機制
 設備文件:
 設備文件沒有具體的內容,對設備文件的讀寫操作實際上與某個設備的輸入輸出操作關聯在一起。
 符號鏈接:
 符號鏈接的內容是指向另一個文件的路徑。當對符號鏈接進行操作時,系統會根據情況將這個操作轉移到它所指向的文件上去,而不是對它本身進行操作。
 socket:
  socket也是一種進程間通信的方式,與管道不同的是,它們可以在不同的主機上進行通信,也就是網絡通信。
3)文件描述符
所有執行I/O操作的系統調用使用文件描述符來表示打開的文件。
 文件描述符是一個非負整數。
 文件描述符可以表示各種類型的打開的文件。
 對文件的操作只要使用文件描述符即可指定所操作的文件
4)文件操作的一般過程
打開文件,打開成功後,應用程序將獲得文件描述符。
 應用程序使用文件描述符對文件進行讀寫等操作。
 全部操作完畢後,應用程序需要將文件關閉以釋放用於管理打開文件的內存。
5)open()系統調用可以打開或創建一個文件。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
Returns file descriptor on success, or –1 on error    
 各參數及返回值的含義如下:
 pathname:要打開或創建的文件名稱。
 flags:標誌位,指定打開文件的操作方式。
 mode:指定新文件的訪問權限。(僅當創建新文件時才使用該參數)
 返回值:若成功返回文件描述符,否則返回-1並設置變量errno的值。
基本取值:
 O_RDONLY:以只讀方式打開文件。
 O_WRONLY:以只寫方式打開文件。
 O_RDWR:以讀寫方式打開文件。
注意:上述三個常量必須指定且只能指定一個。
 可選取值(部分):
 O_CREAT:如果被打開的文件不存在,則自動創建這個文件。
 O_EXCL:如果O_CREAT標誌已經使用,那麼當由pathname參數指定的文件已經存在時,open返回失敗。
 O_TRUNC:如果被打開的文件存在並且是以可寫的方式打開的,則清空文件原有的內容。
 O_APPEND:新寫入的內容將被附加在文件原來的內容之後,即打開後文件的讀寫位置被置於文件尾。
 思考:這些參數如何組合使用?
 6)read()系統調用從打開的文件中讀取數據。
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
Returns number of bytes read, 0 on EOF, or –1 on error    
    各參數及返回值的含義如下:
 fd:要讀取的文件的描述符。
 buf:讀取到的數據要放入的緩衝區。
 count:要讀取的字節數。
 返回值:若成功返回讀到的字節數,若已到文件結尾則返回0,若出錯則返回-1並設置變量errno的值。
注意:
1. 這裏的size_t是無符號整型,ssize_t是有符號整型。
2. buf指向的內存空間必須事先分配好。
7)write()系統調用向打開的文件寫數據。
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
Returns number of bytes written, or –1 on error    
    各參數及返回值的含義如下:
 fd:要寫入的文件的描述符。
 buf:要寫入的數據所存放的緩衝區。
 count:要寫入的字節數。
 返回值:若成功返回已寫的字節數,出錯則返回-1並設置變量errno的值。
8) close()系統調用關閉一個打開的文件。
#include <unistd.h>
int close(int fd);
Returns 0 on success, or –1 on error    
    各參數及返回值的含義如下:
 fd:要關閉的文件的描述符。
 返回值:若成功返回0,出錯則返回-1。
注:當一個進程終止時,內核會自動關閉它所有打開的文件。
9)lseek系統調用可以改變文件偏移量(File Offset)。文件偏移量是一個整數,表示距文件起始處的字節數。
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fildes, off_t offset, int whence);
Returns new file offset if successful, or –1 on error
    其中,參數whence必需是以下三個常量之一:
SEEK_SET:將文件偏移量設置在距文件開始處offset個字節。
SEEK_CUR:將文件偏移量設置在其當前值加offset,offset可正可負。
SEEK_END:將文件偏移量設置爲文件長度加offset,offset可正可負。

10)I/O緩衝
 爲了提高系統進行I/O操作的效率!
 系統調用要請求內核的服務,會引發CPU模式的切換,期間會有大量的堆棧數據保存操作,開銷比較大。如果頻繁地進行系統調用,會降低應用程序的運行效率。有了緩  衝機制以後,多個讀寫操作可以合併爲一次系統調用,減少了系統調用的次數,將大大提高程序的運行效率。
 所謂的標準I/O函數實際上是對底層系統調用的封裝,最終讀寫設備或文件的操作仍需調用系統I/O函數來完成。
11)文件指針和流
標準I/O函數並不直接操作文件描述符,而是使用文件指針。文件指針和文件描述符是一一對應的關係,這種對應關係由標準I/O庫自己內部維護。文件指針指向的數據類型爲FILE型,但應用程序無須關心它的具體內容。
 在標準I/O中,一個打開的文件稱爲流(stream),流可以用於讀(輸入流)、寫(輸出流)或讀寫(輸入輸出流)。每個進程在啓動後就會打開三個流,分別對應:stdin(標準輸入流)、stdout(標準輸出流)以及stderr(標準錯誤輸出流)。
 12)fopen用於打開一個標準I/O流
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
fopen打開由path指定的文件,並把它與一個文件流關聯起來。mode參數指定文件的打開方式。
fopen執行成功返回一個非空的FILE*指針,失敗時返回NULL。
"r"或"rb":以只讀方式打開。
 "w"或"wb":以只寫方式打開,並把文件長度截短爲零。
 "a"或"ab":以寫方式打開,新內容追加在文件尾。
 "r+"或"rb+"或"r+b":以更新方式打開(讀和寫)。
 "w+"或"wb+"或"w+b":以更新方式打開,並把文件長度截短爲零。
 "a+"或"ab+"或"a+b":以更新方式打開,新內容追加在文件尾。
注:字母b表示文件是一個二進制文件而不是文本文件。
13)fread()用於從一個文件流裏讀取數據。
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
  數據從文件流stream讀到ptr指向的數據緩衝區裏。size參數指定每個數據記錄的長度,nmemb給出要傳輸的記錄的個數。函數的返回值是成功讀到數據緩衝區裏的記錄的個數(不是字節數)。
14)fwrite()從指定的數據緩衝區裏取出數據記錄,並把它們寫到輸出流中。它的返回值是成功寫入的記錄個數。函數原型如下:
#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb,
FILE *stream);
15) fclose()函數用於關閉指定的文件流。
#include <stdio.h>
int fclose(FILE *fp);
16)  fseek()用於在文件流裏爲下一次讀寫操作指定位置。
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);  
offset和whence參數的含義和取值與lseek函數完全一樣,但lseek返回的是一個off_t數值,而fseek返回的是一個整數:0表示成功,-1表示失敗並設置errno指出錯誤。
17) fgetc()從文件流裏取出下一個字節並把它作爲一個字符返回。當它到達文件結尾或出現錯誤時,返回EOF。getc()和fgetc()一樣,但它有可能被實現爲一個宏。getchar()相當於getc(stdin)。
#include <stdio.h>
int fgetc(FILE *stream);
int getc(FILE *stream);
int getchar(void);
18) fputc()把一個字符寫到一個輸出文件流中,它返回寫入的值,如果失敗,則返回EOF。類似fgetc()和getc(),putc()的作用也相當於fputc(),但它可能被實現爲一個宏。putchar()相當於putc(c, stdout),它把單個字符寫到標準輸出。
#include <stdio.h>
int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
int putchar(int c);
注意:putchar和getchar都是把字符當作int類型而不是char類型來使用的,這就允許文件結尾EOF取值爲-1。

發佈了75 篇原創文章 · 獲贊 45 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章