Linux編程基礎——檔案讀寫

來源:互聯網
上載者:User

緩衝I/O和非緩衝I/O

檔案讀寫主要牽涉到了如下五個操作:開啟、關閉、讀、寫、定位。在Linux系統中,提供了兩套API,一套是C標準API:fopen、fclose、fread、fwrite、fseek,另一套則是POSIX定義的系統API:open、close、read、write、seek。

其中POSIX定義的API是系統API,而C標準API是基於系統API的封裝,並且提供了額外的緩衝的功能。因此也可以把它們叫做緩衝I/O函數和非緩衝I/O函數。

除了前面介紹的這幾個緩衝IO函數外,C標準庫<stdio.h>裡面還提供了一系列封裝的IO函數:如puts、putchar、printf等。

為什麼要有增加緩衝區這個功能呢?主要是因為IO操作時,作業系統要從使用者態轉換為核心態的,而這個轉換過程相對來說比較慢,因此可以通過緩衝的形式減少轉換到核心態的次數。那麼,緩衝IO函數又是如何工作的呢?

  1. 當用fopen開啟檔案時,除了分配檔案控制代碼外,還額外申請了一個緩衝區。
  2. 讀檔案時,會首先讀到緩衝區中,然後返回使用者需要的部分,多餘的部分仍然放在緩衝區,下次再讀的時候可以直接從緩衝區中返回。
  3. 寫檔案時,會先寫到緩衝區中,等緩衝區滿後再統一寫到檔案中。

那麼,我們該如何選擇哪一組I/O函數呢?

非緩衝I/O函數每次讀寫都要進核心,調一個系統調用比調一個使用者空間的函數要慢很多,所以在使用者空間開闢I/O緩衝區還是必要的。

  • 用緩衝I/O庫函數要時刻注意I/O緩衝區和實際檔案有可能不一致,在必要時需調用fflush()。
  • I/O函數也用於讀寫裝置,比如終端或網路裝置。此時通常需要更快的響應,一般不使用緩衝I/O函數。

PS:嚴格來講,就算是POSIX的I/O函數,仍然是有核心I/O緩衝的,所以write也不一定是直接寫到檔案的,也可能寫到核心I/O緩衝區中,至於究竟寫到了檔案中還是核心緩衝區中對於進程來說是沒有太大差別的,我們不用太關注這一點。

阻塞I/O和非阻塞I/O

檔案讀寫通常有阻塞和非阻塞兩種方式,其中阻塞方式是我們比較常見的一種方式,此時函數會阻塞至操作完成。例如,對於如下一個等待使用者輸入字串,並在螢幕上輸出的例子:

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

    int main(void)
    {
        char buf[10];
        int n = read(STDIN_FILENO, buf, 10);
        write(STDOUT_FILENO, buf, n);
        return 0;
    }

執行該函數時,read函數會一直阻塞到在螢幕上輸入資料並斷行符號(此時STDIN有資料可用)為止。

阻塞IO有一個很大的問題是:無法實現並發。當同時進行多個IO操作的時候,前面的檔案資料停用時候(往往是Socket之類的IPC操作),後面的IO操作無法執行。

非阻塞IO則可以很好的解決這個問題,要使用非阻塞IO操作,需要在open的時候制定O_NONBLOCK標誌。這樣,如果裝置暫時沒有資料可讀就返回-1,調用者應該試著再讀一次(again)。這種行為方式稱為輪詢(Poll),調用者只是查詢一下,而不是阻塞在這裡死等,這樣可以同時監視多個裝置:

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

    int main(void)
    {
        char buf[10];
        int fd, n;

        fd = open("/dev/tty", O_RDONLY|O_NONBLOCK);

        while (1)
        {
            n = read(fd, buf, 10);
            if (n >= 0)
                break;

            sleep(1);
        }

        write(STDOUT_FILENO, buf, n);
        close(fd);
        return 0;
    }

PS:為了樣本函數簡單,我這裡沒有考慮異常情況(如open失敗)的處理,而這些是在實際項目中是必不可少的。

非阻塞I/O有一個缺點,如果所有裝置都一直沒有資料到達,調用者則需要反覆查詢,這樣會一直佔著cpu不放。因此,在使用非阻塞I/O時,通常不會在一個while迴圈中一直不停地查詢(這稱為Tight Loop),而是每延遲等待一會兒來查詢一下,以免做太多無用功,在延遲等待的時候可以調度其它進程執行。

但是,這樣又引入了一個新的問題,可能導致資料讀取的不夠及時,就拿我前面的例子來說,我在每次迴圈的時候Sleep了一秒。如果剛開始Sleep的時候資料可用,但此時卻無法立即響應,需要到Sleep結束後鐘才能輸出結果。

要解圓滿解決這個問題,則需要用到select函數,它可以阻塞地同時監視多個裝置,還可以設定阻塞等待的逾時時間,由於select多見於socket編程情境,這裡不大好舉例,後續如果會介紹socket編程的時候再詳細介紹它,要瞭解它的工作原理可以看一下這篇文章select,多路同步I/O模型。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.