「UNIXAdvancedEnvironmentProgramming」のプロセス制御セクションには、ほぼ次のような例があり
ます。子プロセスで変数を変更し、親プロセスの後で変数の変更を観察します。
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include<sys/mman.h>
#include<pthread.h>
#include<semaphore.h>
#include <netinet/in.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/select.h>
#include<sys/epoll.h>
#include<unistd.h>
#include<dirent.h>
#include<pwd.h>
#include<grp.h>
#include<time.h>
#include<errno.h>
#include<signal.h>
#include<fcntl.h>
#include<sys/wait.h>
int glob=6;
char buf[]="write to stdout\n";
int main(int argc,char**argv)
{
int var;
pid_t pid;
var =88;
if(write(STDOUT_FILENO,buf,sizeof(buf)-1)!=sizeof(buf)-1){
fprintf(stderr,"write error");
exit(0);
}
printf("before fork\n");
// fflush(stdout);
// setbuf(stdout,NULL);
// setvbuf(stdout,(char*)NULL,_IONBF,0);
if((pid=fork())<0){
fprintf(stderr,"fork error");
exit(0);
}else if(pid==0){
glob++;
var++;
}else
sleep(2);
printf("pid =%d ,glob =%d ,var=%d\n",getpid(),glob,var);
return 0;
}
最初は、子プロセスが書き込み時に親プロセスのスタックとデータスペースをコピーし、子プロセスが親プロセスの変数を変更するため、これも当てはまると思いました。
しかし、本は突然振り返り、標準のIO関数書き込みバッファーをコピーする問題について言及しました。
問題は次のとおりです。書き込み(STDOUT_FILENO)が端末に1回出力され、printf( "フォークの前\ n")が端末に1回印刷されます。しかし、この結果をリダイレクトa.out >a.txt
の形式で
ファイルにリダイレクトすると、これらのステートメントは数回出力されますか?
回答:write(STDOUT_FILENO)は1回出力され、printf( "before fork \ n")は2回出力され
ます。どうした???この本は、writeにはキャッシュがなく、printfには8192バイトのキャッシュがあると指摘しています。驚くべきことであり、見落とされがちなのは、子プロセスが親プロセスの書き込みバッファーの内容を継承することです。では、なぜこの文を端末に書き込んだときに
出力されなかったのに、ファイルに書き込んだときに表示されたのでしょうか。これは、標準出力が行バッファーであるためです。行バッファーを更新するために書き込んだコンテンツには、「\ n」の新しい行文字があります。したがって、プロセスはこのコンテンツを取得できません。ただし、リダイレクトは出力ポイントをファイルに設定することであり、この時点で、そのバッファリングモードは、通常のファイルを書き込むためのバッファリングモードになります。フルバッファリング(「UNIX高度な環境プログラミング」の第5章を理解していません)これにより、親プロセスのprintfが終了した後、バッファ内のバッファのコンテンツが作成され、コンテンツが子プロセスにコピーされます。子プロセスもこの文を印刷します。
好奇心旺盛な赤ちゃんは、どうして子供がこの文を印刷できないのかと尋ねます。??
答えは、これら3つの関数のいずれかを使用して、親プロセスprintfの後にバッファーを更新することです。
fflush(stdout);
setbuf(stdout,NULL);
setvbuf(stdout,(char*)NULL,_IONBF,0);
また、書き込みバッファが8192バイトであることがわかります。8192バイトを超えて書き込む場合、バッファはフラッシュされていますか?
答えはイエスです。
次の実験:文字列が300バイトになるまで、printf( "forkの前\ n")に多くのスペースを追加します。次に、3000×30 = 9000 = 8192の30回印刷します。これは、このフルバッファーの下で、300×28 = 8400バイトの印刷後に、バッファーのすべての内容がフラッシュされることを意味します。ファイルに文の文字列を印刷できますか???親プロセスの30+は、バッファー(30-28)= 32の子プロセスに引き続き継承されます。答えは確かにそうです。
このとき、このコマンドを使用して、a.out |grep before | wc
表示できる行数を推測しますか?32です!ここ|
では、標準出力をgrepの実行可能ファイルにリダイレクトします。この記事をこれまで読んだことがない場合は、30になると思っていたはずです。!!びっくり?
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include<sys/mman.h>
#include<pthread.h>
#include<semaphore.h>
#include <netinet/in.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/select.h>
#include<sys/epoll.h>
#include<unistd.h>
#include<dirent.h>
#include<pwd.h>
#include<grp.h>
#include<time.h>
#include<errno.h>
#include<signal.h>
#include<fcntl.h>
#include<sys/wait.h>
int glob=6;
char buf[]="write to stdout\n";
int main(int argc,char**argv)
{
int var;
pid_t pid;
var =88;
if(write(STDOUT_FILENO,buf,sizeof(buf)-1)!=sizeof(buf)-1){
fprintf(stderr,"write error");
exit(0);
}
for(int i=0;i<30;i++)
printf("before fork \n");
// fflush(stdout);
// setbuf(stdout,NULL);
// setvbuf(stdout,(char*)NULL,_IONBF,0);
if((pid=fork())<0){
fprintf(stderr,"fork error");
exit(0);
}else if(pid==0){
glob++;
var++;
}else
sleep(2);
printf("pid =%d ,glob =%d ,var=%d\n",getpid(),glob,var);
return 0;
}
それを発見したので、自分で小さな実験をすることで多くを得ることができます。