Google グループは Usenet の新規の投稿と購読のサポートを終了しました。過去のコンテンツは引き続き閲覧できます。
表示しない

[Q] パイプで通信

閲覧: 10 回
最初の未読メッセージにスキップ

Takahide Nojima

未読、
2003/08/19 1:02:322003/08/19
To:
nojimaです。

いろいろ調べたのですがどうしても別らないので御聞きします。
ヒントだけで結構ですので、情報お持ちの方は教えて頂けると嬉しいです。

なお、質問中のプロセス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とかは既に読んでいるのですが、該当する機構が見付からないのです

Shinji KONO

未読、
2003/08/19 2:54:302003/08/19
To:
河野真治 @ 琉球大学情報工学です。ま、良くある質問なんだけどさ。

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(機能と構成)

Junn Ohta

未読、
2003/08/19 2:53:452003/08/19
To:
fj.unixの記事<m3k79a2...@nightmare.hm.taito.co.jp>で
noj...@taito.co.jpさんは書きました。

> この時、名前無しpipeを2本作り、片方のpipeを使ってプロセスAから
> プロセスBへデータを出力し、同時にもう片方のpipeを使ってプロセスAが
> 再度受け取るというものを作ろうとしたと仮定します。
> +-->B-->+
> | |
> +---A---+
> この場合にプロセスAはどうすれば、
> ・ プロセスBにできるだけ滞ることなくデータを出力でき、
> ・ プロセスBからできるだけ滞ることなくデータを得ることが出来
> ますでしょうか?どのような機構がプロセスAには必要なのでしょうか?

select()で2本のパイプのファイル記述子を監視し、準
備ができているほうのパイプと入出力をする、というこ
とをくり返せばよいと思います。

プロセスAは、タイムアウトなしの(入出力が可能になる
までブロックする)select()で2本のパイプを監視します。
select()から返ってきたときは、プロセスBに出力する
パイプの準備ができていたら出力を行い、プロセスBか
ら入力するパイプの準備ができていたら入力を行います
(両方の作業を行わなければならないこともある)。あと
はこれをプロセスBがパイプをクローズするまでくり返
せばよい、と。

# というか、select()をご存じなのになぜこれが出てこ
# ないのか不思議。select()を使ってはいけない理由が
# 何かあるのでしょうか...。

なお、パイプからどれだけデータが読めるか、またパイ
プにどれだけデータが書けるかは読み書きの段階では不
明なので、read()は指定したバイト数に満たないデータ
しか読めなくなるまでくり返し読む(パイプの容量がわ
かっているならその上限を指定して1回だけ読むのでも
可)、write()は返り値を調べて何バイト書けたかをきち
んとチェックする、などの配慮が必要です。
--
太田純(Junn Ohta) (株)リコー/新横浜事業所
oh...@sdg.mdd.ricoh.co.jp

Shinji KONO

未読、
2003/08/19 3:10:472003/08/19
To:
河野真治 @ 琉球大学情報工学です。

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 を作った奴は死んでいいです....)

Hideo Sir MaNMOS Morishita

未読、
2003/08/19 3:29:422003/08/19
To:

In article <3988844...@insigna.ie.u-ryukyu.ac.jp>,
ko...@ie.u-ryukyu.ac.jp (Shinji KONO) writes:

> > 可)、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

Junn Ohta

未読、
2003/08/19 4:55:122003/08/19
To:
fj.unixの記事<3988844...@insigna.ie.u-ryukyu.ac.jp>で
ko...@ie.u-ryukyu.ac.jpさんは書きました。

> > # というか、select()をご存じなのになぜこれが出てこ
> > # ないのか不思議。select()を使ってはいけない理由が
> > # 何かあるのでしょうか...。
>
> ちゃんと使うのは難しいですよ。

むむ、そうかな。

> > 可)、write()は返り値を調べて何バイト書けたかをきち
> > んとチェックする、などの配慮が必要です。
>
> BSD ならwriteは失敗しないのでその必要はありません。Linux
> では必要です。

4096バイトしか容量のないパイプにそれより大きいデー
タを書こうとしたときは、BSDでも書けたところまでの
バイト数が返ると思ったのですが、違うかな。

> さらにalarm/itimer と select を組み合わせると、read/write/select
> が、それにひっかかると失敗するので、それに対応するコードを
> 書く必要があります。これは結構大変です。あはは。

シグナルがからむとね。pselect()があるならselect()
でなくてそっちを使うという手もありますが。

でもalarm()/itimer()とselect()を組み合わせるってど
んなときだろう。デーモンプロセスで下位のクライアン
ト管理はselect()で行って、上位のスケジューリングに
alarm()/itimer()を使うのかな?

select()を使いたいときって外部のイベントと協調して
動きたいわけなので、内部の都合(タイマーとか)で縛ら
れなくてすむように設計しませんか?

Hideo Sir MaNMOS Morishita

未読、
2003/08/19 5:08:142003/08/19
To:

デーモン屋です。

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値を変えながらやることも少なくないですが。

Shinji KONO

未読、
2003/08/19 5:20:282003/08/19
To:
河野真治 @ 琉球大学情報工学です。

In article <squ4r0e...@stellar.co.jp>, man...@stellar.co.jp (Hideo "Sir MaNMOS" Morishita) writes


> > alarm()/itimer()を使うのかな?
> そう言うのはmulti threadで書きます。
> まあ、selectのtimeout値を変えながらやることも少なくないですが。

ですよね。

もとのプログラムがitimer使ってたりすると書き直すが大変...

Yasushi Shinjo

未読、
2003/08/19 6:22:592003/08/19
To:
新城@筑波大学情報です。こんにちは。

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() 。

\\ 新城 靖 (しんじょう やすし) \\
\\ 筑波大学 電子・情報       \\

Takahide Nojima

未読、
2003/08/19 12:42:402003/08/19
To:
nojimaです。

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 */
----------ここまで------------

Shinji KONO

未読、
2003/08/19 19:15:152003/08/19
To:
河野真治 @ 琉球大学情報工学です。

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 の方が簡単になる珍しい例かも。

Yasushi Shinjo

未読、
2003/08/20 3:22:082003/08/20
To:
新城@筑波大学情報です。こんにちは。

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 --------------------- */

SAITOH akinori

未読、
2003/08/20 5:09:022003/08/20
To:
大阪大学の齊藤です

Shinji KONO wrote:
>>可)、write()は返り値を調べて何バイト書けたかをきち
>>んとチェックする、などの配慮が必要です。

> BSD ならwriteは失敗しないのでその必要はありません。Linux
> では必要です。

BSDのような振りをしていたNeXTSTEPでは、このあたりでMachの
癖が出ていたのかパイプへのpartialwriteが起こっていました。

> (System V を作った奴は死んでいいです....)

linuxのパイプは、setsocokoptでパイプのバッファサイズを
拡張できないのが気に入らない。4KBのバッファじゃぁ
パフォーマンスが。。。(数年前の話なので今のカーネルでは
変わっているかも)

齊藤明紀 sai...@ist.osaka-u.ac.jp

SAITOH akinori

未読、
2003/08/20 5:17:022003/08/20
To:
大阪大学の齊藤です

Junn Ohta wrote:

>>BSD ならwriteは失敗しないのでその必要はありません。Linux
>>では必要です。

> 4096バイトしか容量のないパイプにそれより大きいデー
> タを書こうとしたときは、BSDでも書けたところまでの
> バイト数が返ると思ったのですが、違うかな。

そういうときはブロックしますね。
ノンブロックモードだと、話は別ですが。

齊藤明紀 sai...@ist.osaka-u.ac.jp

新着メール 0 件