1. 程式人生 > ># 2018-2019-1 20165317 第六周學習總結

# 2018-2019-1 20165317 第六周學習總結

適用於 sock 抽象 共享文件 應用程序 一個表 應用 讀取文本 定義

2018-2019-1 20165317 第六周學習總結

教材學習內容總結

輸入/輸出(I/O)是主存和外部設備(I/O設備)(如磁盤驅動器、終端、網絡)之間拷貝數據的過程。輸入是從I/O設備拷貝到主存。反之則反。

10.1Unix I/O

  • Unix文件就是一個m字節的序列:b0,b1,b2….bm-1。所有的I/O設備都被虛擬化為文件。所有的輸入輸出都是在當成相對應的文件的讀寫。將設備映射為文件,Unix內核引出一個應用接口,Unix I/O。

  • 輸入輸出的執行方式:

    • 打開文件:打開文件,內核會返回描述符。標準輸入(STDIN_FILENO)描述符為0、標準輸出(STDOUT_FILENO)描述符為1、標準錯誤(STDERR_FILENO)描述符為2。也就是說在Unix生命周期一開始,0、1、2就被占用,以後的open只能從3開始——習題10.1

    • 改變當前文件位置:文件位置k,是文件開頭起始的字節偏移量。

    • 讀寫文件:讀是從文件拷貝到存儲器。寫相反。當k超過文件字節數m時,會觸發end-of-file(EOF)條件。

    • 關閉文件:釋放文件打開時創建的數據結構(釋放文件的存儲器資源),將描述符恢復到可用的描述符池中。

10.2打開和關閉文件

  • 1.open函數

(1)函數定義:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

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

(2)參數解析:

返回值:類型為int型,返回的是描述符數字,總是在進程中當前沒有打開的最小描述符。如果出錯,返回值為-1.
  • filename:文件名

  • flags:指明進程打算如何訪問這個文件,可以取的值見下:

    O_RDONLY:只讀
    O_WRONLY:只寫
    O_RDWR:可讀可寫
    O_CREAT:文件不存在,就創建新文件
    O_TRUNC:如果文件存在,就截斷它
    O_APPEND:寫操作前設置文件位置到結尾處
    這些值可以用“或”連接起來。

  • mode:指定了新文件的訪問權限位,符號名稱如下:

技術分享圖片
      

  • 2.close函數

(1)函數定義:

#include <unistd.h>

int close(int fd);

(2)參數解析:

返回值:成功返回0,出錯返回-1

關閉一個已經關閉的描述符會出錯

fd:即文件的描述符。

10.3讀和寫文件

  • 1.讀 read

(1)函數原型:

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t n);//返回有符號值

(2)參數解析:

返回值:成功則返回讀的字節數,EOF返回0,出錯返回-1。返回值為有符號數。
fd:文件描述符
buf:存儲器位置
n:最多從當前文件位置拷貝n個字節到存儲器位置buf
  • 2.寫 write

(1)函數原型:

#include <unistd.h>

ssize_t write(int fd, void *buf, size_t n);

(2)參數解析:

返回值:成功則返回寫的字節數,出錯返回-1。返回值為有符號數。
fd:文件描述符
buf:存儲器位置
n:最多從存儲器位置buf拷貝n個字節到當前文件位置
  • 需要註意的是,read和write在正常情況下返回值是實際傳送的字節數量。

  • 3.不足值

      不足值指在某些情況下,read和write傳送的字節比應用程序要求的要少,原因如下:
      讀的時候遇到EOF
      從終端讀文本行
      讀和寫socket

10.4用RIO包健壯地讀寫

  • RIO包能自動地處理不足值。提供了兩個函數:無緩沖的輸入輸出函數,帶緩沖的輸入函數。

  • RIO的無緩沖的輸入輸出函數

這些函數的作用是直接在存儲器和文件之間傳送數據,常適用於網絡和二進制數據之間。

rio_readn函數和rio_writen定義:

#include "csapp.h"

ssize_t rio_readn(int fd, void *usrbuf, size_t n);
ssize_t rio_writen(int fd, void *usrbuf, size_t n);

參數:

fd:文件描述符
usrbuf:存儲器位置
n:傳送的字節數

返回值:

rio_readn成功則返回傳送的字節數,EOF為0(一個不足值),出錯為-1
rio_writen成功則返回傳送的字節數,出錯為-1,沒有不足值。
  • RIO的帶緩沖的輸入函數

    • 可以高效的從文件中讀取文本行和二進制數據。

    • 文本行就是一個由換行符結尾的ASCII碼字符序列。換行符數字值為0x0a.rio_readlineb函數從內部讀緩沖區拷貝一個文本行,當緩沖區為空時,會自動地調用read重新填滿緩沖區。rio_readn帶緩沖區的版本:rio_readnb。

    • rio_readlineb從rp讀出一個文本行(包括換行符)並把它存到usrbuf,並用空字符結束這個文本行。最多讀maxlen-1個字節,剩下一個給結尾處的空字符。

    • rio_readnb最多讀n個字節。

10.5讀取文件元數據

  • 檢索文件元數據的方式:調用stat和fstat函數。兩者功能相似。
    • stat數據結構中的成員。重點掌握st_mode,st_size.

       st_size:文件字節數大小
      
       st_mode:文件訪問許可位和文件類型。(普通文件:二進制文件和文本文件。目錄文件:其他文件信息。套接字:通過網絡與其他進程通信的文件。)

10.6共享文件

  • 用三個相關的數據結構表示打開的文件:

    • 描述符表:每個打開的描述符表項指向文件表中的一個表項。

    • 文件表:打開的文件的集合是由一張文件表表示的,所有的進程共享這張表。包括文件位置、引用計數(當前指向該表項的描述符表項數),指向v-node表的指針。

    • v-node表:包含stat結構中大多數信息。

      打開文件的內核數據結構:
      技術分享圖片

      文件共享。共享了同一個磁盤文件:
      技術分享圖片

    子父進程共享文件。子進程有一個父進程描述表符副本,所以他們共享打開文件的集合。註意,在內核刪除相應文件表表項之前,子父進程必須都關閉他們的描述符。
    技術分享圖片

10.7 I/O重定向

  • I/O重定向操作符: >

      ls > foo.txt

這句代碼的含義就是使外殼加載和執行ls程序,並且將標準輸出重定向到磁盤文件foo.txt。

  • I/O重定向函數: dup2

    函數定義為:

      #include <unistd.h>
    
      int dup2(int oldfd, int newfd);

返回值:成功返回描述符,錯誤返回-1

- 這個函數執行的操作是,拷貝描述符表表項oldfd,覆蓋描述表表項newfd,如果後者被打開,則在拷貝前關閉它。

10.8 標準I/O

標準I/O庫:一組高級輸入輸出函數。將一個打開的文件模型化為一個流,一個流即一個指向FILE類型的結構的指針。

每個ANSI C程序開始時都有三個打開的流:stdin(標準輸入),stdout(標準輸出),stderr(標準錯誤)。

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

10.9 綜合:該使用哪些I/O函數

在網絡套接字的時候使用RIO函數。需要格式化輸出,使用sprintf函數格式化一個字符串,然後用rio_writen把它發送到套接口。

格式化輸入,使用rio_readlineb讀一個完整的文本行,再使用scanf從文本行提取不同字段。

# 2018-2019-1 20165317 第六周學習總結