Linux編程學習筆記 | Linux IO學習[1] - 文件IO

系統調用與程序運行空間

在Linux操做系統中,爲了提升系統的穩定性,保證內核的安全,程序運行時的內存空間被分爲了用戶空間和內核空間。普通應用程序工做在用戶空間,不能直接訪問內核空間。它們須要使用Linux系統提供給用戶的一些"特殊接口" - 系統調用來安全地訪問內核空間。linux

要對文件進行讀寫就須要使用Linux系統提供的一些系統調用。在這篇文章中我主要介紹 open() , write() , read() , lseek()close() 等函數,在下文中我會詳細講解這些函數的使用。git

文件描述符

在Linux系統中,一切均可以被看做是文件,這包括:普通文件、目錄文件、連接文件和設備文件。要訪問文件,必須使用文件描述符。文件描述符是一個非負的整數,它是系統中被打開文件的索引。當打開或者建立一個文件時,內核會返回一個文件描述符;當須要讀寫文件時,也須要將相應的文件描述符做爲參數傳給讀寫函數。程序啓動時,默認有3個文件描述符:github

文件描述符 說明
0 STDIN_FILENO 標準輸入
1 STDOUT_FILENO 標準輸出
2 STDERR_FILENO 標準錯誤輸出

若是此時建立或打開一個文件,這個文件的文件描述符就是3.小程序

文件IO基本操做

打開/建立文件

open() 函數用於打開或者建立文件。其在打開或者建立文件時能夠指定文件的屬性及用戶的權限等各類參數。要使用 open() 函數,須要包含 #include <sys/stat.h>#include <fcntl.h> 這兩個頭文件。下面是函數的說明:安全

int open(const char *path, int oflag, [mode_t mode]);

args:
    const char *path: 文件路徑,能夠是絕對,也能夠是相對路徑 
    int oflag       : 文件打開的方式
                        - O_RDONLY 只讀打開
                        - O_WRONLY 只寫打開
                        - O_RDWR   可讀可寫打開
                        以上3種必選一個,如下4種能夠任意選擇
                        - O_APPEND 追加打開,所寫數據附加到文件末
                        - O_CREAT  若此文件不存在則建立它
                        - O_EXCL   若文件存在則報錯返回 
                        - O_TRUNC  若是文件已存在,而且以只寫或可讀可寫方式打開,則將其長度截斷爲0字節
    [mode_t mode]   : 文件權限,只有在建立文件時須要使用
    
return:
    文件描述符,非負整數是成功,-1是失敗

open() 函數中,文件的打開方式不止上面的幾種,這裏只列舉了經常使用的7種。注意,新建文件的權限不是直接等於 mode 的值,而是等於 mode & ~uname函數

寫文件

當文件打開後,咱們就能夠向該文件寫數據了。在Linux系統中,用 write() 向打開的文件寫入數據,要使用這個函數,須要包含 #include <unistd.h> 。下面是函數的說明:this

ssize_t write(int fildes, const void *buf, size_t nbyte);

args:
    int fildes     : 寫入文件的文件描述符
    const void *buf: 寫入數據在內存空間存儲的地址
    size_t nbyte   : 期待寫入數據的最大字節數
    
return:
    文件實際寫入的字節數,非負整數是成功,-1是失敗(磁盤已滿或者超出該文件的長度等)

注意函數的返回類型是 ssize_tssize_tsize_t 相似,只是 ssize_t 表示有符號數。想了解更多 size_tssize_t 的區別請看這篇文章spa

讀文件

同寫文件相似,要使用讀文件函數 read() ,須要包含 #include <unistd.h> 。下面是函數的說明:操作系統

ssize_t read(int fildes, void *buf, size_t nbyte);

args:
    int fildes  : 讀取文件的文件描述符
    void *buf   : 讀取數據在內存空間存儲的地址
    size_t nbyte: 期待讀取數據的最大字節數
    
return:
    文件實際讀取的字節數,非負整數是成功,-1是失敗

write() 同樣, read() 函數的返回類型也是 ssize_t.net

文件的偏移量

在每一個打開的文件中都有一個文件的偏移量,文件的偏移量會根據文件的讀寫而改變位置。咱們能夠經過 lseek() 函數來調整文件的偏移量。默認狀況下,新打開文件的文件偏移量在文件的開始。同 write()read() 函數相似,要使用這個函數,須要包含 #include <unistd.h> 。下面是函數的說明:

off_t lseek(int fildes, off_t offset, int whence);

args:
    int fildes  : 修改文件的文件描述符
    off_t offset: 文件偏移量移動的距離
    int whence  : 文件偏移量的基址
                    - SEEK_SET 文件開始處
                    - SEEK_CUR 文件當前位置
                    - SEEK_END 文件結束處
    
