[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[FDclone-users:00477] Re: 疑似端末有効かつバックグラウンドプロセスを起動すると LOG_DIR しても PWD が変わらない



 しらいです。

In Message-Id <060322224720.M0216040@mix.syd.odn.ne.jp>
        hma@syd.odn.ne.jpさんwrites:
> 濱崎です。

> LOG_DIR(l キー) で表示ディレクトリ を変えても、
> 環境変数 PWD が変わりません。
> バックグラウンドプロセスを実行したときのディレクトリのままです。

 これは PWD 設定は本質ではなくて、current working directory
(CWD) が変化しないということですよね。


> 使用していて不便に感じるのは、
> LOG_DIR 後に EXECUTE_FILE(x キー)で、あるファイルを対象に
> コマンドを実行しようとした時に
> No such file or directory
> とエラーになってしまうことです。

 こういう時は、「&」の代わりに「&|」を使って disown してや
ればいいと思います。
 もしくは、background process が一瞬で終わるようなものであ
れば、「ls & sleep 1」のように待ち時間を設けて、process が完
全に終了するのを待ってから EXECUTE_SH を抜けるようにするのも
有効です。


> FDclone の中の仮想端末について、理解不足なので
> これを、バグと呼んでいいのかどうか迷っています。
> 「バックグラウンドで実行したプロセスが終了するまでは
>   環境を変化させることができない」
> のだとすると、これはこれで仕方ないのかなと思います。

 仕様と言えば仕様ですね。UNIX の枠組ではこれはどうしようも
ない現象です。

 MS-DOS と違って UNIX では process 毎に異なる CWD を持って
いますから、親 process と言えど別の process である子 process
の CWD を変更することは出来ません。
 これを行なうためには process 間通信により双方の process が
お互いの了承の下で CWD の変更手続きをとる必要があります。

 FDclone での process 間通信は必ず一方通行で、
	子 process -> 親 process
	親 process -> terminal emulator
という経路しか用意していません。
 子 process 稼働中は、親 process は子 process を監視してい
ますし、terminal emulator は親 process からの通信を待ってい
ます。
 これら二つの process がどの phase にいるか明確なのに対し、
子 process の方は一体何を行なっているかは全く不明です。外部
process を起動していたりするともう通信には応答出来ません。
 なので、子 process が行なった chdir の結果を親 process に
反映させることは出来ても、その逆は出来ないんですね。親の心子
知らずで、子は勝手なタイミングで勝手なことをしてますから。

 子が foreground の場合は、EXECUTE_SH の度に違う子が起動す
るので、起動前の親の CWD を引継ぎますが、子が background の
場合は二度目の EXECUTE_SH でも前の子が生き残っています。
 正確に言うと background なのは子ではなくて孫なのですが、子
は disown しない限りは孫を看取る必要があるので、孫が生き続け
ている限りは先に死ぬ訳にはいかないんです。
 なので、二度目の EXECUTE_SH の時に前の子を生き残らせないよ
うにするには、確実に孫が終了するまで待つか、「&|」で disown
してやるかしかないのです。

 実際他の shell でも background process の CWD は変更出来な
いのでその旨の表示をしていますよね。
	bash-3.00$ pwd
	/tmp
	bash-3.00$ echo &
	[1] 606
	bash-3.00$
	cd ..
	[1]+  Done                    echo  (wd: /tmp)
	(wd now: /)
	bash-3.00$ 
 FDclone でもこれと同じようなことが起こっていると思って下さ
い。

 勿論、「ls & sleep 1」に相当することを FDclone の側で自動
的に行なうことは出来ますが、何秒待てば十分なのかは環境依存な
ので、ひょっとしたら終了しないかも知れない子のために必ず何秒
も待たせるのは非実用的ですよね。
 逆に、環境によっては孫が完了するのが早過ぎて、子が孫を看取
ってから EXECUTE_SH を終了するので、「sleep 1」を入れなくて
も症状が収まるかも知れません。
 なので、環境依存性なしにこの問題を解決するのは難しいんです
ね。


 さて、下らない蘊蓄はこのくらいにしまして、patch です。

 あれだけ「実装不能」と言っておいてなんですが、環境依存性を
ある程度吸収した上で何とか状況に対処することは出来ます。
 孫が死んだことを子が知るには、明示的に wait(2) で待つ以外
に SIGCHLD で感知する方法があります。bgnotify flag を有効に
すると、明示的に待っている時以外に死んだ場合も感知出来ます。
 なので、PTYMODE=1 の時には bgnotify flag を有効にしてやる
ことで、EXECUTE_SH から抜けるぎりぎりまで孫の死を待つことが
可能です。

 勿論タイミングの問題なので、そのぎりぎりを過ぎて孫が完了し
た場合は間に合いません。でも、大概はこの程度の対処で何とかな
ると思います。
 うだうだ御託を並べたのは、対処が完全ではないので対処しきれ
なかった時のための言い訳ですね。この patch でも症状が解消さ
れない場合は「間に合わなかった」と理解して下さい。
 確実に対処可能な訳ではないので、HISTORY には挙げませんけど、
この patch は一応次期 release には含めておこうと思います。

---- Cut Here ----
diff -u ../old/FD-2.08a/system.c ./system.c
--- ../old/FD-2.08a/system.c	Tue Feb  7 00:00:00 2006
+++ ./system.c	Thu Mar 23 01:08:12 2006
@@ -1871,9 +1894,25 @@
 #if	defined (FD) && defined (SIGALRM)
 			noalrm--;
 #endif
-			for (i = 0; i < NSIG; i++)
+			for (i = 0; i < NSIG; i++) {
+#if	defined (SIGCHLD) && !defined (NOJOB)
+# if	!defined (FD) || defined (_NOPTY)
+				if (i == SIGCHLD && bgnotify)
+# else
+				if (i == SIGCHLD
+				&& (bgnotify || parentfd >= 0))
+# endif
+				{
+					int n;
+
+					for (n = 0; n < maxjobs; n++)
+						if (joblist[n].pids) break;
+					if (n < maxjobs) continue;
+				}
+#endif	/* SIGCHLD && !NOJOB */
 				if (oldsigfunc[i] != SIG_ERR)
 					signal2(i, oldsigfunc[i]);
+			}
 #if	!MSDOS
 			Xsigsetmask(oldsigmask);
 #endif
@@ -2035,7 +2051,12 @@
 #if	defined (SIGCHLD) && (!defined (NOJOB) || defined (SYSV))
 		case SIGCHLD:
 # ifndef	NOJOB
-			if (bgnotify) {
+#  if	!defined (FD) || defined (_NOPTY)
+			if (bgnotify)
+#  else
+			if (bgnotify || (!setsigflag && parentfd >= 0))
+#  endif
+			{
 				checkjob(1);
 				break;
 			}
diff -u ../old/FD-2.08a/termemu.c ./termemu.c
--- ../old/FD-2.08a/termemu.c	Tue Feb  7 00:00:00 2006
+++ ./termemu.c	Thu Mar 23 01:37:00 2006
@@ -24,6 +24,7 @@
 #else
 #include "system.h"
 # ifndef	NOJOB
+extern int bgnotify;
 extern jobtable *joblist;
 extern int maxjobs;
 # endif
@@ -834,6 +835,10 @@
 				if (joblist[i].pids) break;
 			if (i >= maxjobs) break;
 			VOID_C signal2(SIGHUP, (sigcst_t)trap_hup);
+# ifdef	SIGCHLD
+			if (!bgnotify)
+				VOID_C signal2(SIGCHLD, SIG_DFL);
+# endif
 			sendparent(TE_CHANGESTATUS, n);
 			if (recvmacro(&command, &arg, &flags) < 0) break;
 #endif	/* !_NOORIGSHELL && !NOJOB */
---- Cut Here ----

                                               しらい たかし