UNIX/LINUX,Linux操作系統-標準IO庫(1)

 2023-12-09 阅读 35 评论 0

摘要:Linux操作系統—標準I/O庫(1)(2015-8-3) 分類:Linux操作系統 ??不僅在linux,在很多操作系統上都實現了標準I/O庫,該庫由ANSI C標準說明。標準I/O庫是在系統調用函數基礎上構造的,它處理很多細節(例如緩存分配)以優化執行I/O。與基于

Linux操作系統—標準I/O庫(1)(2015-8-3)

分類:Linux操作系統

??不僅在linux,在很多操作系統上都實現了標準I/O庫,該庫由ANSI C標準說明。標準I/O庫是在系統調用函數基礎上構造的,它處理很多細節(例如緩存分配)以優化執行I/O。與基于文件描述符的I/O相比,基于流的I/O更加簡單,方便,也更加高效。因而在Linux環境C程序的編寫中,基于流的I/O使用更為廣泛。

流和文件指針

??在基于文件描述符中的底層系統調用的I/O操作中,所有的I/O函數都是針對文件描述符的。當打開一個文件時,即返回一個文件描述符,然后該問價描述符就用于后續的I/O操作。
??在標準I/O庫中,所有的I/O操作都是圍繞流(stream)來進行的。在Linux中,文件和設備都可以看做是數據流。
??什么是數據流?數據流是指無結構的字節序列。當用標準I/O庫打開或創建一個文件時,即將一個流與一個文件結合起來。
??標準I/O庫提供了函數fopen用于打開一個流。當打開一個流時,該函數會返回一個指向FILE對象的指針,即文件指針(類型為FILE *)。FILE對象通常是一個結構,它包含了I/O庫為管理該流所需要的所有信息:用于實際I/O的文件描述符,指向流緩存的指針,緩存長度,當前在緩存中的字節數,出錯標志等。但一般應用程序沒有必要關心FILE結構體的各成員值,使用流時,只需要將FILE指針作為參數傳遞給每個標準I/O函數。
??Linux對一個進程預定義了三個流:標準輸入流,標準輸出流和標準錯誤輸出流,它們自動地為進程打開并可用。這三個標準I/O流通過在頭文件<stdio.h>中預定義的文件指針stdin,stdout和stderr加以引用。它們與文件描述符STDIN_FILENO,STDOUT_FILENO和STDERR_FILENO所表示的標準輸入,標準輸出和標準錯誤輸出相對應。

緩存

??為什么要有緩存?
??標準I/O庫(注意:標準I/O庫是在系統調用函數基礎上構造的)提供緩存的目的是盡可能減少使用read和write調用的次數,以提高I/O效率。標準I/O也對每個I/O流自動進行緩存管理,免除了由應用程序考慮這一點的麻煩。
??標準I/O提供了三種類型的緩存。
- 全緩存

UNIX/LINUX、??使用全緩存時,只有當標準I/O緩存填滿后才進行實際的I/O操作。
??對磁盤文件的標準I/O操作一般是實施全緩存的。在一個流上執行第一次I/O操作時,相關標準I/O函數通常調用malloc函數分配所需要使用的緩存。
??術語刷新(flush)用于說明標準I/O緩存的寫操作。緩存可由標準I/O例程自動刷新(比如當填滿一個緩存時),或者可以調用函數fflush顯示刷新一個流。

  • 行緩存

??使用行緩存時,標準I/O庫會在輸入和輸出中遇到換行符時執行實際的I/O操作。當流涉及一個終端時(例如標準輸入和標準輸出),典型地使用行緩存。
??對行緩存有兩個限制:
1. 因為行的緩存長度是固定的,所以只要填滿了緩存,即使還沒有寫一個換行符,也會進行I/O操作。
2. 任何時候只要通過標準I/O庫要求從一個不帶緩存的流或一個行緩存的流(它預先要求從內核中得到數據,所需要的數據可能已在該緩存中)得到輸入的數據,那么就會造成刷新所有的行緩存輸出流。

  • 不帶緩存

