天天看點

5233楊光--第十章

Unix I/O

所有的I/O裝置,如網絡、磁盤都被模型化為檔案,而所有的輸入和輸出都被當做對相應檔案的讀和寫來執行。這種将裝置映射為檔案的方式,允許UNIX核心引出一個簡單、低級的應用接口,稱為UNIX I/O。

    輸入輸出的執行方式:

      打開檔案:

        打開檔案,核心會傳回描述符。标準輸入(STDIN_FILENO)描述符為0、标準輸出(STDOUT_FILENO)描述符為1、标準錯誤(STDERR_FILENO)描述符            為2。

    改變目前檔案位置:

        檔案位置k,是檔案開頭起始的位元組偏移量。

     讀寫檔案:

        讀是從檔案拷貝到存儲器。寫相反。當k超過檔案位元組數m時,會觸發end-of-file(EOF)條件。

    關閉檔案:

        釋放檔案打開時建立的資料結構(釋放檔案的存儲器資源),将描述符恢複到可用的描述符池中。

打開和關閉檔案

    程序通過open函數來打開一個已存在的檔案或者建立一個新檔案的。

    int open(char *filename, int flags, int mode);

    flags參數指明了程序打算如何通路這個檔案:

    O_RDONLY:隻讀

    O_WRONLY:隻寫

    O_RDWR:可讀可寫

    O_CREAT:如果檔案不存在,就建立它的一個截斷的(空)檔案。

    O_TRUNC:如果檔案已經存在,就截斷它。

    O_APPEND:在每次寫操作前,設定檔案位置到檔案的結尾處。

    當程序通過帶某個mode參數的open函數調用來建立一個新檔案時,檔案的通路權限位被設定為mode & ~umask。

    程序通過調用close關閉一個打開的檔案。

讀和寫檔案

    傳回值-1表示一個錯誤,而傳回值0表示EOF。否則,傳回值表示的時實際傳送的位元組數量。

    include <unistd.h>

      ssize_t read(int fd, void *buf, size_t n);   //傳回:若成功則為0,若出錯則為-1.

    寫檔案

      write函數從儲存器位置buf拷貝至多n個位元組到描述符fd到目前位置。

    include

      ssize_t write(int fd, const void *buf, size_t n);   //傳回:若成功則為寫的位元組數,若出錯則為-1.

    讀時遇到EOF

      假設該檔案從目前檔案位置開始隻含有20個位元組,而應用程式要求我們以50個位元組的片進行讀取,這樣一來,這個read的傳回的值是20,在此之後的read則傳回0.

    從終端讀文本行

      如果打開的檔案是與終端相關聯的,那麼每個read函數将一次傳送一個文本行,傳回的不足值等于文本行的大小。(具體的含義可看我以前的文章,關于緩沖區的)

    讀和寫網絡套接字(socket)

      如果打開的檔案對應于網絡套接字,那麼内部緩沖限制和較長的網絡延遲會導緻read和write傳回不足值。

讀和寫檔案 598

    read 執行輸入(讀)

    write執行輸出(寫)

    read 函數:從fd的目前檔案位置 拷貝 最多n個位元組到存儲器位置 buf。

       傳回值-1表示錯誤,0表示EOF,否則表示實際傳送的位元組數。

       write 同理。

    不足值:read和write傳送的位元組數少于應用程式的要求。

    讀時遇到了EOF

    從終端讀文本行:如果打開的檔案是與終端相聯的,那麼每個read函數将一次傳送一個文本行,不足值=文本行大小。

       讀和寫網絡套接字。

用rio包健壯地讀寫 599

    rio_readn函數從描述符fd的目前檔案位置最多傳送n個位元組到存儲器位置usrbuf。類似的rio_writen函數從位置usrbuf傳送n個位元組到描述符fd。rio_readn函數在遇到EOF時隻能傳回一個不足值。rio_writen函數絕不會傳回不足值。

    注意:如果rio_readn和rio_writen函數被一個從應用信号處理程式的傳回中斷,那麼每個函數都會手動地重新開機read或write。

    一個文本行就是一個由 換行符 結尾的ASCII碼字元序列。在Unix系統中,換行符是‘\n’,與ASCII碼換行符LF相同,數值為0x0a。假設我們要編寫一個程式來計算文本檔案中文本行的數量應該如何來實作呢?

    一種方法是用read函數來一次一個位元組地從檔案傳送到使用者存儲器,檢查每個位元組來查找換行符。

    這種方法的問題就是效率不高,每次取檔案中的一個位元組都要求陷入核心。

    一種更好的方法是調用一個包裝函數(rio_readlineb),它從一個内部緩沖區拷貝一個文本行,當緩沖區變空時,會自動的調用read系統調用來重新填滿緩沖區。

讀取檔案中繼資料

    應用程式能通過調用stat和fstat函數,檢索到關于檔案的資訊(中繼資料)。stat函數以一個檔案名作為輸入,fstat函數以檔案描述符作為輸入。

    st _ size成員包含了檔案的位元組數大小。st _ mode成員則編碼了檔案通路許可位和檔案類型。

    普通檔案包括某種類型的二進制或文本資料。

    目錄檔案包含關于其他檔案的資訊。

    套接字是一種用來通過網絡與其他程序通信的檔案。

共享檔案

    核心用三個相關的資料結構來表示其打開的檔案。

    描述符表:表項由程序打開的檔案描述符來索引的,每個打開的描述符表指向檔案表中的一個表項,每個程序有其獨立的描述符表。

    檔案表:打開檔案的集合是由一張檔案表來表示的,所有的程序共享這張表。包括:目前的檔案位置、引用計數、以及一個指向v-node表中對應表項的指針。

    v-node表:每個表項包含stat結構中的大多數資訊,;包括st_mode和st_size成員,所有程序共享。

标準I/O

    标準I/O庫:一組進階輸入輸出函數。将一個打開的檔案模型化為一個流,一個流即一個指向FILE類型的結構的指針。每個ANSI C程式開始時都有三個打開的流:stdin(标準輸入),stdout(标準輸出),stderr(标準錯誤)。

    類型為FILE的流是對檔案描述符和流緩沖區的抽象。為了減小系統開銷。

    大多數時候使用标準I/O就可以了。

    在網絡套接字的時候使用RIO函數。需要格式化輸出,使用sprintf函數格式化一個字元串,然後用rio_writen把它發送到套接口。格式化輸入,使用rio_readlineb讀一個完整的文本行,再使用scanf從文本行提取不同字段。

問題:

    使用課本上定義的csapp.h編譯出現錯誤。。。。

           後來聽說是因為那個就是錯的,對的要自己找