從 0 開始學(xué)習(xí) Linux 系列之「12.必須掌握的 5 個(gè)底層 IO 函數(shù)」

LowIO

版權(quán)聲明:本文為 cdeveloper 原創(chuàng)文章,可以隨意轉(zhuǎn)載闷袒,但必須在明確位置注明出處庐舟!

底層輸入輸出(Low-Level Input/Output)

這篇博客主要介紹 Linux 原生的 IO 操作(Low IO)滞造,你可能會(huì)想不是有跨平臺(tái)的 ANSI C 可以使用么河胎,為啥還要學(xué)習(xí)底層 IO ? 有以下 4 個(gè)原因:

  1. 用于讀取大塊的二進(jìn)制文件
  2. 在解析之前將整個(gè)文件讀入內(nèi)核
  3. 執(zhí)行數(shù)據(jù)傳輸以外的操作
  4. 將描述符傳遞給子進(jìn)程

你需要知道這 4 個(gè)使用底層 IO 的原因讳嘱,在以后遇到實(shí)際的情況時(shí)能夠想到利用底層 IO 來解決幔嗦。因?yàn)榈讓虞斎胼敵龅暮瘮?shù)也有很多,這篇博客主要介紹 5 個(gè)最常用沥潭,最基本的底層 IO 函數(shù)

  1. 打開文件:open
  2. 關(guān)閉文件:close
  3. 讀取文件:read
  4. 寫入文件:write
  5. 操作文件指針:lseek

如果你還有興趣學(xué)習(xí)其他的底層 IO 函數(shù)邀泉,建議你查看 glibc 的官方底層 IO 的學(xué)習(xí)資料,那是最好钝鸽,最權(quán)威的資料汇恤,下面就一起來看看這 5 個(gè)函數(shù)的用法。

open & close

我們操作 IO 首先要學(xué)會(huì)的就是打開和關(guān)閉文件寞埠,我們使用 openclose 這兩個(gè)函數(shù)屁置,他們的聲明如下(man 2 open):

open

// open 需要 3 個(gè)頭文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

/*
 * filename: 要打開的文件名稱
 * flags: 打開標(biāo)記,例如:O_CREAT | ORDWR 表示文件不存在就創(chuàng)建仁连,并且可讀可寫
 * mode: 打開權(quán)限
 * return: 成功蓝角,返回一個(gè)新的文件描述符 fd,失敗返回 -1饭冬,并設(shè)置 errno
 */
int open (const char *filename, int flags, mode t mode);

打開文件也可以用 create使鹅,不過這個(gè)函數(shù)已經(jīng)棄用了!

close

再來看看 close昌抠,關(guān)閉文件會(huì)有如下結(jié)果:

  1. 文件描述符被取消分配
  2. 文件上進(jìn)程所擁有的任何記錄鎖都將被解鎖
  3. 當(dāng)與管道或 FIFO 相關(guān)聯(lián)的所有文件描述符都已關(guān)閉時(shí)患朱,任何未讀數(shù)據(jù)被丟棄
#include <unistd.h>

/*
 * fd: 要關(guān)閉的文件的描述符
 * return: 成功返回 0,失敗返回 -1炊苫,并設(shè)置 errno
 */
int close(int fd);

實(shí)例 1: open_close.c

我們來看一個(gè)簡(jiǎn)單的例子:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc, char* argv[]) {
    if (argc < 2) {
        printf("please input filename:\n");
        exit(1);
    }

    // argv[1] = "1.txt", 打開的文件不存在就創(chuàng)建 | 可讀可寫裁厅,666(rw-rw-rw)
    int fd = open(argv[1], O_CREAT | O_RDWR, 666);

    if (fd < 0) {
        printf("file open fail.\n");
        exit(1);
    } else {
        printf("file open success.\n");
        // 必須關(guān)閉文件
        close(fd);
        printf("file closed.\n");
    }

    return 0;
}

編譯:

gcc open_close.c -o open_close

運(yùn)行,我們沒有新建 1.txt 文件:

./open_close 1.txt
file open success.
file close success.

文件打開成功侨艾,并且當(dāng)前目錄下也有了一個(gè) 1.txt 文件了执虹,說明我們指定的 O_CREAT 標(biāo)記使得程序建立了這個(gè)文件。這兩個(gè)函數(shù)基本用法就是這樣唠梨,更多更詳細(xì)的用法還需要你自己到 glibc 官網(wǎng)或者 man 2 open 去找袋励。

