姓名:鄭煜爍 學號:19029100010 學院:電子工程學院
轉(zhuǎn)自:https://blog.csdn.net/nan_lei/article/details/81460798
【嵌牛導讀】簡單介紹linux系統(tǒng)中的標準I/O
【嵌牛鼻子】標準I/O
【嵌牛提問】標準I/O和文件I/O的區(qū)別
【嵌牛正文】
一谷朝、文件與文件類型
1堕扶、文件定義
? ? 定義:文件(File)是一個具有符號名字的一組相關(guān)聯(lián)元素的有序序列弓熏。文件可以包含的內(nèi)容十分廣泛,操作系統(tǒng)和用戶都可以將具有一定獨立功能的一個程序模塊杯道、一組數(shù)據(jù)或一組文字命名為一個文件。
? ? 文件名:這個數(shù)據(jù)有序序列集合(文件)的名稱揣非。
2叛氨、文件的分類
? ? 文件由許多種,運行的方式也各有不同擂橘。在Windows中晌区,我們是通過文件的后綴名來對文件分類的。例如.txt通贞、.doc朗若、.exe等。而在Linux系統(tǒng)中則不是昌罩,它不以文件后綴來區(qū)分文件的類型哭懈。
在Linux中,我們可以使用ls -l指令來查看文件的類型茎用。在Linux系統(tǒng)中遣总,文件主要有7種類型睬罗。
? ? -? ? 普通文件? ? 指ASCII文本文件、二進制文件以及硬鏈接文件
? ? d? ? 目錄文件? ? 包含若干文件或子目錄
? ? l? ? 符號鏈接? ? 只保留所指向文件的地址而非文件本身
? ? p? ? 管道文件? ? 用于進程間通信
? ? c? ? 字符設(shè)備? ? 原始的I/O設(shè)備文件旭斥,每次操作僅操作1個字符(例如鍵盤)
? ? b? ? 塊設(shè)備? ? ? ? 按塊I/O設(shè)備文件(例如硬盤)
? ? s? ? 套接字? ? ? ? 套接字是方便進程間通信的特殊文件容达,與管道不同的是套接字能通過網(wǎng)絡(luò)連接使不同的計算機的進程進行通信
3、Linux的文件目錄結(jié)構(gòu)
? ? Linux系統(tǒng)中文件采取樹形結(jié)構(gòu)垂券,即一個根目錄(/)花盐,包含下級目錄或文件的信息;子目錄又包含更下級的目錄或文件的信息菇爪。依次類推層層延伸算芯,最終構(gòu)成一棵樹。
Linux系統(tǒng)的每個目錄都有其特定的功能娄帖,這里只簡單介紹一些主要目錄及其功能
目錄? ? ? ? ? ? 功能說明
/etc? ? ? ? ? ? 存放系統(tǒng)配置文件
/bin? ? ? ? ? ? 存放常用指令
/sbin? ? ? ? (root用戶的)存放指令目錄
/home? ? ? ? 用戶主目錄也祠,所有用戶p的文件默認建立在此目錄下(用戶工作目錄)
/boot? ? ? ? ? 包含內(nèi)核啟動文件
/dev? ? ? ? ? ? 存放設(shè)備文件(與底層驅(qū)動交互)
/usr? ? ? ? ? ? 存放應(yīng)用程序
/mnt? ? ? ? ? ? 掛載目錄
/root? ? ? ? ? ? root用戶主目錄
/proc? ? ? ? ? ? process的所寫,存放描述系統(tǒng)進程的詳細信息
/lib? ? ? ? ? ? ? 存放常見庫文件
/lost+found? ? 可以找到一些誤刪除或丟失的文件并恢復它們
二近速、系統(tǒng)調(diào)用與用戶編程接口(API)
? ? 系統(tǒng)調(diào)用(System Call)是由操作系統(tǒng)實現(xiàn)提供的所有系統(tǒng)調(diào)用所構(gòu)成的程序接口的集合诈嘿。是應(yīng)用程序與操作系統(tǒng)間的接口與紐帶。
操作系統(tǒng)的主要功能是為管理硬件資源和為應(yīng)用程序開發(fā)人員提供良好的環(huán)境來使應(yīng)用程序具有良好的兼容性削葱。為了達到這個目的奖亚,內(nèi)核提供一系列具備預(yù)定功能的函數(shù),通過系統(tǒng)調(diào)用的接口呈現(xiàn)給用戶析砸。當用戶訪問系統(tǒng)調(diào)用昔字,系統(tǒng)調(diào)用把應(yīng)用程序的請求傳遞給內(nèi)核,調(diào)用相應(yīng)的內(nèi)核函數(shù)完成所需處理首繁,將處理結(jié)果返回給應(yīng)用程序作郭。
? ? 應(yīng)用程序編程接口(API,Application Programming Interface)是一些預(yù)定義的函數(shù)弦疮,目的是提供應(yīng)用程序與開發(fā)人員基于軟件/硬件得以訪問一組例程的能力夹攒,而又無需訪問源碼或理解內(nèi)部工作原理機制。
在實際開發(fā)應(yīng)用程序的過程中胁塞,我們并不直接使用系統(tǒng)調(diào)用接口咏尝,而是使用用戶編程接口(API)。為什么呢啸罢?
? ? ? ? 1.系統(tǒng)調(diào)用功能非常簡單编检,有時無法滿足程序的需求。
? ? ? ? 2.不同操作系統(tǒng)的系統(tǒng)調(diào)用接口不兼容扰才,若使用系統(tǒng)調(diào)用接口則程序移植工作量非常大允懂。
? ? 用戶編程接口使用各種庫(在C語言中最主要的是C庫)中的函數(shù)。為了提高編程效率衩匣,C庫中實現(xiàn)了很多函數(shù)累驮。這些函數(shù)實現(xiàn)了許多常用功能供程序開發(fā)者調(diào)用酣倾。這樣一來,程序開發(fā)者無需自己編寫這些代碼谤专,直接可以調(diào)用函數(shù)就能實現(xiàn)功能,提高了編碼效率和代碼復用率午绳。
? ? 使用用戶編程接口還有一個好處:一定程度上解決了程序的可移植性(雖然C語言的可移植性仍沒有Java好)置侍。幾乎所有的操作系統(tǒng)上都實現(xiàn)了C庫,因此使用C語言編寫的程序只需在不同的系統(tǒng)下重新編譯即可運行拦焚。
? ? 通常情況下蜡坊,用戶編程接口API在實現(xiàn)時需要依賴系統(tǒng)調(diào)用接口。例如創(chuàng)建進程API函數(shù)fork()需要調(diào)用內(nèi)核空間的sys_fork()系統(tǒng)調(diào)用赎败。但是還有一些API無需調(diào)用任何系統(tǒng)調(diào)用秕衙。
在Linux中用戶編程接口遵循在Unix系統(tǒng)中最流行的應(yīng)用編程編程標準POSIX標準。
/***************POSIX簡介*************************/
? ? POSIX表示可移植操作系統(tǒng)接口(Portable Operating System Interface 僵刮,縮寫為 POSIX )据忘,POSIX標準定義了操作系統(tǒng)應(yīng)該為應(yīng)用程序提供的接口標準,是IEEE為要在各種UNIX操作系統(tǒng)上運行的軟件而定義的一系列API標準的總稱搞糕,其正式稱呼為IEEE 1003勇吊,而國際標準名稱為ISO/IEC 9945。
? ? POSIX標準意在期望獲得源代碼級別的軟件可移植性窍仰。換句話說汉规,為一個POSIX兼容的操作系統(tǒng)編寫的程序,應(yīng)該可以在任何其它的POSIX操作系統(tǒng)(即使是來自另一個廠商)上編譯執(zhí)行驹吮。
/***************POSIX簡介end**********************/
標準I/O與文件I/O的區(qū)別:
? ? 1.文件I/O又稱為低級磁盤I/O针史,遵循POSIX標準。任何兼容POSIX標準的操作系統(tǒng)都支持文件I/O碟狞。標準I/O又稱為高級磁盤I/O啄枕,遵循ANSI C相關(guān)標準。只要開發(fā)環(huán)境有標準C庫篷就,標準I/O就可以使用射亏。
在Linux系統(tǒng)中使用GLIBC標準,它是標準C庫的超集竭业,既支持ANSI C中定義的函數(shù)又支持POSIX中定義的函數(shù)智润。因此Linux下既可以使用標準I/O,也可以使用文件I/O未辆。
? ? 2.通過文件I/O讀寫文件時窟绷,每次操作都會執(zhí)行相關(guān)系統(tǒng)調(diào)用。這樣的好處是直接讀寫實際文件咐柜,壞處是頻繁的系統(tǒng)調(diào)用會增加系統(tǒng)開銷兼蜈。標準I/O在文件I/O的基礎(chǔ)上封裝了緩沖機制攘残,每次先操作緩沖區(qū),必要時再訪問文件为狸,從而減少了系統(tǒng)調(diào)用的次數(shù)歼郭。
? ? 3.文件I/O使用文件描述符打開操作一個文件,可以訪問不同類型的文件(例如普通文件辐棒、設(shè)備文件和管道文件等)病曾。而標準I/O使用FILE指針來表示一個打開的文件,通常只能訪問普通文件漾根。
三泰涂、Linux標準I/O
1、標準I/O定義
? ? 標準I/O指的是ANSI C中定義的用于I/O操作的一系列函數(shù)辐怕。只要操作系統(tǒng)安裝了C庫逼蒙,就可以調(diào)用標準I/O。換句話說寄疏,若程序使用標準I/O函數(shù)是牢,那么源代碼無需進行任何修改就可以在其他操作系統(tǒng)上編譯,具有更好的可移植性赁还。
? ? 除此之外妖泄,由于標準I/O封裝了緩沖區(qū),使得在讀寫文件的時候減少了系統(tǒng)調(diào)用的次數(shù)艘策,提高了效率蹈胡。在執(zhí)行系統(tǒng)調(diào)用的時候,Linux必須從用戶態(tài)切換到內(nèi)核態(tài)朋蔫,在內(nèi)核中處理相應(yīng)的請求罚渐,然后再返回用戶態(tài)。如果頻繁地執(zhí)行系統(tǒng)調(diào)用則會增加這種開銷驯妄。標準I/O為了減少這種開銷荷并,采取緩沖機制,為用戶空間創(chuàng)建緩沖區(qū)青扔,讀寫時優(yōu)先操作緩沖區(qū)源织,在必須訪問文件時(例如緩沖區(qū)滿、強制刷新微猖、文件讀寫結(jié)束等情況)再通過系統(tǒng)調(diào)用將緩沖區(qū)的數(shù)據(jù)讀寫實際文件中谈息,從而避免了系統(tǒng)調(diào)用的次數(shù)。
2凛剥、流的定義
? ? 標準I/O的核心對象是流侠仇。當用標準I/O打開一個文件時,就會創(chuàng)建一個FILE結(jié)構(gòu)體描述該文件。我們把這個FILE結(jié)構(gòu)體稱為“流”逻炊。標準I/O函數(shù)都是基于流進行各種操作的互亮。
/**********************流的分類***********************/
流的分類分為文本流和二進制流兩種:
? ? 文本流:文本流是由字符文件組成的序列,每一行包含0個或多個字符并以'\n'結(jié)尾余素。在流處理過程中所有數(shù)據(jù)以字符形式出現(xiàn)豹休,'\n'被當做回車符CR和換行符LF兩個字符處理,即'\n'ASCII碼存儲形式是0x0D和0x0A溺森。當輸出時慕爬,0x0D和0x0A轉(zhuǎn)換成'\n'
? ? 二進制流:二進制流是未經(jīng)處理的字節(jié)組成的序列,在流處理過程中把數(shù)據(jù)當做二進制序列屏积,若流中有字符則把字符當做ASCII碼的二進制數(shù)表示。'\n'不進行變換磅甩。
例如:2016在文本流中和二進制流中的數(shù)據(jù)類型不同:
文本流:2016---->'2''0''1''6'---->50 48 49 54
二進制流:2016-->數(shù)字2016--->0000011111010001
在Linux/Unix系統(tǒng)中炊林,文本流與二進制流沒有差異,但是在Windows中稍有差異卷要,所以標準C庫定義了兩種流渣聚。
/**********************流的分類end********************/
在使用標準I/O操作文件的時候,每個被程序使用的文件都會在內(nèi)存中開辟一塊區(qū)域僧叉,用來存放與文件相關(guān)的屬性信息奕枝,這些信息存放在一個FILE類型的結(jié)構(gòu)體中,F(xiàn)ILE類型的結(jié)構(gòu)體是由系統(tǒng)定義的一個結(jié)構(gòu)體:
typedef struct
{
? ? short level;? ? ? ? ? ? ? ? //緩沖區(qū)滿/空的狀態(tài)
? ? unsigned flags;? ? ? ? ? ? //文件狀態(tài)標志
? ? char fd;? ? ? ? ? ? ? ? ? ? //文件描述符
? ? unsigned char hold;? ? ? ? //如緩沖區(qū)無內(nèi)容則不讀取字符
? ? short bsize;? ? ? ? ? ? ? ? //緩沖區(qū)的大小
? ? unsigned char *buffer;? ? ? //數(shù)據(jù)緩沖區(qū)的位置
? ? unsigned char *curp;? ? ? ? //指針當前的指向
? ? unsigned istemp;? ? ? ? ? ? //臨時文件指示器
? ? short token;? ? ? ? ? ? ? ? //用于有效性檢查
}FILE;
在標準I/O中瓶堕,常用FILE類型的結(jié)構(gòu)體指針FILE*來操作文件隘道。
/***************對“流”與“文件”的關(guān)系的討論************/
? ? 在初學C語言文件I/O相關(guān)知識點時,經(jīng)常會陷入“什么是流郎笆?”“什么是文件谭梗?”“流和文件有什么關(guān)系(區(qū)別)?”等問題宛蚓。在這里對“流”與“文件”進行簡單的討論激捏,基礎(chǔ)好的同學可跳過該部分。
《C Primer Plus》上說凄吏,C程序處理一個流而不是直接處理文件远舅。但是后面的解釋十分抽象:『流(stream)是一個理想化的數(shù)據(jù)流,實際輸入或輸出映射到這個數(shù)據(jù)流』痕钢。
? ? 本質(zhì)上來說图柏,文件本身就是數(shù)據(jù)的有序序列,因此我們操作文件時是按順序依次操作該文件的數(shù)據(jù)盖喷。我們可以想象一個傳送帶爆办,傳送帶上的產(chǎn)品就是待操作數(shù)據(jù)。當我們對文件內(nèi)的數(shù)據(jù)進行操作時课梳,已操作的數(shù)據(jù)從當前位置離開距辆,待操作的數(shù)據(jù)不斷流向當前位置余佃,這樣文件內(nèi)的數(shù)據(jù)就產(chǎn)生了流動的感覺,這個“傳送帶”就是C語言內(nèi)“流”的原型跨算。
? ? 我們打開一個流爆土,就意味著將該流與該文件進行了連接(即讓文件內(nèi)的“產(chǎn)品”放上“傳送帶”),關(guān)閉流將斷開流與該文件的連接诸蚕。此時我們對流進行操作就是對文件進行操作步势。通常在不產(chǎn)生歧義的情況下,“文件”與“流”可以不予區(qū)分背犯。
? ? 在程序開始執(zhí)行的時候坏瘩,操作系統(tǒng)會默認開啟stdin、stdout漠魏、stderr三個文件倔矾,這三個文件作為輸入、輸出柱锹、輸出錯誤的流哪自,這樣我們使用諸如scanf()、printf()等就無需手動加載流禁熏,方便使用壤巷。
當我們使用fopen()打開一個文件的時候,該文件會返回一個FILE*類型的指針瞧毙,例如:
FILE *fp = fopen("hello.txt","w")
此時文件hello.txt與流指針fp就關(guān)聯(lián)了起來胧华,對fp的操作就相當于對文件hello.txt進行操作。
/***************對“流”與“文件”的關(guān)系的討論end*********/
在標準I/O中預(yù)定義了三塊緩沖區(qū):stdin升筏、stdout撑柔、stderr,分別代表標準輸入流您访、標準輸出流铅忿、標準輸出錯誤流。見下:
流的名稱? ? ? ? 程序中使用
標準輸入? ? ? ? ? stdin? ?
標準輸出? ? ? ? ? stdout? ?
標準錯誤輸出? ? stderr? ?
標準I/O中的流的緩沖類型有三種:
1.全緩沖:這種情況下灵汪,當緩沖區(qū)被填滿后才進行實際的I/O操作檀训。對于存放在磁盤上的普通文件用標準I/O打開時默認是全緩沖的。當緩沖區(qū)滿或者執(zhí)行刷新緩沖區(qū)(fflush)操作才會進行磁盤操作享言。
2.行緩沖:這種情況下峻凫,當在輸入/輸出中遇到換行符時執(zhí)行I/O操作。-->標準輸入/輸出流(stdin/stdout)就是使用行緩沖览露。
3.無緩沖:不使用緩沖區(qū)進行I/O操作荧琼,即對流的讀寫操作會立即操作實際文件。標準出錯流(stderr)是不帶緩沖的,這就使得當發(fā)生錯誤時命锄,錯誤信息能第一時間顯示在終端上堰乔,而不管錯誤信息是否包含換行符。
示例1.1:stdout使用行緩沖形式
效果:不會立即打印內(nèi)容脐恩,而是等待'\n'或者緩沖區(qū)滿才輸出镐侯。
#include<stdio.h>
int main()
{
? ? while(1)
? ? {
? ? ? ? printf("Hello World");
? ? ? ? sleep(1);//延時1秒
? ? }
? ? return 0;
}
當緩沖區(qū)滿的時候才輸出到屏幕
示例1.2:stdout使用行緩沖形式
效果:當添加了'\n'之后,會正確地輸出
#include<stdio.h>
int main()
{
? ? while(1)
? ? {
? ? ? ? printf("Hello World\n");
? ? ? ? sleep(1);//延時1秒
? ? }
? ? return 0;
}
每1S輸出一次
示例1.3:stderr使用無緩沖形式
效果:stderr使用無緩沖驶冒,即使不使用'\n'仍能立即輸出
#include<stdio.h>
int main()
{
? ? while(1)
? ? {
? ? ? ? perror("Hello World");? //無緩沖
? ? ? ? sleep(1);//延時1秒
? ? }
? ? return 0;
}
perror會自動打印系統(tǒng)的提示信息 (:Success)
示例2:編寫程序?qū)崿F(xiàn)以下功能:
? ? ⒈向標準輸出流輸出HelloWorld
? ? ⒉向標準錯誤流輸出HelloWorld
? ? ⒊控制輸出重定向苟翻,使程序僅能輸出標準輸出流的字符
? ? ⒋控制輸出重定向,使程序僅能輸出標準錯誤流的字符
#include<stdio.h>
int main()
{
? ? fprintf(stdout,"%s","This is stdout:HelloWorld!\n");
? ? fprintf(stderr,"%s","This is stderr:HelloWorld!\n");
? ? //fprintf的作用是向某個流(文件)中按格式輸出指定內(nèi)容
? ? return 0;
}
實現(xiàn)第三步的功能:在執(zhí)行時:./a.out 2> /dev/null
僅能顯示標準輸出的內(nèi)容
實現(xiàn)第四步的功能:在執(zhí)行時:./a.out 1> /dev/null
僅能顯示標準出錯的內(nèi)容
————————————————
版權(quán)聲明:本文為CSDN博主「nan_lei」的原創(chuàng)文章骗污,遵循CC 4.0 BY-SA版權(quán)協(xié)議崇猫,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/nan_lei/article/details/81460798