いろいろ調べたのですがどうしても別らないので御聞きします。
ヒントだけで結構ですので、情報お持ちの方は教えて頂けると嬉しいです。
なお、質問中のプロセスAのような機能を実際に実装しているUNIX用の
プログラムがあれば、そのプログラムの名前を教えていただけるだけでも結構です。
[Q] UNIXで動作するプロセスA/Bを用意します。
ここで、プロセスBは標準入力(stdin)からデータを吸い込んで標準出力(stdout)
にはきだす機能があります。ただ、プロセスBについては、
・標準入力(stdin)からデータを吸い込んで内部で加工し、加工したデータを
標準出力(stdout)に出力するということ以外全くわからない
ものとします。つまり、プロセスBは、
・どれだけ読みこんだら出力に出力するのか、また、
・どれだけ出力したら読込みに転じるのか、
が全く不明なものとします。
この時、名前無しpipeを2本作り、片方のpipeを使ってプロセスAから
プロセスBへデータを出力し、同時にもう片方のpipeを使ってプロセスAが
再度受け取るというものを作ろうとしたと仮定します。
+-->B-->+
| |
+---A---+
この場合にプロセスAはどうすれば、
・ プロセスBにできるだけ滞ることなくデータを出力でき、
・ プロセスBからできるだけ滞ることなくデータを得ることが出来
ますでしょうか?どのような機構がプロセスAには必要なのでしょうか?
#O_NONBLOCKとかはpipeだと何でかわからないのですが効いてくれないし、
#read/writeのブロックについてはselect/alarm等のtime outで復活させる位
#しか思い付けない...
#ashとかは既に読んでいるのですが、該当する機構が見付からないのです
In article <m3k79a2...@nightmare.hm.taito.co.jp>, Takahide Nojima <noj...@taito.co.jp> writes
> +-->B-->+
> | |
> +---A---+
> #O_NONBLOCKとかはpipeだと何でかわからないのですが効いてくれないし、
> #read/writeのブロックについてはselect/alarm等のtime outで復活させる位
> #しか思い付けない...
まず、O_NONBLOCKは使ってはいけません。効かないってのは嘘です。
効きますよ。でも正しい設定の仕方は教えてあげない。何故かと言
うと、それを設定するとデータが落ちるからです。
read/write のブロックを防いでもいけません。必要だからブロッ
クされているのですから。select ループは必要なら使うことがで
きますが、結構大変です。expect とかを使う方がいいんじゃない
かな。Perl だったらopen2 or open3。
> ものとします。つまり、プロセスBは、
> ・どれだけ読みこんだら出力に出力するのか、また、
> ・どれだけ出力したら読込みに転じるのか、
> が全く不明なものとします。
それは構いませんが、必ず、
プロセスBは、必要な入力を受け取った後、
出力をcloseして正常終了する
ことは保証する必要があります。出力をcloseすれば十分です。
正常終了すれば出力はcloseされます。
問題は、Bの「必要な入力」の定義ですが、それはわかっているん
ですよね? 一般的には、こちらからそれを出力した後、pipeをclose
してやればOkです。逆に、close しないと、待ちに入ってしまうこ
とが多いと思います。そうなると、B/A でデッドロックすることに
なります。(きっと、これで悩んでいるのだと想像します)
そして、A の出力をバファリングしないように注意してください。
close すればだいたいOkなんですけどね。
Bが出力をcloseしない場合は、Aのreadはブロックしてしまいます。
そのような場合は、もう少しBに関する情報が必要です。
> この時、名前無しpipeを2本作り、片方のpipeを使ってプロセスAから
> プロセスBへデータを出力し、同時にもう片方のpipeを使ってプロセスAが
> 再度受け取るというものを作ろうとしたと仮定します。
もし、B の出力を見ながらAの出力を調整するみたいなことをする、
つまり、Aの出力をcloseする前にBの出力を読み出すような場合だ
とすると、Bの出力のタイミングを知らずにdead lock を避けるこ
とは一般的にはできません。なので、select loopが必須となります。
その場合にも、必ず、B の出力がバッファリング無しであることが
必要です。
でも、まぁ、一度動けば、後は巨大な入出力とかが来ない限り
安定して動いていくれるようです。
---
Shinji KONO @ Information Engineering, University of the Ryukyus,
PRESTO, Japan Science and Technology Corporation
河野真治 @ 琉球大学工学部情報工学科,
科学技術振興事業団さきがけ研究21(機能と構成)
select()で2本のパイプのファイル記述子を監視し、準
備ができているほうのパイプと入出力をする、というこ
とをくり返せばよいと思います。
プロセスAは、タイムアウトなしの(入出力が可能になる
までブロックする)select()で2本のパイプを監視します。
select()から返ってきたときは、プロセスBに出力する
パイプの準備ができていたら出力を行い、プロセスBか
ら入力するパイプの準備ができていたら入力を行います
(両方の作業を行わなければならないこともある)。あと
はこれをプロセスBがパイプをクローズするまでくり返
せばよい、と。
# というか、select()をご存じなのになぜこれが出てこ
# ないのか不思議。select()を使ってはいけない理由が
# 何かあるのでしょうか...。
なお、パイプからどれだけデータが読めるか、またパイ
プにどれだけデータが書けるかは読み書きの段階では不
明なので、read()は指定したバイト数に満たないデータ
しか読めなくなるまでくり返し読む(パイプの容量がわ
かっているならその上限を指定して1回だけ読むのでも
可)、write()は返り値を調べて何バイト書けたかをきち
んとチェックする、などの配慮が必要です。
--
太田純(Junn Ohta) (株)リコー/新横浜事業所
oh...@sdg.mdd.ricoh.co.jp
In article <bhshhp$qfg$1...@ns.src.ricoh.co.jp>, oh...@src.ricoh.co.jp (Junn Ohta) writes
> # というか、select()をご存じなのになぜこれが出てこ
> # ないのか不思議。select()を使ってはいけない理由が
> # 何かあるのでしょうか...。
ちゃんと使うのは難しいですよ。
> 可)、write()は返り値を調べて何バイト書けたかをきち
> んとチェックする、などの配慮が必要です。
BSD ならwriteは失敗しないのでその必要はありません。Linux
では必要です。
さらにalarm/itimer と select を組み合わせると、read/write/select
が、それにひっかかると失敗するので、それに対応するコードを
書く必要があります。これは結構大変です。あはは。
(System V を作った奴は死んでいいです....)
> > 可)、write()は返り値を調べて何バイト書けたかをきち
> > んとチェックする、などの配慮が必要です。
>
> BSD ならwriteは失敗しないのでその必要はありません。Linux
> では必要です。
>
> さらにalarm/itimer と select を組み合わせると、read/write/select
> が、それにひっかかると失敗するので、それに対応するコードを
> 書く必要があります。これは結構大変です。あはは。
うーん、そのあたりは、シグナルをブロックしてもいいような書き方をする方
が楽だと思います。
タイムアウトとかが心配だと、selectもtimer指定したり、FIONREADで読める
分を考えてreadするとか。
> (System V を作った奴は死んでいいです....)
まあ、それは思いますが。シグナル回りでも。
--
___ わしは、山吹色のかすてーらが大好きでのぅ
[[o o]] ふぉっふぉっふぉ
'J' 森下 お代官様 MaNMOS 英夫@ステラクラフト
PGP Finger = CD EA D5 A8 AD B2 FE 7D 02 74 87 52 7C B7 39 37
むむ、そうかな。
> > 可)、write()は返り値を調べて何バイト書けたかをきち
> > んとチェックする、などの配慮が必要です。
>
> BSD ならwriteは失敗しないのでその必要はありません。Linux
> では必要です。
4096バイトしか容量のないパイプにそれより大きいデー
タを書こうとしたときは、BSDでも書けたところまでの
バイト数が返ると思ったのですが、違うかな。
> さらにalarm/itimer と select を組み合わせると、read/write/select
> が、それにひっかかると失敗するので、それに対応するコードを
> 書く必要があります。これは結構大変です。あはは。
シグナルがからむとね。pselect()があるならselect()
でなくてそっちを使うという手もありますが。
でもalarm()/itimer()とselect()を組み合わせるってど
んなときだろう。デーモンプロセスで下位のクライアン
ト管理はselect()で行って、上位のスケジューリングに
alarm()/itimer()を使うのかな?
select()を使いたいときって外部のイベントと協調して
動きたいわけなので、内部の都合(タイマーとか)で縛ら
れなくてすむように設計しませんか?
In article <bhsolg$s52$1...@ns.src.ricoh.co.jp>,
oh...@src.ricoh.co.jp (Junn Ohta) writes:
> でもalarm()/itimer()とselect()を組み合わせるってど
> んなときだろう。デーモンプロセスで下位のクライアン
> ト管理はselect()で行って、上位のスケジューリングに
> alarm()/itimer()を使うのかな?
そう言うのはmulti threadで書きます。
まあ、selectのtimeout値を変えながらやることも少なくないですが。
In article <squ4r0e...@stellar.co.jp>, man...@stellar.co.jp (Hideo "Sir MaNMOS" Morishita) writes
> > alarm()/itimer()を使うのかな?
> そう言うのはmulti threadで書きます。
> まあ、selectのtimeout値を変えながらやることも少なくないですが。
ですよね。
もとのプログラムがitimer使ってたりすると書き直すが大変...
In article <m3k79a2...@nightmare.hm.taito.co.jp>
Takahide Nojima <noj...@taito.co.jp> writes:
> この時、名前無しpipeを2本作り、片方のpipeを使ってプロセスAから
> プロセスBへデータを出力し、同時にもう片方のpipeを使ってプロセスAが
> 再度受け取るというものを作ろうとしたと仮定します。
> +-->B-->+
> | |
> +---A---+
> この場合にプロセスAはどうすれば、
> ・ プロセスBにできるだけ滞ることなくデータを出力でき、
> ・ プロセスBからできるだけ滞ることなくデータを得ることが出来
> ますでしょうか?
私なら、A をマルチスレッドで書きます。たとえば、こんな感じ。
新城: "外部プログラムとキャッシングを利用した堆積可能汎用フィ
ルタ・ファイル・オブジェクトの実現方式",情報処理学会論文誌,
Vol.41, No.6, pp.1708-1722 (2000).
http://www.ipsj.or.jp/members/Journal/Jpn/4106/article008.html
それから、一番簡単なのは、B を毎回毎回 fork しては exec して
使い回すことです。遅いけど。ファイルにデータを全部出力して
f = popen("B < file","r")
とします。
速くするなら、B に手を入れてTCP/IP 上のプロトコルのような形
で、要求と応答の組になるように書き換えるか、RPC にするかしま
す。RPC にしたのはこれ。
Y.Shinjo: "The World Wide Shell based on The Object-Stacking
Model", Proc. The Worldwide Computing and Its Applications '97
(WWCA97), pp.C-4-2-1--C-4-2-16 (March 10-11, Tsukuba, 1997).
(also in Springer LNCS 1274, pp.410-425).
kakasi を毎回起動すると遅いから、一度起動してから動かしっぱ
なしにして、web proxy からを RPC でつなぎました。
> #O_NONBLOCKとかはpipeだと何でかわからないのですが効いてくれないし、
O_NONBLOCK よりは、マルチスレッドだなあ。
マルチスレッドより上の逐次の popen() 。
\\ 新城 靖 (しんじょう やすし) \\
\\ 筑波大学 電子・情報 \\
oh...@src.ricoh.co.jp (Junn Ohta) writes:
> select()で2本のパイプのファイル記述子を監視し、準
> 備ができているほうのパイプと入出力をする、というこ
> とをくり返せばよいと思います。
>
> プロセスAは、タイムアウトなしの(入出力が可能になる
> までブロックする)select()で2本のパイプを監視します。
> select()から返ってきたときは、プロセスBに出力する
> パイプの準備ができていたら出力を行い、プロセスBか
> ら入力するパイプの準備ができていたら入力を行います
> (両方の作業を行わなければならないこともある)。あと
> はこれをプロセスBがパイプをクローズするまでくり返
> せばよい、と。
上のやってみたところ、全く問題なく動作しました。ありがとうございました。
#selectがreadも監視できることをうっかり忘れていました。
----------ここから------------
/* 処理概要です RHL9.0で確認*/
int to_A[2],to_B[2];
pipe(to_A); pipe(to_B);
pid_t pid=fork();
if(!pid){
/* child */
close(to_B[1]);close(to_A[0]);
dup2(to_B[0],STDIN_FILENO); dup2(to_A[1],STDOUT_FILENO);
close(to_B[0]);close(to_A[1]);
execv("process_B",NULL);
/* never return */
}else if(pid>0){
/* parent */
fd_set is_readable,is_writable;
close(to_B[0]);close(to_A[1]);
FD_ZERO(&is_readable);FD_ZERO(&is_writable);
FD_SET(to_B[1],&is_writable);FD_SET(to_A[0],&is_readable);
int write_size=0;
while(1){
select(FD_SETSIZE,&is_readable,&is_writable,NULL,NULL);
if(FD_ISSET(to_B[1],&is_writable)){
if(!write_size){
if(need_to_stop_sending_to_B()){
close(to_B[1]);
break;
}
fill_up(some_data_to_B);
write_size=size_of_a_piece;
}
int real_write_size=write(to_B[1],some_data_to_B,write_size);
int remain=write_size-real_write_size;
if(remain>0){
memmove(&some_data_to_B[0],&some_data_to_B[real_write_size],remain);
}
write_size=remain;
}
if(FD_ISSET(to_A[0],&is_readable)){
int real_read_size=read(to_A[0],some_data_from_B,size_of_a_piece);
/* use some_data_from_B ...*/
}
FD_SET(to_B[1],&is_writable);FD_SET(to_A[0],&is_readable);
}
while(read(to_A[0],some_data_from_B,size_of_a_piece)){
/* use some_data_from_B */
}
/* END */
----------ここまで------------
In article <m3fzjx38...@nightmare.hm.taito.co.jp>, Takahide Nojima <noj...@taito.co.jp> writes
> 上のやってみたところ、全く問題なく動作しました。ありがとうございました。
> #selectがreadも監視できることをうっかり忘れていました。
良かったですね。
> ----------ここから------------
> /* 処理概要です RHL9.0で確認*/
> int to_A[2],to_B[2];
Thread で書いた例も希望します。> deamon 屋さん。
(いや、無理にとはいわないけど)
Thread の方が簡単になる珍しい例かも。
In article <3988851...@insigna.ie.u-ryukyu.ac.jp>
ko...@ie.u-ryukyu.ac.jp (Shinji KONO) writes:
> 河野真治 @ 琉球大学情報工学です。
> > /* 処理概要です RHL9.0で確認*/
> > int to_A[2],to_B[2];
> Thread で書いた例も希望します。> deamon 屋さん。
deamon と Thread は違うんだけど、書いてみました。下の例で
closeしないと終らないんだけど、もう一度
pthread_create(,,pipe_writer,) することも可能なので、あえて
close していません。
> (いや、無理にとはいわないけど)
> Thread の方が簡単になる珍しい例かも。
select() よりは、Thread の方が簡単になる例は、よくあります。
mutex が出てこないから、デッドロックの心配もないし。
In article <m3fzjx38...@nightmare.hm.taito.co.jp>
Takahide Nojima <noj...@taito.co.jp> writes:
> if(FD_ISSET(to_A[0],&is_readable)){
> int real_read_size=read(to_A[0],some_data_from_B,size_of_a_piece);
> /* use some_data_from_B ...*/
この辺り、use が本当に可能かどうか、パイプから必要なデータが
全部届くのかどうか、プログラムの性質に因ります。データを作る
方も。
\\ 新城 靖 (しんじょう やすし) \\
\\ 筑波大学 電子・情報 \\
----------------------------------------------------------------------
/*
c/pthread/piperw-pthread.c --
Yasushi Shinjo <y...@is.tsukuba.ac.jp>
Start: 2003/08/20 15:39:52
*/
#include <stdio.h>
#include <unistd.h> /* ssize_t, write() */
#include <pthread.h>
struct pipe_writer_arg {
int fd ;
char *buf ;
int size ;
};
extern void pipe_reader( int fd );
extern void pipe_writer( struct pipe_writer_arg *arg );
extern ssize_t writen(int fd, const void *vptr, size_t n);
typedef (*start_routine_t)(void *);
main()
{
int pid ;
int to_A[2],to_B[2];
pipe(to_A); pipe(to_B);
if( (pid=fork()) == 0 ) /* child */
{
char *process_B = "/bin/cat" ;
close(to_B[1]);
close(to_A[0]);
dup2(to_B[0],STDIN_FILENO);
dup2(to_A[1],STDOUT_FILENO);
close(to_B[0]);close(to_A[1]);
execv(process_B,NULL);
perror(process_B);
exit( 1 );
/* never return */
}
else if( pid > 0 )
{
struct pipe_writer_arg arg ;
pthread_t writer_thread ;
close(to_B[0]);
close(to_A[1]);
arg.fd = to_B[1] ;
arg.buf = "hello" ;
arg.size = strlen( arg.buf );
pthread_create( &writer_thread, 0, pipe_writer, &arg );
pthread_detach( writer_thread );
pipe_reader( to_A[0] );
}
}
void
pipe_reader( int fd )
{
char buf[1024+1];
int count ;
printf("pipe_reader()\n");
while( (count=read(fd, buf, 1024)) > 0 )
{
buf[count] = 0 ;
printf("pipe_reader: read: %s (%d bytes)\n", buf,count );
}
printf("pipe_reader: EOF\n", buf );
}
void
pipe_writer( struct pipe_writer_arg *arg )
{
printf("pipe_writer()\n");
writen( arg->fd, arg->buf, arg->size );
/* close( fd );*/
}
/*
W.リチャード・スティーブンス著、篠田陽一訳:
"UNIXネットワークプログラミング第2版 Vol.1 ネットワークAPI:ソケットとXTI",
ピアソン・エデュケーション, 1999年. ISBN 4-98471-205-9
3.9節 readn, writen, および readline 関数 (p.76)
Richard Stevens: "UNIX Network Programming, Volume 1, Second Edition:
Networking APIs: Sockets and XTI", Prentice Hall, 1998.
ISBN 0-13-490012-X.
Section 3.9 readn, writen, and readline Functions (p.77)
http://www.kohala.com/start/ (http://www.kohala.com/~rstevens/)
http://www.kohala.com/start/unpv12e/unpv12e.tar.gz
*/
/* include writen */
/*#include "unp.h"*/
#include <errno.h>
ssize_t /* Write "n" bytes to a descriptor. */
writen(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
if (errno == EINTR)
nwritten = 0; /* and call write() again */
else
return(-1); /* error */
}
nleft -= nwritten;
ptr += nwritten;
}
return(n);
}
/* --------------------- c/pthread/piperw-pthread.c --------------------- */
Shinji KONO wrote:
>>可)、write()は返り値を調べて何バイト書けたかをきち
>>んとチェックする、などの配慮が必要です。
> BSD ならwriteは失敗しないのでその必要はありません。Linux
> では必要です。
BSDのような振りをしていたNeXTSTEPでは、このあたりでMachの
癖が出ていたのかパイプへのpartialwriteが起こっていました。
> (System V を作った奴は死んでいいです....)
linuxのパイプは、setsocokoptでパイプのバッファサイズを
拡張できないのが気に入らない。4KBのバッファじゃぁ
パフォーマンスが。。。(数年前の話なので今のカーネルでは
変わっているかも)
Junn Ohta wrote:
>>BSD ならwriteは失敗しないのでその必要はありません。Linux
>>では必要です。
> 4096バイトしか容量のないパイプにそれより大きいデー
> タを書こうとしたときは、BSDでも書けたところまでの
> バイト数が返ると思ったのですが、違うかな。
そういうときはブロックしますね。
ノンブロックモードだと、話は別ですが。