read & write

我們?cè)诖蜷_文件后肯定會(huì)做的就是讀寫文件了,不然你打開文件干嘛当叭,我們來看看讀文件的 API:

read

read 函數(shù)從文件描述符 filedes 指定的文件中讀取 size 個(gè)字節(jié)茬故,存儲(chǔ)到 buffer 中。

#include <unistd.h>

/*
 * filedes: 要讀取的文件描述符
 * buffer: 存儲(chǔ)讀取字節(jié)的緩沖區(qū)
 * size: 要讀取的大小
 * return: 成功返回讀取的字節(jié)數(shù)蚁鳖,失敗返回 -1磺芭,并設(shè)置 errno
 */
ssize_t read (int filedes, void *buffer, size t size);

write

write 函數(shù)從 buffer 中取出 size 個(gè)字節(jié)的數(shù)據(jù),寫到 filedes 描述符表示的文件中醉箕。

#include <unistd.h>

/*
 * filedes: 寫入的文件描述符
 * buffer: 存儲(chǔ)待寫入數(shù)據(jù)的緩存區(qū)
 * size: 要寫入的字節(jié)數(shù)
 * return: 成功返回寫入的字節(jié)數(shù)钾腺,失敗返回 -1甘邀,并設(shè)置 errno
 */
ssize_t write (int filedes, const void *buffer, size t size);

實(shí)例 2: read_write.c

來看一個(gè)簡(jiǎn)單的讀寫文件的例子:讀取 file1 的內(nèi)容,寫到 file2 中垮庐,相當(dāng)于文件拷貝

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char* argv[]) {
    // 1. 打開兩個(gè)文件
    int fd1 = open(argv[1], O_CREAT | O_RDWR, 0666);
    int fd2 = open(argv[2], O_CREAT | O_RDWR, 0666);

    if ((fd1 < 0) || (fd2 < 0)) {
        printf("file open fail.\n");
        exit(1);
    } else {
        printf("file1 open success: fd1 = %d\n", fd1);
        printf("file2 open success: fd2 = %d\n", fd2);

        char buf[1024];
        // clear
        memset(buf, 0, 1024);

        int read_length = 0;
        int write_length = 0;

        // 2. 將文件 1 的內(nèi)容讀取到 buf 中
        if ((read_length = read(fd1, buf, 1024)) != -1)  {
            // 3. 將 buf 的內(nèi)容寫入到文件 2 中
            if (read_length == write(fd2, buf, read_length)) 
                printf("write to fd2 success.\n");
            else 
                printf("write to fd2 falil.\n");
        } else {
            printf("read fd1 fail.\n");
        }
        
        // 必須關(guān)閉 2 個(gè)文件
        close(fd1);
        close(fd2);

        printf("file1 close success.\n");
        printf("file2 close success.\n");
    }

    return 0;
}

編譯:

gcc read_write.c -o read_write

運(yùn)行,1.txt 的內(nèi)容是 hello world

./read_write 1.txt 2.txt

file1 open success: fd1 = 3
file2 open success: fd2 = 4
write to fd2 success.
file1 close success.
file2 close success.

寫入成功坞琴,查看下 2.txt 的內(nèi)容:

cat 2.txt

hello world

發(fā)現(xiàn)成功寫入 hello world 到 2.txt 文件了哨查,并且注意到 fd1 = 3, fd2 = 4,這也說明前面的 3 個(gè)文件描述符被系統(tǒng)使用了剧辐。

lseek

lseek 用來移動(dòng)文件指針寒亥,什么是文件指針呢?你可以理解為當(dāng)前讀取或者寫入的位置荧关,我們移動(dòng)這個(gè)指針可以控制讀取或者寫入數(shù)據(jù)的位置溉奕,聲明如下:

#include <sys/types.h>
#include <unistd.h>

/*
 * filedes: 要操作的文件描述符
 * offset: 根據(jù)當(dāng)前 whence 的偏移量
 * whence: 指定當(dāng)前文件指針的位置
 * return: 成功返回設(shè)置后的文件位置,可以使用 `SEEK_CUR` 查看當(dāng)前文件指針位置忍啤,
 *         失敗返回 -1 并設(shè)置 errno
 */
off_t lseek (int filedes, off t offset, int whence);

