[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[FDclone-users:00994] Re: パイプ先を間違えることができない?
- Subject: [FDclone-users:00994] Re: パイプ先を間違えることができない?
- From: Takashi SHIRAI <shirai@unixusers.net>
- Date: Fri, 13 Jul 2012 01:09:50 +0900
しらいです。
In Message-Id <20120711174705.6585487C001@yuka.unixusers.net>
Takashi SHIRAI <shirai@unixusers.net>writes:
> しらいです。
> ちょっと体調を崩して寝込んでますので、今日のところは、結論
> に至る前の中間報告という形でご了承下さい。
結論出ました。
> これは、Xtcsetattr() が errno=EINTR を無視して tcsetattr()
> を呼び続けているだけのことで、無限ループしていること自体がお
> かしい訳ではなさそうです。
こちらは根本原因ではありませんが、system call が EINTR で
失敗する場合の対処として無条件のリトライは余り賢くないので、
別途修正しておきます。
I/O 周りの system call が SIGTTIN/SIGTTOU で中断される場合、
何度リトライしたところで結果は同じなので、EINTR に対してはそ
の元凶の signal に応じてエラーにすべきでしょうね。
termio.c 中にはこの手の system call wrapper が沢山あるので、
今回はその修正 patch は載せませんが、次回 release にて修正し
ておきます。
> 試しに該当箇所の Xtcsetattr() を素の tcsetattr() に書換え
> てみると、誰かが SIGSTOP を発行していることに気づくと思いま
> す。
これ SIGSTOP ではなくて SIGTTIN でした。SIGTTIN を無視して
先に進ませると SIGSTOP で割込まれていただけのことで、直接の原
因は SIGTTIN でした。
なら話は早くて、制御端末の設定が何かのタイミングでおかしく
なってしまっただけのことです。で、頻度が小さいのでログに吐か
せて trace してみると、原因が判明しました。
二段パイプの実行時には Bourne shell の作法だと二回 fork()
と waitpid() が実行され、その都度制御端末の切替が行なわれま
す。素直に実行されると下記の順番です。
1.前段用に fork()。制御端末を子に移管。
2.後段用に fork()。制御端末を前段の子に移管。
3.後段の子を waitpid() で待つ。制御端末を親に戻す。
4.前段の子を waitpid() で待つ。制御端末を親に戻す。
これが異常時には 2. -> 3. -> 1. -> 4. の順序で発生していま
した。そうすると 3. で親に戻した制御端末を 1. が子に移管して
しまうので、4. で SIGTTIN が発生してしまいます。
fork() 時の移管は子の側で行なっているのですが、その完遂を
待たずに親が次の作業に移ってしまっていたために、タイミングに
よってはこういう妙な順序が発生してしまっていました。
そこで対処法ですが、子が制御端末の移管を完了するまで親が待
つことにしました。と言っても無限には待てないので、取り敢えず
2 秒で timeout にしています。まぁ普通は 1 秒もかかりません。
このせいで、全体として処理が遅くなるケースが出て来るかとは
思いますが、親ばかり先走って先に進めても足並みが揃わなくなる
だけですので、対処としては正しいと思います。
因みに timeout 時には親側で子を殺すことになるんですが、そ
の良い言い訳を思いつかなかったので取り敢えず SIGPIPE で殺し
ています。
fork() はパイプ以外でも使うので正しくはないのですが、入出
力関連の死因と捉えればそう遠くはないと思います。子側で sleep
して無理矢理発生させてみましたが特に支障ないようです。
という訳で patch を作成してみたのでお試し下さい。
---- Cut Here ----
diff -ur ../old/FD-3.00l/system.c ./system.c
--- ../old/FD-3.00l/system.c Sat Jun 30 00:00:00 2012
+++ ./system.c Thu Jul 12 23:36:48 2012
@@ -146,6 +146,7 @@
#define PS4STR "+ "
#define UNLIMITED "unlimited"
#define MAXTMPNAMLEN 8
+#define WAITFORK 2000 /* msec */
#define getconstvar(s) (getshellvar(s, strsize(s)))
#define constequal(s, c, l) ((l) == strsize(c) && !strnenvcmp(s, c, l))
@@ -527,6 +528,9 @@
#endif
#if !MSDOS
static VOID NEAR setstopsig __P_((int));
+# ifndef NOJOB
+static int NEAR waittermio __P_((p_id_t, int));
+# endif
static p_id_t NEAR makechild __P_((int, p_id_t, int));
#endif /* !MSDOS */
static VOID NEAR safermtmpfile __P_((CONST char *));
@@ -2592,6 +2592,33 @@
}
}
+# ifndef NOJOB
+static int NEAR waittermio(pgrp, job)
+p_id_t pgrp;
+int job;
+{
+ p_id_t tmp;
+ int n;
+
+ if (ttypgrp < (p_id_t)0 || pgrp < (p_id_t)0) return(0);
+ if (!job) {
+ ttypgrp = pgrp;
+ return(0);
+ }
+
+ for (n = 0; n < WAITFORK; n++) {
+ if (gettcpgrp(ttyio, &tmp) < 0) break;
+ if (tmp == pgrp) {
+ ttypgrp = pgrp;
+ return(0);
+ }
+ usleep(1000L);
+ }
+
+ return(-1);
+}
+# endif /* !NOJOB */
+
/*ARGSUSED*/
static p_id_t NEAR makechild(tty, ppid, stop)
int tty;
@@ -2639,7 +2666,13 @@
childpgrp = (ppid >= (p_id_t)0) ? ppid : (pid) ? pid : mypid;
if (pid) {
if (jobok) VOID_C setpgroup(pid, childpgrp);
- if (tty && ttypgrp >= (p_id_t)0) ttypgrp = childpgrp;
+ if (tty && waittermio(childpgrp, jobok) < 0) {
+ VOID_C kill(pid, SIGPIPE);
+ while (!waitjob(pid, NULL, WUNTRACED))
+ if (interrupted) break;
+
+ return((p_id_t)-1);
+ }
# if defined (DEP_PTY) && defined (CYGWIN)
if (parentfd >= 0) addmychild(pid);
# endif
---- Cut Here ----
しらい たかし