??即不對字符進行緩存。如果用標準I/O函數寫若干條字符到不帶緩存的流中,則相當于用write系統調用函數將這些字符寫至相關聯的打開文件上。標準錯誤輸出流stderr通常是不帶緩存的,這就使得出錯信息可以盡快顯示出來,而不管它們是否含有一個新行字符。

ANSI C規定了下列緩存特征:
- 當且僅當標準輸入和標準輸出并不涉及交互作用設備時,它們才是全緩存的。
- 標準錯誤輸出絕對不會是全緩存的。

流的打開和關閉

LINUX和WINDOWS的區別,??標準I/O庫提供了fopen系列函數用以創建或打開流文件,提供了fclose函數關閉已打開的流文件。

打開流

??使用fopen系列函數可以創建或打開流文件,這些函數的原型如下。

#include <stdio.h>FILE *fopen(const char *path, const char *mode);
FILE *fdopen(int fd, const char *mode);
FILE *freopen(const char *path, const char *mode, FILE *stream);

??這三個函數的區別如下:
(1)fopen打開路徑名由path指定的一個文件。
(2)freopen在由stream指定的流上打開一個指定的文件(其路徑由path指定),如若該流已經打開,則先關閉該流。次函數一般用于將一個指定的文件打開為一個預定義的標準流:標準輸入,標準輸出或者標準錯誤輸出。
(3)fdopen取一個現存的文件描述符,并使一個標準的I/O流與該描述相結合。此函數常用于創建管道和網絡通信通道函數獲得的描述符。因為這些特殊類型的文件不能用標準I/O函數fopen打開,而必須先調用設備專用函數以獲得一個文件描述符,然后用fdopen使一個標準I/O流與該描述符相結合。注意:fdopen函數不是ANSI C的標準函數,而是屬于POSIX.1的標準。
??這三個函數各參數和返回值的含義如下:
1. path:要打開或創建的文件的名字
2. mode:對該I/O流的讀,寫方式,ANSI C規定了15種不同的可能值

  • r或rb:以讀方式打開
  • w或wb:以寫方式打開或創建,并將文件長度截為0
  • a或ab:以寫方式打開,新內容追加在文件尾
  • r+或r+b或rb+:以更新方式打開(讀和寫)
  • w+或w+b或wb+:以更新方式打開,并將文件長度截為0
  • a+或a+b或ab+:以更新方式打開,新內容追加在文件尾

??注意:(1)字符b的作用是區分文本文件和二進制文件。但Linux內核并不對這兩種文件進行區分,所以在Linux系統環境下字符b作為mode的一部分實際上并無作用。(2)對于fdopen,因為該描述符已經被打開,即所引用的文件必已存在,所以fdopen以寫方式打開并不會創建該文件或截短該文件。
3-fd:待關聯的底層文件描述符
4-stream:待關聯的流的文件指針,若該流已經打開則會被先關閉
5-返回值:成功時返回流文件的指針,失敗時返回NULL
??能否成功打開文件流受打開方式的限制,如下表所示。另外,進程可打開的文件流的數量有一個上限,該上限值由stdio.h中定義的FOPEN_MAX常量定義。
??打開一個I/O流的六種不同方式的限制

限制rwar+w+a+
文件必須已存在
擦除文件以前的內容
流可讀
流可寫
流只可在尾端寫

docker底層原理,??在指定w或a類型創建一個新文件時,無法指定該文件的存取許可權位,POSIX.1要求這種方式創建的文件具有以下存取許可權。
S_USR | S_WUSR | S_RGRP | S_WGRP | S_ROTH | S_WOTH
??除非流引用終端設備,否則系統默認它被打開時是全緩存的。若流引用終端設備,則該流四行緩存的。

關閉流

??在使用完流文件后,應調用fclose函數關閉流。fclose函數原型如下:

#include <stdio.h>
int fclose(FILE *fp);

