[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[FDclone-users:00171] Re: {FreeBSD,OpenBSD}/sparc64, IA64(AIX対応その後)
- Subject: [FDclone-users:00171] Re: {FreeBSD,OpenBSD}/sparc64, IA64(AIX対応その後)
- From: Takashi SHIRAI <shirai@unixusers.net>
- Date: Fri, 09 May 2003 19:04:15 +0900
しらいです。
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() を挿入してみると明確になるでしょう。
しらい たかし