return:
    當前文件指針的位置,非負整數是成功,-1是失敗

off_tssize_t 相似,都是有符號數。

關閉文件

當文件再也不被使用時,能夠調用 close() 函數來關閉被打開的文件。
除了用 close() 顯示地關閉文件外,經過結束進程也能隱式地關閉被該進程打開的全部文件。要使用該函數,須要包含 #include <unistd.h> 。下面是函數的說明:

int close(int fildes);

args:
   int fildes: 要關閉文件的文件描述符
   
return:
    文件關閉狀態,0是成功,-1是失敗

文件IO實例

文件基本操做

這是一個簡單的文件基本操做實例。在這個例子中,程序分兩次將內存中的字符串寫入文件,而後又將文件內容讀回內存空間。

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

/**
 * This is a simple example for using open(), write(), read(), lseek() and close().
 */
int main(int argc, char *argv[])
{
    int fd;
    ssize_t wr_size, rd_size;
    char buffer[128];
    char string_1[30], string_2[30] = "This is the second line!\n";
    char *path = "./file_io.log"; 
    
    fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 511);
    if (fd < 0) {
        printf("File create fail...\n");
        return -1; 
    } else {
        printf("File create success...\n");
    }
    
    /* write the first line to file_io.log */
    strcpy(string_1, "This is a demo for file_io!\n");
    wr_size = write(fd, string_1, strlen(string_1));
    if (wr_size < 0) {
        printf("File write 1 fail...\n");
        printf("wr_size = %d\n", wr_size);
        return -1; 
    } else {
        printf("File write 1 success...\n");
        printf("wr_size = %d\n", wr_size);
    }
    
    /* write the second line to file_io.log 
     * in this case, we only write 10 bytes data from string_2 to file.
     */
    wr_size = write(fd, string_2, 10);
    /* add "\0"(not '\0'!!) to the end of the second line */ 
    wr_size = write(fd, "\0", 1);
    if (wr_size < 0) {
        printf("File write 2 fail...\n");
        printf("wr_size = %d\n", wr_size);
        return -1; 
    } else {
        printf("File write 2 success...\n");
        printf("wr_size = %d\n", wr_size);
    }
    
    /* decrease current file offset by 20 bytes */
    lseek(fd, -20, SEEK_CUR);

    rd_size = read(fd, buffer, 100); 
    if (rd_size < 0) {
        printf("File read_1 fail...\n");
        printf("rd_size = %d\n", rd_size);
        return -1; 
    } else {
        printf("File read_1 success...\n");
        printf("rd_size = %d,\nbuffer = %s\n", rd_size, buffer);
    } 

    close(fd);
    
    return 0; 
}

編譯並運行該程序,程序和文件輸出結果以下:
程序輸出結果
文件輸出結果

對於上面的例子,有幾點須要注意:
1) 在40行處, wr_size = write(fd, string_2, 10); 咱們寫入的字節數是小於 string_2 中的字節數的。若是想要寫入的字節數大於 string_2 中的字節數,那 string_2 外的字節也會寫入文件(這些額外的字節不是咱們但願要的)。好比咱們將40行改成 wr_size = write(fd, string_2, 100); 其輸出結果以下:
實際寫入的字節數大於須要寫入的字節數

2)若是註釋掉53行,則讀出的字節數爲0,由於此時文件的偏移量處於文件的尾部。
忘記修改文件偏移量

簡易版CP指令

這是一個模仿Linux cp指令的小程序,這裏並無考慮效率,也沒有考慮特殊狀況,只是簡單地實現其功能。

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

/* 
 * This a simple version of cp command. 
 */
int main(int argc, char *argv[])
{
    int fd1, fd2;
    ssize_t rd_size;
    char buffer[128];
    
    if (argc != 3) {
        printf("You should enter enter 2 parameters\n"); 
        return -1;
    }

    fd1 = open(argv[1], O_RDONLY);
    if (fd1 < 0) {
        printf("File %d does not exist...\n", fd1);
        return -1; 
    }
    fd2 = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 511); 
    if (fd2 < 0) {
        printf("File %d open fail...\n", fd2);
        return -1; 
    }

    while(read(fd1, buffer, 1))
        write(fd2, buffer, 1);

    close(fd1);
    close(fd2);
    
    return 0; 
}

編譯並運行該程序,程序輸出結果以下:
模仿CP指令

總結

這篇文章主要介紹瞭如何使用文件IO的系統調用函數對文件進行操做,文中出現的代碼均可在個人github上找到。

若是以爲本文對你有幫助,請多多點贊支持,謝謝!

相關文章
相關標籤/搜索