??函數成功時返回0,失敗時返回EOF(-1)
??在文件流被關閉之前,fclose會刷新緩存中的輸出數據,但緩存中的輸入數據將被丟棄。如果標準I/O庫已經為該流自動分配了一個緩存,則釋放此緩存。
??當一個進程正常終止時(直接調用exit函數,或從main函數返回),則所有帶未寫緩存數據的標準I/O流都被刷新,所有打開的標準I/O流都被關閉。

實踐篇

?? 目標一:在當前目錄下創建一個file.txt文件
方法一:

#include <stdio.h>
#include <stdlib.h>int main(int argc, char *argv[])
{FILE *fp = NULL;fp = fopen("file.txt", "w");if (fp == NULL){printf("Cannot create files!\n");exit(-1);}fclose(fp);return 0;
}

docker菜鳥教程,??該程序能夠達到預定目標,它在我的機器上的運行結果如下:

biantiao@lazybone1994-ThinkPad-E430:~/桌面$ gedit fopen.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ gcc -o fopen fopen.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ./fopen
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ls
file.txt  fopen  fopen.c  fopen.c~
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ll file.txt
-rw-rw-r-- 1 biantiao biantiao 0  84 08:57 file.txt
biantiao@lazybone1994-ThinkPad-E430:~/桌面$

運行結束后,它在當前目錄下創建了一個空的file.txt文件。

方法二:

#include <stdio.h>
#include <stdlib.h>int main(int argc, char *argv[])
{FILE *fp = NULL;fp = fopen("file.txt", "r");if (fp == NULL){printf("Cannot create files!\n");exit(-1);}fclose(fp);return 0;
}

??該方法和上面的區別是,fopen中的參數由w換成了r,結果怎樣呢?結果是不行的,它在我的機器上的運行結果如下:

biantiao@lazybone1994-ThinkPad-E430:~/桌面$ gedit fopen.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ gcc -o fopen fopen.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ls
fopen  fopen.c  fopen.c~
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ./fopen
Cannot create files!
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ll
總用量 28
drwxr-xr-x  2 biantiao biantiao 4096  84 09:04 ./
drwxr-xr-x 46 biantiao biantiao 4096  83 21:41 ../
-rwxrwxr-x  1 biantiao biantiao 8704  84 09:04 fopen*
-rw-rw-r--  1 biantiao biantiao  220  84 09:01 fopen.c
-rw-rw-r--  1 biantiao biantiao  220  84 08:56 fopen.c~
biantiao@lazybone1994-ThinkPad-E430:~/桌面$

docker.io是什么。??應該能夠明白創建文件該用哪個參數了吧。注意w和r的區別,如下:

r或rb:以讀的方式打開(注意只能打開,不能創建)
w或wb:以寫方式打開或創建,并將文件長度截為0

??寫到這里我又想寫一個小程序來驗證上面的話,真心不好意思,初學者好奇心很重。說干就干。驗證什么呢?驗證以w方式打開一個文件能將其長度截為0。首先先在當前目錄創建一個test.txt文件,在里面輸入一句hello world。如下圖:
這里寫圖片描述
??編寫下列程序,保存為cut.c

#include <stdio.h>
#include <stdlib.h>int main(int argc, char *argv[])
{FILE *fp = NULL;fp = fopen("test.txt", "w");if (fp == NULL){printf("Open file failed!\n");exit(-1);}fclose(fp);return 0;
}

??從上面的程序可以看出,代碼只是將已存在的test.txt文件打開,然后關閉。看看test.txt文件會有什么變化。原先test.txt文件中寫有一句Hello World!。編譯并運行,查看結果。

biantiao@lazybone1994-ThinkPad-E430:~/桌面$ gcc -o cut cut.c
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ls
cut  cut.c  test.txt
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ ./cut
biantiao@lazybone1994-ThinkPad-E430:~/桌面$ cat test.txt
biantiao@lazybone1994-ThinkPad-E430:~/桌面$

前端 docker。??從命令行中cat的結果來看,test.txt文件確實被截短為0了。為了更直觀我用gedit打開看看。如下圖:
這里寫圖片描述
文件確實被截短為0了!(下次編程時要注意了,如果不想文件原來的內容被截掉,不能使用w參數,而得使用a或r參數來實現)

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/195878.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息