プロフィール

kosaki

Author:kosaki
連絡先はコチラ

ブログ検索
最近の記事
最近のコメント
最近のトラックバック
リンク
カテゴリー
月別アーカイブ
RSSフィード
FC2ブログランキング

スポンサーサイト このエントリーをはてなブックマークに追加

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。


スポンサー広告 | 【--------(--) --:--:--】 | Trackback(-) | Comments(-)

systemの挙動について このエントリーをはてなブックマークに追加

ちょっとakrさんと議論する機会があったのでメモ

現状のRubyだと子プロセス実行中に Ctrl-Cが効かないという問題がある

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/31007

実際に、困っているひともいて。Rubyのtarに含まれているmake testが子プロセスをつくって子供がテストして結果をかえすというスタイルなので、Ctrl-Cで中断できなくて開発者は日々イライラしている。


で、このスレッドで、そうなっている理由が明らかに。

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-dev/31117


ところで、別件であるがperl の真似をしたといいつつ、perlはSIGHUPをマスクしてはいない。
ちょっと気になったので実装をいくつか調べてみた。

Perl のコード

if (childpid > 0) {
Sigsave_t ihand,qhand; /* place to save signals during system() */
int status;

if (did_pipes)
PerlLIO_close(pp[1]);
#ifndef PERL_MICRO
rsignal_save(SIGINT, (Sighandler_t) SIG_IGN, &ihand);
rsignal_save(SIGQUIT, (Sighandler_t) SIG_IGN, &qhand);
#endif
do {
result = wait4pid(childpid, &status, 0);
} while (result == -1 && errno == EINTR);
#ifndef PERL_MICRO
(void)rsignal_restore(SIGINT, &ihand);
(void)rsignal_restore(SIGQUIT, &qhand);
#endif


SIGINTとSIGQUITは無視してるけど、SIGHUPの手当はなし。また、forkしたあとでSIG_IGNしてるからレースがある



次、glibc

if (__sigaction (SIGINT, &sa, &intr) < 0)
{
SUB_REF ();
goto out;
}
if (__sigaction (SIGQUIT, &sa, &quit) < 0)
{
save = errno;
SUB_REF ();
goto out_restore_sigint;
}
}

pid = __fork ();
if (pid == (pid_t) 0)
{
/* Child side. */
const char *new_argv[4];
new_argv[0] = SHELL_NAME;
new_argv[1] = "-c";
new_argv[2] = line;
new_argv[3] = NULL;

/* Restore the signals. */
(void) __sigaction (SIGINT, &intr, (struct sigaction *) NULL);
(void) __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL);
(void) __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL);
INIT_LOCK ();

/* Exec the shell. */
(void) __execve (SHELL_PATH, (char *const *) new_argv, __environ);
_exit (127);
}
else if (pid < (pid_t) 0)
/* The fork failed. */
status = -1;
else
/* Parent side. */
{
/* Note the system() is a cancellation point. But since we call
waitpid() which itself is a cancellation point we do not
have to do anything here. */
if (TEMP_FAILURE_RETRY (__waitpid (pid, &status, 0)) != pid)
status = -1;
}

if ((SUB_REF () == 0
&& (__sigaction (SIGINT, &intr, (struct sigaction *) NULL)
| __sigaction (SIGQUIT, &quit, (struct sigaction *) NULL)) != 0)
|| __sigprocmask (SIG_SETMASK, &omask, (sigset_t *) NULL) != 0)
{
status = -1;
}

return status;
}


適当に削ったけど、ようするに
・SIGINTとSIGQUITはSIG_IGN。SIGHUPのケアはなし
・forkする前にシグナルハンドラを変えているのでレースなし。えらい

次、bash。
うーん、正直ソースが汚くてよくわからないけど、たぶんこれ。


void
setup_async_signals ()
{
#if defined (__BEOS__)
set_signal_handler (SIGHUP, SIG_IGN); /* they want csh-like behavior */
#endif

#if defined (JOB_CONTROL)
if (job_control == 0)
#endif
{
set_signal_handler (SIGINT, SIG_IGN);
set_signal_ignored (SIGINT);
set_signal_handler (SIGQUIT, SIG_IGN);
set_signal_ignored (SIGQUIT);
}
}


たぶん、これだろう。asyncってのは command & のような奴、で job_control == 0は
!インタラクティブシェル みたいな意味。

なので、

o インタラクティブシェルはCtrl-Cでバンバン死ぬ
o バッチでも & 使わないケースでは普通に死ぬ(なんでだ??)
o SIGINT と SIGQUITは無視。SIGHUPはノーケア。BeOS?しらんしらん。

$(()) のケースとかはめんどくさいことやってるが、読むのめんどいから無視無視。
終了ステータスみて、SIGINTだったら自分にたいしてSIGINT送り直すとかやってた。
なんでコマンド置換のときだけ、そういう気の利いたことするのかはよくわからん。


次、tcsh。コードは結構綺麗なのに読めないのは私の修行が足りないから
うーん、forkの時とかに一時的にSIGINTをBLOCKしているほかはノーガード戦法に見える

関連記事


linux | 【2011-11-11(Fri) 08:10:45】 | Trackback:(0) | Comments:(0)
  1. 無料アクセス解析
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。