第十章 系統級I/O
第七節 I/O重定向
I/O重定向操作符: >
ls > foo.txt
這句代碼的含義就是使外殼加載和執行ls程式,并且将标準輸出重定向到磁盤檔案foo.txt。
I/O重定向函數: dup2
傳回值:成功傳回描述符,錯誤傳回-1
這個函數執行的操作是,拷貝描述符表表項oldfd,覆寫描述表表項newfd,如果後者被打開,則在拷貝前關閉它。
第八節 标準I/O
1.标準I/O庫:
ANSI C定義了一組進階輸入輸出函數,稱為标準I/O庫,包含:
- fopen、fclose,打開和關閉檔案
- fread、fwrite,讀和寫位元組
- fgets、fputs,讀和寫字元串
- scanf、printf,複雜的格式化的I/O函數
2.流——類型為FILE的流是對檔案描述符和流緩沖區的抽象
标準I/O庫将一個打開的檔案模型化為一個流。
每個ANSI C程式開始的時候都有三個打開的流:stdin、stdout、stderr,對應于标準輸入、标準輸出和标準錯誤 。
第九節 套接字
網絡套接字上最好不要使用标準I/O函數,而是使用RIO函數,原因:
如果沒有清楚緩存區,輸入函數後面不能接輸出函數,輸出函數後面也不能接輸入函數,而對套接字使用lseek是非法的,打開兩個流有很麻煩,是以!在網絡套接字上不要使用标準I/O函數來進行輸入和輸出!
錯誤處理
附錄A中主要講了這本書中的錯誤處理方式,有一個方法——錯誤處理包裝函數,這個思想很有意思,相當于給基本函數再套上一層皮,然後run這個皮,發現了錯誤就終止,完全正确的話就跟沒有這層皮一樣。
1.錯誤處理風格
(1)Unix風格
遇到錯誤後傳回-1,并且将全局變量errno設定為指明錯誤原因的錯誤代碼;
如果成功完成,就傳回有用的結果。
(2)Posix風格
傳回0表示成功,傳回非0表示失敗;
有用的結果在傳進來的函數參數中。
(3)DNS風格
有兩個函數,gethostbyname和gethostbyaddr,失敗時傳回NULL指針,并設定全局變量h_errno。
2.錯誤處理包裝函數
Unix風格
成功時傳回void,傳回錯誤時包裝函數列印一條資訊,然後退出。
Posix風格
成功時傳回void,錯誤傳回碼中不會包含有用的結果。
DNS風格
參考資料
1.《深入了解計算機系統》
實踐代碼
stdio.h 标準輸入輸出
stdlib.h C标準函數庫
unistd.h Unix類系統定義符号常量
fcntl.h 定義了很多宏和open,fcntl函數原型
sys/types.h 基本系統資料類型
dirent.h unix類目錄操作的頭檔案,包含了許多UNIX系統服務的函數原型,例如opendir函數、readdir函數。
termios.h 在Posix規範中定義的标準接口
cp
if ((in_fd = open(argv[1], O_RDONLY)) == -1)
oops("Cannot open ", argv[1]);
if ((out_fd = creat(argv[2], COPYMODE)) == -1)
oops("Cannot creat", argv[2]);
while ((n_chars = read(in_fd, buf, BUFFERSIZE)) > 0)
if (write(out_fd, buf, n_chars) != n_chars)
oops("Write error to ", argv[2]);
if (n_chars == -1)
oops("Read error from ", argv[1]);
cp的參數有兩個,分别是要複制的檔案,和目的目錄,檢查cp的第一個參數,即要複制的檔案,用open打開,in_fd為open傳回的描述符,如果傳回-1,代表打開失敗,提示錯誤。檢查cp的第二個參數,複制的目的位址,用create在目的位址建立新檔案,out_fd為open傳回的描述符,如果傳回-1,代表建立失敗,提示錯誤。cp指令的動作就是讀取一個檔案的内容到存儲器,在新的位址建立空白檔案,再從存儲器将内容寫入新檔案。
ls(1)
void do_ls( char dirname[] )
{
DIR *dir_ptr;
struct dirent *direntp;
if ( ( dir_ptr = opendir( dirname ) ) == NULL )
fprintf(stderr,"ls1: cannot open %s\n", dirname);
else
while ( ( direntp = readdir( dir_ptr ) ) != NULL )
printf("%s\n", direntp->d_name );
closedir(dir_ptr);
}
Ls函數打開一個檔案夾*dir,然後将其中的其中包含的檔案名輸出。不輸入檔案夾名字時預設為目前檔案夾。
who
這個函數是,從UTMP_FILE檔案中讀取想要的資訊到存儲器中,然後再用标準輸出函數列印到螢幕上,最後關閉檔案。
echostate 、setecho
這個代碼是用來檢查指令行中的提示符是否顯示的,如果顯示,輸入的指令都可見,不顯示則表示輸入的指令不可見,結合setecho代碼一起。
info.c_lflag |= ECHO ;/*打開提示符*/
info.c_lflag &= ~ECHO ;/*隐藏提示符*/
fileinfo
這個功能用來實作顯示檔案資訊,建立了一個stat資料結構。
void show_stat_info(char *fname, struct stat *buf)
printf(" mode: %o\n", buf->st_mode);
printf(" links: %d\n", buf->st_nlink);
printf(" user: %d\n", buf->st_uid);
printf(" group: %d\n", buf->st_gid);
printf(" size: %d\n", (int)buf->st_size);
printf("modtime: %d\n", (int)buf->st_mtime);
printf(" name: %s\n", fname );
filesize
用st_size成員來計算檔案的位元組數大小。
spwd
void inum_to_name(ino_t inode_to_find , char *namebuf, int buflen)
dir_ptr = opendir( "." );
if ( dir_ptr == NULL ){
perror( "." );
exit(1);
if ( direntp->d_ino == inode_to_find )
strncpy( namebuf, direntp->d_name, buflen);
namebuf[buflen-1] = '\0';
closedir( dir_ptr );
return;
fprintf(stderr, "error looking for inum %d\n", (int) inode_to_find);
列出目前所在目錄
testioctl
列出檔案的行數與列數。
printf("%d rows %d columns\n", size.ws_row, size.ws_col);
return 0;
編譯完成後的檔案清單:
a
a.out
cp1.c
cp1
echostate.c
error
fileinfo.c
filesize.c
foo.txt
ls1
ls1.c
ls2
ls2.c
setecho.c
spwd.c
test
testioctl.c
who1.c
who1
who2.c
who2