其中 whence 參數(shù)需要特別注意加勤,它有 3 種情況:

  1. SEEK_SET:設(shè)置文件指針指向文件開始并偏移 offset 字節(jié)處
  2. SEEK_CUR:設(shè)置文件指針只想當(dāng)前位置偏移 offset 字節(jié)處
  3. SEEK_END:設(shè)置文件指針指向文件末尾偏移 offset 字節(jié)處

實(shí)例 3:file_length.c

我們可以使用 int file_length = lseek(fd, 0, SEEK_END)求文件的長(zhǎng)度,這個(gè)操作經(jīng)常被使用同波。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
    // 打開文件
    int fd = open(argv[1], O_CREAT | O_RDWR, 0666);

    if (fd < 0) {
        printf("file open fail.\n");
        exit(1);
    } else {
        printf("file1 open success: fd = %d\n", fd);

        // 得到文件長(zhǎng)度
        int file_length = lseek(fd, 0, SEEK_END);
        
        // 輸出文件長(zhǎng)度
        printf("file_length = %d\n", file_length);

        // 關(guān)閉文件
        close(fd);
        printf("file1 close success.\n");
    }

    return 0;
}

編譯:

gcc file_length.c -o file_length

運(yùn)行鳄梅,注意到 1.txt 中的 hello world 加上 '\0' 一共 12 個(gè)字符

./file_length 1.txt
file1 open success: fd = 3
file_length = 12
file1 close success.

打印出 12,計(jì)算正確啦未檩。

結(jié)語

到此戴尸,我們就學(xué)習(xí)了 5 個(gè)底層的 IO 函數(shù),并實(shí)際練習(xí)了 3 個(gè)例子冤狡,把這 3 個(gè)例子搞清楚孙蒙,基本的用法也就掌握的差不多了,更加詳細(xì)的用法可以查看系統(tǒng)提供的 man 手冊(cè) man 2 open 等悲雳,或者查閱 glibc 官方文檔挎峦,祝你學(xué)習(xí)愉快。

最后怜奖,感謝你的閱讀浑测,我們下次再見 :)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市歪玲,隨后出現(xiàn)的幾起案子迁央,更是在濱河造成了極大的恐慌,老刑警劉巖滥崩,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岖圈,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡钙皮,警方通過查閱死者的電腦和手機(jī)蜂科,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門顽决,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人导匣,你說我怎么就攤上這事才菠。” “怎么了贡定?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵赋访,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我缓待,道長(zhǎng)蚓耽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任旋炒,我火速辦了婚禮步悠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瘫镇。我一直安慰自己鼎兽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布汇四。 她就那樣靜靜地躺著接奈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪通孽。 梳的紋絲不亂的頭發(fā)上序宦,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音背苦,去河邊找鬼互捌。 笑死,一個(gè)胖子當(dāng)著我的面吹牛行剂,可吹牛的內(nèi)容都是我干的秕噪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼厚宰,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼腌巾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起铲觉,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤澈蝙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后撵幽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體灯荧,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年盐杂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了逗载。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哆窿。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖厉斟,靈堂內(nèi)的尸體忽然破棺而出挚躯,到底是詐尸還是另有隱情,我是刑警寧澤擦秽,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布秧均,位于F島的核電站,受9級(jí)特大地震影響号涯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜锯七,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一链快、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧眉尸,春花似錦域蜗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至袱蜡,卻和暖如春丝蹭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坪蚁。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工奔穿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人敏晤。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓贱田,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親嘴脾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子男摧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 文件操作 (Linux文件操作)) [文件|目錄] Linux文件操作:為了對(duì)文件和目錄進(jìn)程處理,你需要用到系統(tǒng)...
    JamesPeng閱讀 1,467評(píng)論 1 5
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法译打,類相關(guān)的語法耗拓,內(nèi)部類的語法,繼承相關(guān)的語法扶平,異常的語法帆离,線程的語...
    子非魚_t_閱讀 31,631評(píng)論 18 399
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,518評(píng)論 1 51
  • 版權(quán)聲明:本文為 cdeveloper 原創(chuàng)文章,可以隨意轉(zhuǎn)載结澄,但必須在明確位置注明出處哥谷! 標(biāo)準(zhǔn) IO 庫 上一篇...
    登龍zZ閱讀 643評(píng)論 0 3
  • 一岸夯、溫故而知新 1. 內(nèi)存不夠怎么辦 內(nèi)存簡(jiǎn)單分配策略的問題地址空間不隔離內(nèi)存使用效率低程序運(yùn)行的地址不確定 關(guān)于...
    SeanCST閱讀 7,813評(píng)論 0 27