コンテンツ
1.C言語のファイル操作に関連するライブラリ関数の簡単な使用と操作
1.C言語のファイル操作に関連するライブラリ関数の簡単な使用と操作
c言語では、c言語に関連するファイル操作機能の使用を終了しました。詳細については、ブロガーの記事を参照してください。
ファイル操作の詳細な説明 ここでは、いくつかのファイル関連のライブラリ関数fopenを使用しています。以下のコードを参照してください。
1 #include<stdio.h> 2 int main() 3 { 4 FILE *fp=fopen("log.txt","w");//以写的方式打开这个文件,如果文件不存在会自动创建一个 5 if(fp==NULL) 6 { 7 perror("fopen"); 8 } 9 10 int cnt=5; 11 const char *str="hello Linux\n"; 12 while(cnt--) 13 { 14 fputs(str,fp);//往刚打开的文件中写入数据 15 } 16 17 fclose(fp);//关闭文件 18 19 20 }
ここでは、書き込みでファイルを開きます。C言語を学習しているときに、fopenで書き込みでファイルを開くと、ファイルが存在する場合はファイルの内容が空になり、存在しない場合は現在のファイルが作成されます。 path. file、現在のパスは何ですか?
それでは、このプログラムを実行してみましょう。
log.txtファイルが実際にパス/home/ ksy / BK /の下に作成されていることがわかりました。それは、現在のパスが実行可能プログラムが配置されているパスであることを意味しますか?心配しないでください。C言語でファイルを読み取るためにライブラリ関数を使用しましょう。
1 #include<stdio.h> 2 int main() 3 { 4 FILE *fp=fopen("log.txt","r");//以写的方式打开这个文件,如果文件不存在会自动创建一个 5 if(fp==NULL) 6 { 7 perror("fopen"); 8 } 9 //由于上次已经往文件写了,所以我们直接可以读了 10 char buffer[128]; 11 while(fgets(buffer,sizeof(buffer),fp)) 12 { 13 printf("%s",buffer); 14 } 15 return 0; 16 17 }
このプログラムを実行します。
読み取りが成功したことがわかりました。次に、いわゆる現在のパスが実行可能プログラムが配置されているパスであるかどうかについて説明します。テストの便宜のために、/ home / ksy /BK/の下のlog.txtを削除します。このコードを使用してください
1 #include<stdio.h> 2 int main() 3 { 4 FILE *fp=fopen("log.txt","w");//以写的方式打开这个文件,如果文件不存在会自动创建一个 5 if(fp==NULL) 6 { 7 perror("fopen"); 8 } 9 const char *str="这是一次测试\n"; 10 int cnt=5; 11 while(cnt--) 12 { 13 fputs(str,fp); 14 } 15 return 0; 16 17 } ~
/ home / ksy / BK /に実行可能ファイルを生成し、この実行可能ファイルをホームディレクトリで実行します。
log.txtがパス/home/ ksyの下に生成されていることに驚きました。これは、現在のパスが実行可能プログラムが配置されているパスではないことを完全に示しています。しかし、実行可能プログラムがプロセスになると、私たちはそのディレクトリにいて、そのディレクトリに作成されます。
C言語での2つのライブラリ関数の使用法を少し示しましょう。
これらの2つの関数の使用について少し説明します。
fwrite:最初のパラメーターは書き込みたいコンテンツ、2番目のパラメーターは一度に書き込むバイト数、3番目のパラメーターは書き込みの最大回数、4番目のパラメーターはストリームへの書き込み、そしてreturn値は、実際の書き込みの数を示します。簡単なコードでデモンストレーションしましょう。
1 #include<stdio.h> 2 #include<string.h> 3 int main() 4 { 5 FILE *fp=fopen("log.txt","w");//以写的方式打开这个文件,如果文件不存在会自动创建一个 E> 6 const char str="hello Linux\n"; 7 int cnt=5; 8 while(cnt--) 9 { 10 fwrite(str,strlen(str),1,fp); 11 } 12 return 0; 13 }
このプログラムを実行してみましょう:
freadについて説明しましょう:
freadの最初のパラメーターは、読み取ったコンテンツをここに配置することです。2番目のパラメーターは、読み取るバイト数を示し、3番目のパラメーターは、読み取る最大回数を示し、4番目のパラメーターは、読み取り元の場所を示し、数値を返します。値コードが実際に読み取られた回数。
1 #include<stdio.h> 2 #include<string.h> 3 int main() 4 { 5 FILE *fp=fopen("log.txt","r");//以写的方式打开这个文件,如果文件不存在会自动创建一个 6 char buffer[128]; 7 while(fread(buffer,13,1,fp)) 8 { 9 printf("%s",buffer); 10 } 11 return 0; 12 } ~
演算結果:
二.stdout&&stderr && stdin
Linuxでのすべてがファイルであるとよく耳にします。つまり、Linuxでのすべてがファイルと見なすことができるため、もちろんキーボードとモニターもファイルと見なすことができます。「表示ファイル」にデータを書き込むことでディスプレイにデータが表示され、「キーボードファイル」からデータを読み取るため、キーボードで入力すると対応する文字を取得できます。
c言語プログラムが実行されている場合(プログラムの実行中にファイルを開く必要があることに注意してください)、デフォルトで3つのストリーム、つまりstdout(標準出力ストリーム)、stdin(標準入力ストリーム)、およびstderr(標準エラーストリーム)が開かれます。対応するデバイスは、モニター、キーボード、モニターです。マンマニュアルで確認してみましょう。
マニュアルを見ると、それらはすべてファイルポインタであるFILE*タイプであることがわかります。cプログラムを実行すると、システムはこれら3つの入力ストリームと出力ストリームを開きます。開いた後、scanfとprintfを使用して、キーボードとディスプレイで関連する操作を実行できます。つまり、stdin、stdout、stderrは、ファイルを開いたときに取得されるファイルポインタと同じ概念です。fputs関数を使用するときに、2番目のパラメータをstdoutに設定するとします。このとき、fputs関数は次のようになります。間にモニターにデータを表示するのはどうですか?コードで確認してみましょう。
1 #include<stdio.h> 2 #include<string.h> 3 int main() 4 { 5 const char*str="hello ksy\n"; 6 fputs(str,stdout); 7 return 0; 8 } ~ ~
このプログラムを実行してみましょう:
文字列がディスプレイに正常に印刷されていることがわかりました。もちろん、C言語には標準入力ストリーム、標準出力ストリーム、標準エラーストリームがあるだけでなく、C ++にはcin、cout、cerrもあります。他の言語にも同様の概念があります。
3.システムファイルIO
オペレーティングシステムの最下層は、実際にはファイルIOシステムコールインターフェイスを提供します。一部の書き込み、読み取り、クローズ、およびシークには、一連のシステムコールインターフェイスがあります。さまざまな言語が調整され、操作用のライブラリのセットにカプセル化されます。対応する言語のファイル。関数は、基になる呼び出し関係を知る必要がないため、ユーザーの学習コストが削減されます。
システムコールインターフェイスの概要:開く、書き込む、読み取る、閉じる:
1.開く
処置:ファイルを開いて
関数プロトタイプは次のとおりです。
int open(const char*pathname,int flags); int open(const char*pathname,int flags,mode_t mode);
openの最初のパラメーター:パス名
openの最初のパラメータは、ターゲットファイルを開くか作成することを意味します。ここで注意すべきことは次のとおりです。
1.パスの形式で指定されている場合、ファイルを作成する必要がある場合は、指定したパスの下にファイルが作成されます。
2.ファイル名のみが指定されている場合は、現在のパスの下(現在のパスの上にその意味が記載されています)に作成されます。
openの2番目のパラメーター:フラグ
openの2番目のパラメーターは、ファイルがどのように開かれるかを示します。一般的なオプションは次のとおりです。
ファイルを開くときは、|で区切った複数のオプションを使用できます。 例:書き込み専用のファイルを開き、ファイルが存在しない場合はO_WRONLY|O_CREATを作成します。では、フラグとは正確には何ですか?実際には整数です。整数は32ビットです。各ビットはオプションとして使用されます。対応する関数で、そのビットが1であるかどうかを確認して、このオプションを渡したかどうかを判断します。また、O_WRONLYが対応することを意味します。 32ビットの1つだけが1である整数に。それは本当ですか?vimを使用して/usr/include/asm-generic/fcntl.hディレクトリ内のファイルを開き、以下を見てみましょう。
これらのマクロ定義オプションには、バイナリシーケンスに1ビットのみが存在するという共通点があることがわかりました(O_RDONLY)オプションのバイナリシーケンスはすべて0であり、O_RDONLYオプションがデフォルトオプションであることを示します)。open関数では、特定の番号を使用して判断し、特定の関数のみを記述します。
openの3番目のパラメーター:
3番目のパラメーターは、ファイルを作成するためのアクセス許可を設定することです。Linuxでは、ファイルにアクセス許可があります。書き込み専用モードでファイルを開く場合、ファイルが存在しない場合は作成する必要がありますが、作成時にファイルのアクセス許可を設定する必要があります。アクセス許可については、ブロガーの関連記事を参照してください。(ファイルが作成されていない場合、3番目のパラメーターは空白のままにすることができることに注意してください)
openの戻り値は、ファイルを開くファイル記述子が開かず、-1を返すことを意味します。 以下に、コードを示します。
1 #include<stdio.h> 2 #include<string.h> 3 #include<unistd.h> 4 #include<sys/types.h> 5 #include<sys/stat.h> 6 #include<fcntl.h> 7 int main() 8 { 9 int fd1=open("./log1.txt",O_WRONLY|O_CREAT,0644); 10 int fd2=open("./log2.txt",O_WRONLY|O_CREAT,0644); 11 int fd3=open("./log3.txt",O_WRONLY|O_CREAT,0644); 12 int fd4=open("./log4.txt",O_WRONLY|O_CREAT,0644); 13 int fd5=open("./log5.txt",O_WRONLY|O_CREAT,0644); 14 int fd6=open("./log6.txt",O_WRONLY|O_CREAT,0644); 15 printf("%d\n",fd1); 16 printf("%d\n",fd2); 17 printf("%d\n",fd3); 18 printf("%d\n",fd4); 19 printf("%d\n",fd5); 20 printf("%d\n",fd6); 21 close(fd1); 22 close(fd2); 23 close(fd3); 24 close(fd4); 25 close(fd5); 26 close(fd6); 27 return 0; 28 }
このプログラムを実行します:
ファイル記述子は3から始まり、継続的にインクリメントされることがわかりました。存在しないファイルを開いて読み取り専用として作成しない場合、ファイルを開くことができず、-1が返されます。
いわゆるファイル記述子は、基本的にポインタの配列の添え字です。配列内の各添え字は、開いているファイル情報を格納する構造を指しているため、fd(ファイル記述子)情報から対応する開いているファイルを見つけることができます。Linuxでは、デフォルトで3つのファイルが開かれます。標準入力(0)標準出力(1)標準エラー(2)です。これが、ファイルを開く理由であり、ファイル記述子が3から始まる理由です。
2.閉じる
システム内のファイルを閉じるには、closeを使用します。対応する関数プロトタイプ
int close(int fd);
ファイルを閉じるには、対応するファイル記述子を渡すだけで済みます。ファイルが正常に閉じられた場合は0が返され、失敗した場合は-1が返されます。
3.書き込み
書き込み関数は、システムインターフェイスで関連情報をファイルに書き込むために使用されます。書き込み関数の関数プロトタイプは次のとおりです。
最初のパラメーター:対応するファイルのファイル記述子。2番目のパラメーター:何を書きたいか。3番目のパラメーター:書き込みたいバイト数。戻り値:実際に書き込まれたバイト数。
1 #include<stdio.h> 2 #include<string.h> 3 #include<unistd.h> 4 #include<sys/types.h> 5 #include<sys/stat.h> 6 #include<fcntl.h> 7 int main() 8 { 9 int fd=open("./log.txt",O_WRONLY|O_CREAT,0644); 10 const char*str="hello word\n"; 11 int cnt=5; 12 while(cnt--) 13 { 14 write(fd,str,strlen(str)); 15 } 16 close(fd); 17 18 return 0; 19 } ~
このプログラムを実行します:
4.読む
読み取り関数は、ファイルから情報を読み取るためにシステムインターフェイスで使用されます。読み取り関数の関数プロトタイプは次のとおりです。
ssize_t read(int fd, void *buf, size_t count);
最初のパラメーターはファイルに対応するファイル記述子であり、2番目のパラメーターはここに読み取られたコンテンツを配置することです。3番目のパラメーターは数バイトを読み取り、戻り値は実際に読み取られたバイト数です。-1。
1 #include<stdio.h> 2 #include<string.h> 3 #include<unistd.h> 4 #include<sys/types.h> 5 #include<sys/stat.h> 6 #include<fcntl.h> 7 int main() 8 { 9 int fd=open("./log.txt",O_RDONLY); 10 char ch; 11 while(1) 12 { 13 ssize_t ret=read(fd,&ch,1); 14 if(ret<=0) 15 { 16 break; 17 } 18 else 19 { 20 write(1,&ch,1); 21 } 22 } 23 24 25 close(fd); 26 27 return 0; 28 } ~
4.ファイル記述子fd
ファイルはプロセスによって開かれ、プロセスは複数のファイルを開くことができます。また、システムには多数のプロセスがあります。つまり、システムにはいつでも多数のプロセスが存在する可能性があります。ファイルを開くときは、ファイルの関連する属性をメモリにロードする必要があります。オペレーティングシステムは、管理作業を行うソフトウェアです。では、OSシステムはこれらのデータをどのように管理する必要があるのでしょうか。まず、組織について説明します。オペレーティングシステムは、開いているファイルごとに構造体struct_fileを作成し、それを二重リンクリストに整理します。OSのオープンファイルの管理も、リンクリストの追加、削除、確認、変更などの操作になっています。
では、これらのファイルが私によって開かれていることをプロセスはどのようにして知るのでしょうか。ファイルが開かれているプロセスを区別するために、プロセスとファイルの間に対応する関係を確立することも必要です。プロセスを学習しているとき、プログラムを実行すると、対応するコードとデータがメモリに読み込まれ、それに関連するデータ構造(task_struct、mm_struct、ページテーブル)が作成されます。また、仮想アドレスと物理アドレスの間のマッピング関係は、ページテーブルを介して確立されます。
実際、task_structには構造体へのポインタがあります。この構造体はfiles_structと呼ばれます。構造体には配列fd_arrayがあり、この配列の添え字はfdと呼ばれます。プロセスがlog.txtファイルを開くときは、次のものが必要です。最初にファイルがディスクからメモリにロードされて対応する構造体ファイルが形成され、構造体ファイルがファイル二重リンクリストに接続され、構造体の最初のアドレスが下付き文字3の位置に入力されます。 fd_array配列、つまりfd_array配列内添え字3のポインターは、構造体ファイルを指し、最後にファイルのファイル記述子を呼び出し元のプロセスに返します。
したがって、ファイル記述子を使用して、開いているファイルの関連情報を取得し、一連の操作を実行するだけで済みます。以前、ファイル記述子がデフォルトで3から始まることを確認しました。これは、0、1、および2がデフォルトで開かれることを意味します。0は標準入力ストリームを表し、対応するハードウェアデバイスはキーボード、1は標準出力ストリーム、対応するハードウェアデバイスはディスプレイ、2は標準エラーストリーム、対応するハードウェアデバイスはディスプレイです。プロセスが作成されると、OSはキーボード、表示、および表示に従って独自の構造体ファイルを形成し、3つの構造体ファイルをファイルの二重リンクリストにリンクし、3つの構造体ファイルのアドレスをに入力します。それぞれfd_array配列。添え字は0、1、および2であるため、標準の入力ストリーム、標準の出力ストリーム、および標準のエラーストリームがデフォルトで開かれます。
5.ファイル記述子の割り当てルール
以前に6つのファイルを続けて開いたところ、ファイル記述子は3から始まり、連続したアドレスを持っていることがわかりました。それは本当にいつも3から始まりますか?コードの一部を見てみましょう:
1 #include<stdio.h> 2 #include<string.h> 3 #include<unistd.h> 4 #include<sys/types.h> 5 #include<sys/stat.h> 6 #include<fcntl.h> 7 int main() 8 { 9 close(0); 10 int fd1=open("./log1.txt",O_WRONLY|O_CREAT,0644); 11 int fd2=open("./log2.txt",O_WRONLY|O_CREAT,0644); 12 int fd3=open("./log3.txt",O_WRONLY|O_CREAT,0644); 13 int fd4=open("./log4.txt",O_WRONLY|O_CREAT,0644); 14 printf("%d\n",fd1); 15 printf("%d\n",fd2); 16 printf("%d\n",fd3); 17 printf("%d\n",fd4); 18 close(fd1); 19 close(fd2); 20 close(fd3); 21 close(fd4); 22 23 return 0; 24 }
以下でプログラムを実行します。
fdが0から始まり、次に3から始まる方法を見つけました。2を閉じたので、結果がどうなるか見てみましょう。
1 #include<stdio.h> 2 #include<string.h> 3 #include<unistd.h> 4 #include<sys/types.h> 5 #include<sys/stat.h> 6 #include<fcntl.h> 7 int main() 8 { 9 close(0); 10 close(2); 11 int fd1=open("./log1.txt",O_WRONLY|O_CREAT,0644); 12 int fd2=open("./log2.txt",O_WRONLY|O_CREAT,0644); 13 int fd3=open("./log3.txt",O_WRONLY|O_CREAT,0644); 14 int fd4=open("./log4.txt",O_WRONLY|O_CREAT,0644); 15 printf("%d\n",fd1); 16 printf("%d\n",fd2); 17 printf("%d\n",fd3); 18 printf("%d\n",fd4); 19 close(fd1); 20 close(fd2); 21 close(fd3); 22 close(fd4); 23 24 return 0; 25 } ~ ~
演算結果:
0と2も使用されていることがわかりました。これで、ファイル記述子の割り当てルールは、使用されていない最小の添え字から始まることがわかりました。
5.リダイレクトの原則
上記の基盤により、これまでに学んだリダイレクトを深く研究することができます。その原理が何であるかを理解します。最初に、入力リターゲティング用語を見てみましょう。
1.リセット項目を入力します。
以前に学習した出力リダイレクトは、ディスプレイに出力する必要のあるデータを別のファイルにリダイレクトすることです。それで彼の論理的根拠は何ですか?
例:「表示ファイル」に出力する必要のあるデータをlog.txtファイルに出力する場合は、log.txtファイルを開く前に、ファイル記述子1でファイルを閉じることができます。 "display file" "閉じる。後でlog.txtファイルを開いたときに、割り当てられたファイル記述子が1になるようにします。
1 #include<stdio.h> 2 #include<string.h> 3 #include<unistd.h> 4 #include<sys/types.h> 5 #include<sys/stat.h> 6 #include<fcntl.h> 7 int main() 8 { 9 close(1); 10 int fd=open("./log1.txt",O_WRONLY|O_CREAT,0644); 11 printf("hello ksy\n"); 12 printf("hello ksy\n"); 13 printf("hello ksy\n"); 14 printf("hello ksy\n"); 15 close(fd); 16 17 return 0; 18 } ~
演算結果:
データが実際にlog.txtに出力されていることがわかりました。ここで説明してください:
1. printfはデフォルトでデータをstoutに出力し、stdoutはFILE *ポインターでもあり、ファイル記述子である整数をカプセル化する結果本体FILEを指し、stdoutはFILE構造を指します。保存されたファイル記述子は1であるため、printfはファイル記述子が1のファイルにデータを出力します。
2. c言語の出力データは、オペレーティングシステムにすぐには書き込まれませんが、一時的にc言語バッファーに格納され、条件が発生するとバッファーにフラッシュされます。
2.リダイレクトを追加します
追加リダイレクトと出力リダイレクトの違いは、追加リダイレクトがデータを上書きしないことです。
原理を見てみましょう。実際、出力リダイレクトよりもO_APPENDオプションが1つだけあります。
1 #include<stdio.h> 2 #include<string.h> 3 #include<unistd.h> 4 #include<sys/types.h> 5 #include<sys/stat.h> 6 #include<fcntl.h> 7 int main() 8 { 9 close(1); 10 int fd=open("./log1.txt",O_WRONLY|O_CREAT|O_APPEND,0644);//追加重定向和输出重定向的区别就只是多了一个O_APPEND选项 11 printf("hello ksy\n"); 12 printf("hello ksy\n"); 13 printf("hello ksy\n"); 14 printf("hello ksy\n"); 15 close(fd); 16 17 return 0; 18 }
3.入力リダイレクト
入力リダイレクトとは、キーボードからデータを読み取る必要があることを意味しますが、現在は別のファイルからデータを読み取るようにリダイレクトされています。
たとえば、scanf関数は標準入力からデータを読み取り、log1.txtからデータを読み取らせるようにしたので、scanfがデータを読み取る前にclose(0)を実行します。これにより、log1のようにキーボードファイルが閉じられます。txtのファイル記述子0です。
1 #include<stdio.h> 2 #include<string.h> 3 #include<unistd.h> 4 #include<sys/types.h> 5 #include<sys/stat.h> 6 #include<fcntl.h> 7 int main() 8 { 9 close(0); 10 int fd=open("./log1.txt",O_RDONLY);//追加重定向和输出重定向的区别就只是多了一个O_APPEND选项: 11 char buffer[128]; 12 while(~scanf("%s",buffer)) 13 { 14 printf("%s\n",buffer); 15 } 16 close(fd); 17 return 0; 18 } ~
演算結果:
原理はstdoutに似ているので、ここでは説明しません。
質問を考えてみましょう。標準出力ストリームと標準エラーストリームは表示に対応していますが、それらの違いは何ですか?
コードで確認してみましょう。
1 #include<stdio.h> 2 #include<string.h> 3 #include<unistd.h> 4 #include<sys/types.h> 5 #include<sys/stat.h> 6 #include<fcntl.h> 7 int main() 8 { 9 fprintf(stdout,"hello stdout"); 10 fprintf(stderr,"hello stderr"); 11 12 return 0; 13 }
stdoutに出力された再配置のみがlog1.txtに送信されることがわかりました。実際、リダイレクトを使用すると、ファイル記述子1の標準出力ストリームがリダイレクトされ、ファイル記述子2の標準エラーストリームはリダイレクトされません。これが2つの違いです
システムコールdup2
対応するファイル記述子プラクティスの対応する出力リダイレクトと出力リダイレクトのみを閉じることができることがわかったので、閉じることはできませんか?リダイレクトを完了するには、fd_array配列の要素をコピーする必要があります。たとえば、fd_array[3]の内容をfd_array[1]にコピーする場合、C言語のstdoutは、ファイル記述子が1のファイルにデータを出力するため、出力をファイルlog.txtにリダイレクトします。Linuxでは、次のシステムコールが提供されます。
関数:dup2は、fd_array[oldfd]の内容をfd_array[newfd]にコピーします。関数の戻り値:呼び出しが成功した場合は0が返され、失敗した場合は-1が返されます。
それを使用するときは、注意を払う必要があります:
- oldfdが有効なファイル記述子でない場合、dup2呼び出しは失敗し、ファイル記述子newfdを含むファイルはこの時点で閉じられません。
- oldfdが有効なファイル記述子であるが、newfdとoldfdの値が同じである場合、dup2は何もせず、newfdを返します。
dup2を介した以前の出力リダイレクトを示しましょう。
1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<sys/stat.h> 4 #include<unistd.h> 5 #include<fcntl.h> 6 int main() 7 { 8 int fd=open("./log.txt",O_WRONLY|O_CREAT,0644); 9 dup2(fd,1); 10 printf("hello world\n"); 11 printf("hello world\n"); 12 13 }
演算結果:
C言語のファイル
ライブラリ関数はシステムコールインターフェイスのカプセル化であるため、ファイルへのアクセスは基本的にファイル記述子fdを介してアクセスされます。したがって、CライブラリのFILE構造はファイル記述子fdをカプセル化する必要があります。vimを使用してusr/include / stdio.hファイルを開き、ファイルを表示できます
完全な内容は次のとおりです。
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 };
FILEのソースコードから、FILE構造体がfdをカプセル化していることがわかりました。fdは内部に_filenoです。内部にバッファも表示されていることを確認するのは難しくありません。ここでのバッファーとは、c言語のバッファーを指します。バッファ更新戦略は次のとおりです。
1.バッファリングなし:バッファリングなし
2.ラインバッファリング:/ nが検出されると、ディスプレイに出力される対応するデータがこの戦略を使用して更新されます。
3.フルバッファリング:バッファがいっぱいになるとバッファが更新されるか、プロセスが終了するとバッファが更新されます。この戦略はファイルに使用されます。
したがって、リダイレクトによってバッファのフラッシュ戦略が変わることを理解する必要があります。たとえば、出力リダイレクト、ディスプレイに出力するための元の戦略はラインバッファリングですが、ファイルに出力するための戦略は完全バッファリングです。例を見てみましょう。
1 #include<stdio.h> 2 #include<sys/types.h> 3 #include<sys/stat.h> 4 #include<unistd.h> 5 #include<fcntl.h> 6 #include<string.h> 7 int main() 8 { 9 close(1); 10 int fd=open("./log.txt",O_WRONLY|O_CREAT,0644); 11 if(fd<0) 12 { 13 perror("open"); 14 return -2; 15 } 16 const char*str1="hello write\n"; 17 const char*str2="hello printf\n"; 18 const char*str3="hello fwrite\n"; 19 write(fd,str1,strlen(str1)); W> 20 printf(str2); 21 fwrite(str3,strlen(str3),1,stdout); 22 fork(); 23 fflush(stdout);//刷新 24 close(fd); 25 return 0; 26 } ~
システムコールでfwriteのみが1回だけ印刷され、printfとfwriteの両方が2回印刷されるのはなぜですか?これは何ですか?。これは、システムコールwriteの場合、OSに直接書き込まれるのに対し、printfとfwriteはC言語によって提供されるバッファーに書き込まれ、OSにすぐにはフラッシュされないためです。fork()の後、子プロセス親プロセスでコピーオンライトを実行すると、親プロセスのバッファ内のデータが子プロセスによってコピーされ、親プロセスと子プロセスが独自のデータを更新します。そのため、ライブラリ関数が2回出力されることがわかりました。