本文已收錄至《Linux知識(shí)與編程》專欄!
作者:ARMCSKGT
演示環(huán)境:CentOS 7![]()
前言
我們?cè)趯W(xué)習(xí)C語(yǔ)言時(shí)可以使用fopen打開(kāi)文件fclose關(guān)閉文件;那么是C語(yǔ)言幫我們打開(kāi)的文件嗎?其實(shí)并不是,語(yǔ)言沒(méi)有這個(gè)能力,而是借助操作系統(tǒng)之手打開(kāi)文件并進(jìn)行操作,本篇將為大家介紹關(guān)于Linux下文件操作的系統(tǒng)調(diào)用,并介紹Linux系統(tǒng)如何組織和管理進(jìn)程打開(kāi)的文件!
正文
在對(duì)文件操作之前,我們需要知道文件在系統(tǒng)中到底是什么!
文件概念
- 文件操作本質(zhì):我們從語(yǔ)言層面是使用庫(kù)函數(shù)操作文件,讓我們以外文件操作是語(yǔ)言承擔(dān);但其實(shí)并非如此,文件操作是操作系統(tǒng)的事,是系統(tǒng)層面的問(wèn)題,操作系統(tǒng)對(duì)于打開(kāi)文件也會(huì)像管理進(jìn)程一樣通過(guò)先描述再組織進(jìn)行管理
- 幾乎所有語(yǔ)言都有文件操作,但是操作方法不太相同,但都是對(duì)系統(tǒng)接口的封裝,不同的語(yǔ)言有不同的范式,所以使用上會(huì)有區(qū)別,底層調(diào)用的是同一個(gè)系統(tǒng)調(diào)用
- 操作文件的第一件事就是打開(kāi)文件,而 文件=內(nèi)容+屬性 ,針對(duì)文件的操作有對(duì)文件內(nèi)容的操作,也有對(duì)文件屬性的操作;當(dāng)文件沒(méi)有被操作的時(shí)候,文件一般在磁盤(外存)上;當(dāng)我們對(duì)文件進(jìn)行操作的時(shí)候,文件需要被加載到內(nèi)存中,因?yàn)轳T諾依曼體系結(jié)構(gòu),CPU要讀文件需要先把文件搬到內(nèi)存
- 當(dāng)我們對(duì)文件進(jìn)行操作的時(shí)候,文件需要提前被加載到內(nèi)存,至少需要把文件屬性加載到內(nèi)存中;而每分每秒不止一個(gè)文件被加載到內(nèi)存(不止一個(gè)文件被打開(kāi)),也可能不止一個(gè)用戶在加載文件到內(nèi)存,內(nèi)存中一定存在大量的不同文件的屬性
- 打開(kāi)文件本質(zhì)就是將我們需要的文件屬性加載到內(nèi)存中;操作系統(tǒng)內(nèi)部一定會(huì)存在大量的被打開(kāi)的文件,操作系統(tǒng)要管理這些被打開(kāi)的文件,管理方式是:先描述再組織
所以:每一個(gè)被打開(kāi)的文件都要在操作系統(tǒng)內(nèi)對(duì)應(yīng)文件對(duì)象的struct file對(duì)象,可以將所有的struct file對(duì)象用某種數(shù)據(jù)結(jié)構(gòu)鏈接起來(lái),在操作系統(tǒng)內(nèi)部對(duì)被打開(kāi)的文件進(jìn)行管理就被轉(zhuǎn)換成為了對(duì)數(shù)據(jù)結(jié)構(gòu)的增刪查改
–描述: 構(gòu)建在內(nèi)存中的文件結(jié)構(gòu)體,也就是創(chuàng)建 struct file 對(duì)象記錄被打開(kāi)的文件的屬性信息
–組織: 通過(guò)struct file類型對(duì)象指針形成鏈表(等其他數(shù)據(jù)結(jié)構(gòu)),對(duì)文件的管理就變成了對(duì)數(shù)據(jù)結(jié)構(gòu)的增刪查改
- 文件其實(shí)可以被分成兩大類:磁盤文件和被打開(kāi)的文件(內(nèi)存文件)
- 文件被打開(kāi),是操作系統(tǒng)在打開(kāi),是用戶讓操作系統(tǒng)打開(kāi)的,用戶是以進(jìn)程(bash)為代表的;我們之前的所有的文件操作,都是 進(jìn)程 和 被打開(kāi)文件 的關(guān)系;
進(jìn)程 和 被打開(kāi)文件 的關(guān)系:struct task_struct 和 struct file 之間的聯(lián)系結(jié)論:真正的文件操作是需要通過(guò)系統(tǒng)調(diào)用實(shí)現(xiàn),而我們之前的文件操作都是進(jìn)程與操作系統(tǒng)間的交互;文件被打開(kāi),操作系統(tǒng)要為被打開(kāi)的文件,創(chuàng)建對(duì)應(yīng)的內(nèi)核數(shù)據(jù)結(jié)構(gòu)struct file,其中包含各種屬性和各種鏈接關(guān)系!
文件描述符
文件描述符概念
在C語(yǔ)言中,我們打開(kāi)一個(gè)文件會(huì)形成一個(gè)FILE類型的指針,fopen打開(kāi)文件會(huì)傳遞一個(gè)FILE類型的對(duì)象地址(底層創(chuàng)建了一個(gè)FILE對(duì)象)供我們使用,我們操作不同的文件圍繞不同的FILE指針即可!
#include <stdio.h> int main() { FILE* f1 = fopen("test1.txt", "r"); FILE* f2 = fopen("test2.txt", "w"); //讀取寫(xiě)入操作 fclose(f1); fclose(f2); f1 = f2 = NULL; return 0; }
代碼中,我們要操作不同的文件只需要對(duì)f1和f2指針進(jìn)行操作即可,但是FILE是一個(gè)對(duì)象,而底層是對(duì)文件描述符和其他屬性進(jìn)行封裝,在操作系統(tǒng)層面上,對(duì)任何文件操作只認(rèn)該進(jìn)程的文件描述符!
FILE對(duì)象
這里分享FILE對(duì)象源碼,F(xiàn)ILE對(duì)象是_IO_FILE的重命名,_IO_FILE對(duì)象中 _fileno 就是文件描述符!typedef struct _IO_FILE FILE; //stdio庫(kù)中對(duì)_IO_FILE類型重命名為FILE struct _IO_FILE { int _flags; /* High-order word is _IO_MAGIC; rest is flags. */ #define _IO_file_flags _flags /* The following pointers correspond to the C++ streambuf protocol. */ /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */ char* _IO_read_ptr; /* Current read pointer */ char* _IO_read_end; /* End of get area. */ char* _IO_read_base; /* Start of putback+get area. */ char* _IO_write_base; /* Start of put area. */ char* _IO_write_ptr; /* Current put pointer. */ char* _IO_write_end; /* End of put area. */ char* _IO_buf_base; /* Start of reserve area. */ char* _IO_buf_end; /* End of reserve area. */ /* The following fields are used to support backing up and undo. */ char *_IO_save_base; /* Pointer to start of non-current get area. */ char *_IO_backup_base; /* Pointer to first valid character of backup area */ char *_IO_save_end; /* Pointer to end of non-current get area. */ struct _IO_marker *_markers; struct _IO_FILE *_chain; int _fileno; //文件描述符 #if 0 int _blksize; #else int _flags2; #endif _IO_off_t _old_offset; /* This used to be _offset but it's too small. */ #define __HAVE_COLUMN /* temporary */ /* 1+column number of pbase(); 0 is unknown. */ unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; /* char* _save_gptr; char* _save_egptr; */ _IO_lock_t *_lock; #ifdef _IO_USE_OLD_IO_FILE };
打印文件描述符
我們嘗試打印文件描述符,首先我們知道,C語(yǔ)言有三個(gè)默認(rèn)的流:
- sdtin標(biāo)準(zhǔn)輸入流(我們從鍵盤輸入的文件流)
- stdout標(biāo)準(zhǔn)輸出流(打印到屏幕的流)
- stderr標(biāo)準(zhǔn)錯(cuò)誤輸出流
這三個(gè)流都是FILE類型我們通過(guò)以下C++代碼輸出標(biāo)準(zhǔn)流的類型與普通文件流做對(duì)比:#include <iostream> #include <cstdio> #include <typeinfo> //typeid所需庫(kù) using namespace std; int main() { FILE* f1 = fopen("./test1.txt", "r"); FILE* f2 = fopen("./test2.txt", "w"); cout<<"類型對(duì)比:"<<endl; cout<<"f1指針類型:"<<typeid(f1).name()<<endl; cout<<"stdin指針類型:"<<typeid(stdin).name()<<endl; cout<<endl; cout<<"文件描述符"<<endl; cout<<"stdin文件描述符:"<<stdin->_fileno<<endl; cout<<"stdout文件描述符:"<<stdout->_fileno<<endl; cout<<"stderr文件描述符:"<<stderr->_fileno<<endl; cout<<"f1文件描述符:"<<f1->_fileno<<endl; cout<<"f2文件描述符:"<<f2->_fileno<<endl; fclose(f1); fclose(f2); f1 = f2 = NULL; return 0; }
從這里可以看出文件描述符是真實(shí)存在的,且是一個(gè)整數(shù)從0開(kāi)始,而我們自己打開(kāi)一個(gè)文件的文件描述符是從3開(kāi)始的!
文件管理
操作系統(tǒng)要對(duì)這些打開(kāi)的文件進(jìn)行管理,否則每次操作文件都需要去內(nèi)存中查找,高效的管理可以極大的提高IO效率!
對(duì)于打開(kāi)的文件,操作系統(tǒng)會(huì)以先描述再組織的形式管理這些被打開(kāi)的文件!
操作系統(tǒng)將這些打開(kāi)的文件視為file對(duì)象,通過(guò)他們的file指針操作,并將這些file指針存入數(shù)組中,使用數(shù)組下標(biāo)進(jìn)行管理,這個(gè)數(shù)組為 file* fd_array[] ,而數(shù)組的下標(biāo)就是神秘的文件描述符fd
除了文件描述符外,文件的屬性還有文件權(quán)限、大小、路徑、引用計(jì)數(shù)、掛載數(shù)等信息,將這些文件屬性信息匯集起來(lái)就構(gòu)成了 struct files_struct 這個(gè)結(jié)構(gòu)體,它正是進(jìn)程控制塊 struct task_struct 中的成員之一!
而當(dāng)一個(gè)程序啟動(dòng)時(shí),操作系統(tǒng)會(huì)默認(rèn)為他打開(kāi) stdin標(biāo)準(zhǔn)輸入流 , stdout標(biāo)準(zhǔn)輸出流 , stderr標(biāo)準(zhǔn)錯(cuò)誤流 這三個(gè)文件流,將他們的文件file指針存入 fd_array[] 數(shù)組中,依次為 0,1,2 三個(gè)數(shù)組下標(biāo),也就是文件描述符;后續(xù)再打開(kāi)文件時(shí),默認(rèn)將打開(kāi)的文件file指針?lè)峙涞疆?dāng)前數(shù)組未使用的最小下標(biāo)處,所以用戶打開(kāi)文件一般是從3開(kāi)始的,當(dāng)然我們關(guān)閉所有標(biāo)準(zhǔn)流后打開(kāi)文件就是從0開(kāi)始分配文件描述符!
關(guān)于 files_struct
當(dāng)我們打開(kāi)一個(gè)文件時(shí),在內(nèi)存中會(huì)形成一個(gè)files_struct對(duì)象,files_struct對(duì)象是對(duì)該文件屬性的描述!
針對(duì)每個(gè)進(jìn)程都會(huì)打開(kāi)文件,進(jìn)程控制塊task_struct中必然包含文件操作相關(guān)信息,也就是files_struct !
注意:當(dāng)我們沒(méi)有被打開(kāi)時(shí),文件在磁盤上;當(dāng)文件被打開(kāi)后,并不是直接將全部?jī)?nèi)容加載到內(nèi)存上,而是先通過(guò)文件inode(后面介紹)找到磁盤上文件的詳細(xì)信息,加載文件屬性信息形成files_struct,待使用時(shí)再加載內(nèi)容!
文件描述符的分配
文件描述符fd的分配規(guī)則是:分配到當(dāng)前描述符數(shù)組未使用的最小下標(biāo)位置處!
說(shuō)明:
- 當(dāng)我們打開(kāi)文件時(shí),因?yàn)槟J(rèn)的三個(gè)標(biāo)準(zhǔn)文件流已經(jīng)打開(kāi),所以當(dāng)前的最小下標(biāo)一定是3
- 如果我們關(guān)閉了標(biāo)準(zhǔn)文件流,例如stdin(文件描述符為0),則新打開(kāi)的文件會(huì)分配文件描述符0(未使用的最小下標(biāo)處)
#include <iostream> #include <cstdio> using namespace std; int main() { cout<<"stdin文件描述符:"<<stdin->_fileno<<endl; cout<<"stdout文件描述符:"<<stdout->_fileno<<endl; cout<<"stderr文件描述符:"<<stderr->_fileno<<endl; FILE* f1 = fopen("./test1.txt", "r"); //先打開(kāi)test1文件 cout<<"f1文件描述符:"<<f1->_fileno<<endl; cout<<"關(guān)閉stdin標(biāo)準(zhǔn)文件流"<<endl; //關(guān)閉stdin fclose(stdin); FILE* f2 = fopen("./test2.txt", "w"); //再打開(kāi)test2文件 cout<<"f2文件描述符:"<<f2->_fileno<<endl; fclose(f1); fclose(f2); f1 = f2 = NULL; return 0; }
一切皆文件思想
我們知道在Linux系統(tǒng)下一切皆文件,我們?nèi)绾卫斫膺@個(gè)概念?
對(duì)于Linux系統(tǒng)來(lái)說(shuō),無(wú)論是鍵盤還是顯示器等設(shè)備,在他開(kāi)來(lái)都是文件,是一個(gè)file對(duì)象
無(wú)論是硬件外設(shè)還是軟件,對(duì)于操作系統(tǒng)來(lái)說(shuō)無(wú)非就是輸入和輸出兩個(gè)操作,所以操作系統(tǒng)對(duì)于這些硬件只需要提供讀方法和寫(xiě)方法即可驅(qū)動(dòng)該硬件(對(duì)于只讀或只寫(xiě)的設(shè)備屏蔽其中一個(gè)方法即可),所以這些硬件設(shè)備被當(dāng)成文件打開(kāi),在程序啟動(dòng)時(shí)將他們的file寫(xiě)入fd_array中管理即可,所以Linux下一切皆文件!
C語(yǔ)言文件操作
在講解系統(tǒng)調(diào)用前,我們簡(jiǎn)單了解一下C庫(kù)的文件操作方式!
如果想要詳細(xì)的了解,請(qǐng)閱讀官方文檔:C文件操作庫(kù)文件的打開(kāi)與關(guān)閉
文件的打開(kāi)使用fopen,關(guān)閉使用fclose!
打開(kāi)文件:
FILE * fopen ( const char * filename, const char * mode ); //打開(kāi)文件
- 參數(shù):
– filename:被打開(kāi)文件的本地路徑
– mode:打開(kāi)方式(以字符串的方式傳遞)- 返回值:
–如果文件不存在則返回空指針
文件打開(kāi)方式 含義 如果指定文件不存在 “r”(只讀) 為了輸入數(shù)據(jù),打開(kāi)一個(gè)已經(jīng)存在的文本文件 出錯(cuò) “w”(只寫(xiě)) 為了輸出數(shù)據(jù),打開(kāi)一個(gè)文本文件 建立一個(gè)新的文件 “a”(追加) 向文本文件尾添加數(shù)據(jù) 建立一個(gè)新的文件 “rb”(只讀) 為了輸入數(shù)據(jù),打開(kāi)一個(gè)二進(jìn)制文件 出錯(cuò) “wb”(只寫(xiě)) 為了輸出數(shù)據(jù),打開(kāi)一個(gè)二進(jìn)制文件 建立一個(gè)新的文件 “ab”(追加) 向一個(gè)二進(jìn)制文件尾添加數(shù)據(jù) 出錯(cuò) “r+”(讀寫(xiě)) 為了讀和寫(xiě),打開(kāi)一個(gè)文本文件 出錯(cuò) “w+”(讀寫(xiě)) 為了讀和寫(xiě),建議一個(gè)新的文件 建立一個(gè)新的文件 “a+”(讀寫(xiě)) 打開(kāi)一個(gè)文件,在文件尾進(jìn)行讀寫(xiě) 建立一個(gè)新的文件 “rb+”(讀寫(xiě)) 為了讀和寫(xiě)打開(kāi)一個(gè)二進(jìn)制文件 出錯(cuò) “wb+”(讀寫(xiě)) 為了讀和寫(xiě),新建一個(gè)新的二進(jìn)制文件 建立一個(gè)新的文件 “ab+”(讀寫(xiě)) 打開(kāi)一個(gè)二進(jìn)制文件,在文件尾進(jìn)行讀和寫(xiě) 建立一個(gè)新的文件 關(guān)閉文件:
int fclose ( FILE * stream );
- 參數(shù):
–stream:FILE文件指針- 返回值:
–成功返回0,失敗返回EOF(-1)演示:
#include <stdio.h> int main() { FILE* f1 = fopen("./test1.txt", "r"); FILE* f2 = fopen("./test2.txt", "w"); if(f1 && f2) printf("文件打開(kāi)成功!\n"); //不是空指針則打開(kāi)成功 int m = fclose(f1); int n = fclose(f2); if(!m && !n) printf("文件關(guān)閉成功!\n"); //返回0則關(guān)閉成功 f1 = f2 = NULL; return 0; }
文件讀寫(xiě)
文件讀寫(xiě)結(jié)果配套出現(xiàn),有讀就有寫(xiě)!
文件讀取接口:
//讀取文件中的一個(gè)字符 int fgetc ( FILE * stream ); //讀取文件中一行字符 char * fgets ( char * str, int num, FILE * stream ); //從給定流 stream 讀取數(shù)據(jù)到 ptr所指向的數(shù)組中 size_t fread ( void * ptr, size_t size, size_t count, FILE * stream ); //從一個(gè)流中執(zhí)行格式化輸入,遇到空格和換行時(shí)結(jié)束 int fscanf ( FILE * stream, const char * format, ... ); //從字符串讀取格式化輸入,遇到空格和換行時(shí)結(jié)束 int sscanf ( const char * s, const char * format, ...);
文件寫(xiě)入接口:
//將字符character寫(xiě)入文件中 int fputc ( int character, FILE * stream ); //將字符串str寫(xiě)入文件中 int fputs ( const char * str, FILE * stream ); //把 ptr 所指向的數(shù)組中的數(shù)據(jù)寫(xiě)入到給定流 stream 中 size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream ); //將字符串格式化輸出到流 stream 中 int fprintf(FILE *stream, const char *format, ...);
其他寫(xiě)入接口:
//將字符串格式化寫(xiě)入str中 int sprintf(char *str, const char *format, ...); //向s字符串格式化寫(xiě)入n個(gè)字符 int snprintf ( char * s, size_t n, const char * format, ... );
snprintf是sprintf的升級(jí)版,可以控制寫(xiě)入字符數(shù)量,更加安全;在文件操作中,這兩個(gè)接口常用于向緩沖區(qū)中寫(xiě)入數(shù)據(jù),然后整體寫(xiě)入到文件中(為了更加方便合理的向文件中寫(xiě)入數(shù)據(jù),一般預(yù)先定義緩沖區(qū)存儲(chǔ)數(shù)據(jù)然后整體寫(xiě)入)!
示例:
這里我們使用fscanf格式化讀取文件,snprintf格式化寫(xiě)入緩沖區(qū),fprintf格式化寫(xiě)入到文件!#include <stdio.h> #include <stdlib.h> #define FNAME "log" //操作文件名 int main() { //寫(xiě)操作 FILE* wfp = fopen(FNAME,"w+"); //以只寫(xiě)的方式打開(kāi)log文件,如果沒(méi)有則創(chuàng)建 if(!wfp) //打開(kāi)失敗就退出 { perror("fopen error!\n"); exit(EOF); } char buf1[64] = {0}; //寫(xiě)入緩沖區(qū) snprintf ( buf1, sizeof(buf1), "%s:%d", "向文件寫(xiě)入",668 ); //先格式化寫(xiě)入緩沖區(qū) fprintf(wfp,"%s",buf1); //將緩沖區(qū)中的字符串整體寫(xiě)入文件中 fclose(wfp); //寫(xiě)操作完成后關(guān)閉文件 wfp = NULL; //讀操作 FILE* rfp = fopen(FNAME,"r"); //以只讀的方式打開(kāi)log文件 if(!rfp) //打開(kāi)失敗就退出 { perror("fopen error!\n"); exit(EOF); } char buf2[64] = {0}; //讀取緩沖區(qū) fscanf(rfp,"%s",buf2); printf("%s\n",buf2); fclose(rfp); //讀操作完成后關(guān)閉文件 rfp=NULL; return 0; }
注意:我們?cè)趯?xiě)入字符串時(shí)沒(méi)有加入 \n 換行,因?yàn)?\n 是C語(yǔ)言定義的換行符,其他語(yǔ)言和軟件可能無(wú)法識(shí)別,所以我們寫(xiě)入時(shí)建議不要帶有一些僅語(yǔ)言定義的格式符;當(dāng)我們使用cat打印出log文件中的信息時(shí),輸出了與我們文件操作一模一樣的字符只不過(guò)沒(méi)有換行!
文件操作系統(tǒng)調(diào)用
前面簡(jiǎn)單介紹了C語(yǔ)言的文件操作,現(xiàn)在我們來(lái)介紹Linux文件操作系統(tǒng)調(diào)用!
打開(kāi)文件open
打開(kāi)文件使用open接口,關(guān)于open接口:
#include <sys/types.h> //open接口所需庫(kù) #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); //打開(kāi)文件可以修改權(quán)限
- 返回值:
–如果文件打開(kāi)成功則返回對(duì)應(yīng)的文件描述符,如果打開(kāi)失敗則返回-1- 參數(shù):
–pathname:被打開(kāi)文件的路徑(與C語(yǔ)言保持一致)
–flags:打開(kāi)方式,使用的標(biāo)記位的方式傳遞選項(xiàng)信號(hào)(標(biāo)記位以位圖方式設(shè)置)
–mode:如果文件不存在,創(chuàng)建文件時(shí)的權(quán)限設(shè)置,文件起始權(quán)限為0666- 注意:
–這兩個(gè)open函數(shù)類似于重載,如果我們打開(kāi)的文件可能不存在,則一定要手動(dòng)設(shè)置mode權(quán)限,否則文件創(chuàng)建出來(lái)權(quán)限是隨機(jī)值組成的,這樣可以保證文件安全;繼承環(huán)境變量表后,umask 默認(rèn)為 0002,當(dāng)然也可以使用umask函數(shù)自定義!關(guān)于設(shè)置標(biāo)志位flags的理解:
我們知道一個(gè)int有四字節(jié),一共32個(gè)比特位,每一個(gè)比特位可以表示1/0!
而在open函數(shù)的flags函數(shù)中,我們可以想想為有32個(gè)開(kāi)關(guān),進(jìn)行不同的組合,不能有相同的組合和包含某一個(gè)組合,這樣某一個(gè)標(biāo)志位都是獨(dú)立的,表示一個(gè)指令信息!
如果我們要驗(yàn)證某一個(gè)標(biāo)志位是否滿足使用按位與即可,如果我們要融合多個(gè)標(biāo)志位指令一起傳遞給函數(shù)使用按位或即可!
利用這個(gè)特性,我們可以實(shí)現(xiàn)一個(gè)小的位圖deom:#include <stdio.h> #define ONE 0x1 //定義位圖標(biāo)志位(比特位不能包含和相同) #define TWO 0x2 #define THREE 0x4 void directives(int flags) { //模擬實(shí)現(xiàn)三種選項(xiàng)指令傳遞 if(flags & ONE) printf("ONE指令\n"); if(flags & TWO) printf("TWO指令\n"); if(flags & THREE) printf("THREE指令\n"); } int main() { //使用按位或傳遞多個(gè)標(biāo)志位參數(shù) directives(ONE); printf("**************************\n"); directives(ONE | TWO); printf("**************************\n"); directives(ONE | TWO | THREE); return 0; }
關(guān)于flags常用的標(biāo)志位:
O_RDONLY //只讀 O_WRONLY //只寫(xiě) O_APPEND //追加 O_CREAT //新建 O_TRUNC //清空 O_RDRW //可讀可寫(xiě) O_EXCL //文件必須是被創(chuàng)建的,如果文件已存在則報(bào)錯(cuò)-1
這些標(biāo)志位可以通過(guò)按位或進(jìn)行組合!
基于C語(yǔ)言打開(kāi)方式的常用組合:
- w:O_WRONLY | O_CREAT | O_TRUNC (如果文件不存在則創(chuàng)建并清空文件內(nèi)容,只寫(xiě))
- a:O_WRONLY | O_CREAT | O_APPEND (如果文件不存在則創(chuàng)建,只追加寫(xiě)入)
- r:O_RDONLY (以只讀的方式打開(kāi)文件)
- …還有一些其他功能,根據(jù)標(biāo)志位進(jìn)行自由組合即可!
所以只要我們想使用open做到只寫(xiě)方式打開(kāi)不存在的文件,也不會(huì)報(bào)錯(cuò),加個(gè) O_CREAT 參數(shù)即可實(shí)現(xiàn)自動(dòng)創(chuàng)建!
關(guān)閉文件close
系統(tǒng)調(diào)用關(guān)閉文件使用close函數(shù),與fclose相似!
#include <unistd.h> int close(int fildes); //fildes(fd)是文件描述符
close函數(shù)解析:
- 參數(shù)fildes:需要關(guān)閉文件的文件描述符
- 返回值:關(guān)閉成功返回0,失敗返回-1
我們可以通過(guò)close(0),close(1),close(2)方式關(guān)閉標(biāo)準(zhǔn)文件流stdin,stdout,stderr!
寫(xiě)入文件write
write函數(shù)用于寫(xiě)入文件,其返回值類型有點(diǎn)特殊,但使用方法與fwrite基本一致!
#include <unistd.h> ssize_t write(int fildes, const void *buf, size_t count);
write函數(shù)解析:
- 參數(shù):
– fildes:文件描述符
– buf:寫(xiě)入的字符串目標(biāo)源指針或緩沖區(qū)(簡(jiǎn)稱寫(xiě)入源)
– count:寫(xiě)入的字節(jié)數(shù)- 返回值:寫(xiě)入成功返回寫(xiě)入的字節(jié)數(shù),失敗返回-1
讀取文件read
系統(tǒng)調(diào)用read用于從文件中讀取指定字節(jié)的數(shù)據(jù)!
#include <unistd.h> ssize_t read(int fildes, void *buf, size_t count);
read函數(shù)解析:
- 參數(shù):
– fildes:文件描述符
– buf:讀入的緩沖區(qū)(將讀入數(shù)據(jù)寫(xiě)入到buf緩沖區(qū),可以是一個(gè)字符數(shù)組或開(kāi)辟的空間)
– count:讀入的字節(jié)數(shù)- 返回值:讀取成功返回讀入的字節(jié)數(shù),讀取失敗返回-1
系統(tǒng)調(diào)用演示
注意:雖然我們使用系統(tǒng)調(diào)用寫(xiě)入數(shù)據(jù),但是為了方便,我們?cè)趯?xiě)入和讀取時(shí)還是借助緩沖區(qū)buf比較好!
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #define FNAME "log" //操作文件名 int main() { //以只寫(xiě)的方式打開(kāi)文件,>如果不存在則創(chuàng)建且清空文件內(nèi)容 int wfd = open(FNAME,O_WRONLY | O_CREAT | O_TRUNC,0664); assert(wfd>0); //檢測(cè)是否打開(kāi)成功 char buf1[64] = {0}; //寫(xiě)入緩沖區(qū) snprintf ( buf1, sizeof(buf1), "%s:%d", "向文件寫(xiě)入",668 ); //先格式化寫(xiě)入緩沖區(qū) int wsize = write(wfd,buf1,sizeof(buf1)); //寫(xiě)入緩沖區(qū)大小的內(nèi)容 printf("寫(xiě)入%d字節(jié)\n",wsize); close(wfd); //關(guān)閉文件 //以只讀的方式打開(kāi)文件 int rfd = open(FNAME,O_RDONLY); assert(rfd>0); //檢測(cè)是否打開(kāi)成功 char buf2[64] = {0}; //寫(xiě)入緩沖區(qū) int rsize = read(rfd,buf2,sizeof(buf2)); //向緩沖區(qū)buf2讀入文件中緩沖區(qū)大小的內(nèi)容 printf("讀取%d字節(jié)\n",rsize); close(rfd); //關(guān)閉文件 printf("讀入內(nèi)容: %s\n",buf2); return 0; }
同樣的,我們cat打開(kāi)文件沒(méi)有換行的問(wèn)題我們?cè)谇懊鍯語(yǔ)言已經(jīng)介紹了;不過(guò)需要注意的是,通過(guò)系統(tǒng)級(jí)函數(shù) write 寫(xiě)入字符串時(shí),不要刻意加上 ‘\0’,因?yàn)閷?duì)于系統(tǒng)來(lái)說(shuō),這也只是一個(gè)普通的字符(‘\0’ 作為字符串結(jié)尾也是C語(yǔ)言的規(guī)定)與 \n 的問(wèn)題一樣!
最后
Linux文件操作系統(tǒng)調(diào)用到這里就介紹的差不多了,本節(jié)我們介紹了Linux下關(guān)于文件操作的系統(tǒng)調(diào)用,了解了操作系統(tǒng)管理被打開(kāi)文件使用文件描述符的概念,知道了語(yǔ)言庫(kù)函數(shù)底層是對(duì)系統(tǒng)調(diào)用的封裝,以及Linux下一切皆文件思想的依據(jù)等等,文件的學(xué)習(xí)還沒(méi)結(jié)束,下一節(jié)我們繼續(xù)探究通過(guò)文件描述符如何實(shí)現(xiàn)重定向功能!
本次 <Linux文件理解和系統(tǒng)調(diào)用> 就先介紹到這里啦,希望能夠盡可能幫助到大家。
如果文章中有瑕疵,還請(qǐng)各位大佬細(xì)心點(diǎn)評(píng)和留言,我將立即修補(bǔ)錯(cuò)誤,謝謝!文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-476707.html
文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-476707.html
??其他文章閱讀推薦??
Linux<進(jìn)程控制> -CSDN博客
Linux<進(jìn)程地址空間> -CSDN博客
Linux<環(huán)境變量> -CSDN博客
Linux<進(jìn)程初識(shí)> -CSDN博客
Linux<進(jìn)程狀態(tài)及優(yōu)先級(jí)> -CSDN博客
??歡迎讀者多多瀏覽多多支持!??
到了這里,關(guān)于Linux文件理解和系統(tǒng)調(diào)用的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!