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

[FDclone-users:00171] Re: {FreeBSD,OpenBSD}/sparc64, IA64(AIX対応その後)



 しらいです。

In Message-Id <03May9.141028jst.119051@inetgw.lightwell.co.jp>
        SHIOTA Shoichi <Shoichi.Shiota@lightwell.co.jp>さんwrites:
> 潮田です。

> > 2. ./fdsh で sleep や /bin/echo 等の子 process を起動する。
> 正常に終了しますと書こうとしたのですが、
> たまに誤動作があるようです。

 タイミングの問題ですね。fork() の後、子が親が看取るより先
に終了してしまった場合、親が wait() し始める前に SIGCHLD を
発呼してしまうんだと思います。
 waitchild() の中で waitjob() を呼んでいる箇所がありますが、
ここに sleep() を挿入するとほぼ 100% の再現率になるんじゃな
いでしょうか。

 対処法としては
	1. fork() -> wait() の間 SIGCHLD を block する。
	2. handler 内 wait() 時に看取った子を全て記録する。
の二通りが考えられますが、1. の場合、親子のうちどちらが先に
fork() を抜けられるかが特定出来ないので、fork() 直後に block
したとしても完璧な対処にはなり得ません。
 2. の対処法で patch を作成してみましたので、度々ですみませ
んが、また 2.03a に戻して一からやり直してみて下さい。

---- Cut Here ----
diff -u ../old/FD-2.03a/system.c ./system.c
--- ../old/FD-2.03a/system.c	Tue Apr 15 00:00:00 2003
+++ ./system.c	Wed May  9 00:00:00 2003
@@ -2219,9 +2219,16 @@
 #ifdef SIGCHLD
 static int trap_chld(VOID_A)
 {
-# if	!MSDOS && !defined (NOJOB)
-	if (bgnotify) checkjob(1);
-# endif
+# if	!MSDOS
+	if (0);
+#  ifndef	NOJOB
+	else if (bgnotify) checkjob(1);
+#  endif
+#  ifdef	SYSV
+	/* TIPS for obsolete SystemV signal() */
+	else while (waitjob(-1, NULL, WNOHANG | WUNTRACED) > 0);
+#  endif
+# endif	/* !MSDOS */
 	return(trap_common(SIGCHLD));
 }
 #endif
@@ -2646,13 +2705,24 @@
 wait_t *wp;
 int opt;
 {
+# if	!defined (NOJOB) || defined (SYSV)
+	static wait_t *waitlist = NULL;
+	static p_id_t *pidlist = NULL;
+	static int maxtrap = 0;
+# endif
 # ifndef	NOJOB
 	int i, j, sig;
 # endif
+	sigmask_t mask, omask;
 	wait_t w;
 	p_id_t tmp;
 	int ret;

+	Xsigemptyset(mask);
+# ifdef	SIGCHLD
+	Xsigaddset(mask, SIGCHLD);
+# endif
+	Xsigblock(omask, mask);
 	for (;;) {
 # ifndef	JOBTEST
 		tmp = (pid < 0L)
@@ -2684,13 +2754,42 @@
 		if (tmp >= 0L || errno != EINTR) break;
 	}

+# if	!defined (NOJOB) || defined (SYSV)
+	if (pid >= 0L) {
+		if (tmp < 0L && errno == ECHILD) {
+			int n;
+
+			for (n = 0; n < maxtrap; n++) if (pid == pidlist[n]) {
+				tmp = pid;
+				w = waitlist[n];
+				break;
+			}
+		}
+		maxtrap = 0;
+		if (waitlist) {
+			free(waitlist);
+			waitlist = NULL;
+		}
+		if (pidlist) {
+			free(pidlist);
+			pidlist = NULL;
+		}
+	}
+	else if (tmp >= 0L) {
+		waitlist = (wait_t *)realloc2(waitlist,
+			(maxtrap + 1) * sizeof(wait_t));
+		pidlist = (p_id_t *)realloc2(pidlist,
+			(maxtrap + 1) * sizeof(p_id_t));
+		pidlist[maxtrap] = tmp;
+		waitlist[maxtrap] = w;
+		maxtrap++;
+	}
+# endif	/* !NOJOB || SYSV */
+	Xsigsetmask(omask);
+
 	if (!tmp) return(0);
 	else if (tmp < 0L) {
-		if (pid < 0L || errno != ECHILD) return(-1);
-		ret = -1;
-# ifndef	NOJOB
-		sig = -1;
-# endif
+		return(-1);
 	}
 	else {
 		ret = (pid < 0L || tmp == pid) ? 1 : 0;
---- Cut Here ----

# malloc()/free() は reentrant でないので handler 内では使
#いたくなかったのですが、他に実装を思いつかないので、最大限
#race condition を回避する手段として critical section の前
#後で SIGCHLD を block しています。
# 多分、正解は siglongjmp() なんでしょうけどね。


> > 5. ./fdsh で set -b を実行した後 2., 3. と同様の検証。
> 2. 3. とも正常に終了します。

 set -b した場合もタイミングによっては ECHILD で error にな
る筈です。sleep() を挿入してみると明確になるでしょう。

                                               しらい たかし