1.5 輸入和輸出
文件描述符
文件描述符通常是小的且非負(fù)的整數(shù),內(nèi)核使用它來(lái)標(biāo)識(shí)能被特定進(jìn)程訪問(wèn)的文件。只要打開(kāi)或創(chuàng)建了新文件,內(nèi)核就返回一個(gè)文件描述符,我們使用它來(lái)讀寫(xiě)文件。
標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤
通常,一旦一個(gè)新的程序運(yùn)行,所有的shell就為其打開(kāi)三個(gè)描述符:標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤。如果不做什么特別的事,就像下面這樣一條簡(jiǎn)單的命令:
ls
那么所有的三個(gè)描述符都連接到終端。多數(shù)shell提供了一種方式來(lái)重定向三個(gè)描述符到文件。例如:
ls > file.list
執(zhí)行ls命令,并把它的標(biāo)準(zhǔn)輸出重定向到名為file.list的文件。
無(wú)緩沖I/O
無(wú)緩沖I/O由open函數(shù),read函數(shù),write函數(shù),lseek函數(shù)和close函數(shù)提供。這些函數(shù)都需要文件描述符才能工作。
例子
如果我們想要從標(biāo)準(zhǔn)輸入讀取,從標(biāo)準(zhǔn)輸出寫(xiě)入,那么圖1.4中的程序就可以拷貝UNIX系統(tǒng)中的常規(guī)文件。
apue.h中包含的<unistd.h>頭文件,以及兩個(gè)常量STDIN_FILENO和STDOUT_FILENO都是POSIX標(biāo)準(zhǔn)(下章中將會(huì)詳細(xì)介紹)的一部分。在該頭文件中有許多UNIX系統(tǒng)服務(wù)的函數(shù)的原型,例如我們調(diào)用的read和write函數(shù)。
常量STDIN_FILENO和STDOUT_FILENO在<unistd.h>中定義,它們指定了標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出的文件描述符。通常,STDIN_FILENO和STDOUT_FILENO的值分別為0和1,然而,在便攜設(shè)備開(kāi)發(fā)中,我們使用新的名字來(lái)表示它們。
3.9節(jié)中會(huì)詳細(xì)討論BUFFSIZE常量,將看到它的不同值是如何影響一個(gè)程序的效率的。盡管如此,無(wú)論BUFFSIZE為何值,圖1.4中的程序都會(huì)拷貝常規(guī)文件。
read函數(shù)返回讀取的字節(jié)數(shù),這個(gè)值被用來(lái)作為要寫(xiě)入的字節(jié)數(shù)。當(dāng)?shù)竭_(dá)輸入文件的末尾時(shí),read函數(shù)返回0,程序停止。如果讀取時(shí)發(fā)生錯(cuò)誤,read函數(shù)返回-1。多數(shù)系統(tǒng)函數(shù)在發(fā)生錯(cuò)誤時(shí)返回1。
如果像下面這樣按標(biāo)準(zhǔn)名字(a.out)編譯我們的程序
./a.out > data
標(biāo)準(zhǔn)輸入就是終端,而標(biāo)準(zhǔn)輸出被重定向到文件data,同時(shí)標(biāo)準(zhǔn)錯(cuò)誤也是終端。如果這個(gè)輸出文件不存在,那么shell將默認(rèn)創(chuàng)建它。除非輸入終止符(通常是Control-D),程序?qū)⒖截愝斎氲綐?biāo)準(zhǔn)輸出。
如果我們運(yùn)行
./a.out < infile > outfile
那么文件finfile將被拷貝到文件outfile。

圖 1.4 列出目錄中的所有文件
第三章中將詳細(xì)描述無(wú)緩沖I/O函數(shù)。
標(biāo)準(zhǔn)I/O
標(biāo)準(zhǔn)I/O函數(shù)為無(wú)緩沖I/O函數(shù)提供了緩沖接口。使用標(biāo)準(zhǔn)I/O可以防止在選擇合適緩沖大小的時(shí)候出現(xiàn)錯(cuò)誤,例如圖1.4中的BUFFSIZE常量。使用標(biāo)準(zhǔn)I/O函數(shù)的另一個(gè)優(yōu)點(diǎn)是其僅僅處理輸入的行(UNIX應(yīng)用程序中的通常事件)。例如fgets函數(shù),它讀取整個(gè)一行。另一方面,read函數(shù)讀取指定的字節(jié)數(shù)。就像將5.4節(jié)中看到的那樣,標(biāo)準(zhǔn)I/O庫(kù)提供的函數(shù)讓我們能夠控制緩沖的使用風(fēng)格。
最常見(jiàn)的標(biāo)準(zhǔn)I/O函數(shù)就是printf。如果在程序中調(diào)用printf函數(shù),可以通過(guò)包含apue.h來(lái)包含<stdio.h>頭文件,<stdio.h>頭文件中包含了所有標(biāo)準(zhǔn)I/O函數(shù)的原型。
例子
圖1.5中的程序會(huì)在5.8節(jié)中詳細(xì)討論。該程序像前面的程序那樣調(diào)用read何write函數(shù)。該程序拷貝標(biāo)準(zhǔn)輸入到標(biāo)準(zhǔn)輸出,同時(shí)也能夠拷貝任何常規(guī)文件。
getc函數(shù)一次讀取一個(gè)字符,該字符被putc函數(shù)寫(xiě)入。在最后一個(gè)輸入的字節(jié)被讀取后,getc返回常量EOF(<stdio.h>中定義)。標(biāo)準(zhǔn)I/O常量stdin和stdout也在<stdio.h>頭文件中定義,它們分別表示標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出。

圖 1.5 使用標(biāo)準(zhǔn)I/O拷貝標(biāo)注輸入到標(biāo)注輸出