あるプロセスから外部プロセスの入出力をつないで処理するのはCGIなどでお馴染みの処理だ。
int pid;
int pfd[2];
pipe(pfd);
pid=fork();
if(pid==0){
//子プロセス
close(STDIN_FILENO);
dup2(pfd[1], STDIN_FILENO);
close(pdf[1]);
printf( "Hello world!");
close(STDIN_FILENO);
exit(1);
}else{
//親プロセス
//パイプから読み込み
read(pfd[0],buffer,1,1000);
write(socket, buffer,1000);
}
こんな感じに外部プロセスとやりとりができる。しかしサーバ用途では外部プロセスから一度読み込み、これを再度書き込む等というとても面倒かつ負荷分散が面倒なところもない。子プロセスにパワーをかければ、パイプから外に書き出すところにもパワーがいる。
単純に新規プロセスに書き込みをお任せできないのかと考えてみた。このときに理解してなければならないのはファイルディスクリプタの挙動だ。ファイルディスクリプタは番号で管理されforkでプロセスを分けても同じ番号となる。確認はしてないが子プロセスと親プロセスはプロセスが分かれていることからforkで内部的には別物となっていると思われる。つまりforkの段階で内部的にdup同様の処理がされていると思う。大抵の子プロセスでは最終的に閉じて終わるが親プロセスでは同じファイルディスクリプタをそのまま使えているからだ。
そう考えると親プロセスでは早々にファイルディスクリプタを閉じておいて、子プロセスで頑張って処理をしてもらい、親は放任主義でいくのが実は効率いいんじゃないかと思う。
int socket;
//socketとかの処理
pid=fork();
if( pid==0){
//子プロセス
//socketへの読み書き
close(socket);
}else{
//親プロセス
close(socket);
}
親は子プロセスを巻き込んで死亡したりしなければ問題ない。あとは標準入出力をファイルディスクリプタに割り当てる事ができれば良い。これは多分知られているんだろうけど、調べてても出てこなかったので書いておく。
int backup=dup(socket);
close(STDIN_FILENO);
dup2(socket,STDIN_FILENO);
close(STDOUT_FILENO);
dup2(socket,STDOUT_FILENO);
close(socket);
//printfやread,write等の処理
//STDIN,STDOUTは放置
//戻すならsocket=dup(backup);
close(backup);
これで入出力共に子プロセスを乗っ取れる。面倒だった子プロセスとの同期をまったく考えなくて済む。とはいえ、子プロセスは放任主義なので何かあっても責任は取れないが。
一応自分のサーバもこれで実装してみた。ものすごく問題があることだったら是非ご教授ください。やはり調べて出てこないとちょっと不安になるので。
Copyright(c) 2015-2023 Birdland Ltd. All Rights Reserved.