20145320 《資訊保安系統設計基礎》第9周學習總結
教材學習內容總結
輸入/輸出(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從文字行提取不同欄位。
教材練習
10.1
- Unix程式生命週期開始時,開啟的描述符賦給了stdin(描述符0)、stdout(描述符1)和stderr(描述符2)。open函式總是返回最低的未開啟的描述符,所以第一次呼叫open會返回描述符3。呼叫close函式會釋放描述符3.最後對open的呼叫會返回描述符3,因此程式的輸出是“fd2=3”。
10.2
- 描述符fd1和fd2都要各自的開啟檔案表表項,所以每個描述符對於foobar.txt都有它自己的位置。因此,從fd2的讀操作會讀取foobar.txt的第一個自己,並輸出
- c=f ,而不是 c=o
10.3
- 這裡是子程式繼承父程式的描述符表,以及所有程式共享的同一個開啟檔案表。因此,描述符fd在父子程式中都指向同一個開啟檔案表表項。當子程式讀取檔案的一個位元組時,檔案位置加1.因此,父程式會讀取第二個位元組,而輸出就是
- c=o,而不是10.2中的 c=f
10.4
- 這裡考察的是重定向的內容,重定向標準輸入(描述符0)到描述符5,直接呼叫dup2(5,0)
10.5
- 這題考察重定向的應用,這裡把fd1重定向到了fd2,輸出應該為
- c=o