From: Tanaka Akira Date: 2010-08-11T11:17:45+09:00 Subject: [ruby-dev:41993] Re: [Bug #3673] PTY.getpty with IO.pipe doesn't finish on FreeBSD 2010年8月11日8:05 NARUSE, Yui : > > あと、ps で wchan を見てみると、以下のようになりますね > % pgrep a.out|xargs procstat > PID PPID PGID SID TSID THR LOGIN WCHAN EMUL COMM > 35305 35304 35304 47112 47112 1 naruse ttyout FreeBSD ELF64 a.out > 35304 47112 35304 47112 47112 1 naruse wait FreeBSD ELF64 a.out これは良い情報です。 ttyout ってことはなにか出力を待っているんですね。 子プロセスも取り除けました。 freebsd8% cat tst.c #include #include #include #include #include int main(int argc, char *argv[]) { int m, s; char *slavedev; if ((m = posix_openpt(O_RDWR|O_NOCTTY)) == -1) { perror("posix_openpt"); exit(1); } if (grantpt(m) == -1) { perror("grantpt"); exit(1); } if (unlockpt(m) == -1) { perror("unlockpt"); exit(1); } if ((slavedev = ptsname(m)) == NULL) { perror("ptsname"); exit(1); } if ((s = open(slavedev, O_RDWR|O_NOCTTY, 0)) == -1) { perror("open"); exit(1); } if (write(m, "a", 1) == -1) { perror("write"); exit(1); } fprintf(stderr, "before close(s)\n"); if (close(s) == -1) { perror("close"); exit(1); } fprintf(stderr, "after close(s)\n"); return 0; } freebsd8% gcc -Wall tst.c freebsd8% ./a.out before close(s) (ここでハング) どうやら、close がブロックしているようですね。 exit も内部的には close 相当のことをするでしょうから、 そこでブロックしているのでしょう。 以下のようにしてもハングします。 % ./ruby -rpty -e ' m, s = PTY.open m.write "a" s.close ' なんで出力があるかというと、おそらく tty のエコーだろうということで、 エコーを抑制するとハングしません。 #include #include #include #include #include #include #include int main(int argc, char *argv[]) { int m, s; char *slavedev; struct termios t; if ((m = posix_openpt(O_RDWR|O_NOCTTY)) == -1) { perror("posix_openpt"); exit(1); } if (grantpt(m) == -1) { perror("grantpt"); exit(1); } if (unlockpt(m) == -1) { perror("unlockpt"); exit(1); } if ((slavedev = ptsname(m)) == NULL) { perror("ptsname"); exit(1); } if ((s = open(slavedev, O_RDWR|O_NOCTTY, 0)) == -1) { perror("open(slavedev)"); exit(1); } if (tcgetattr(s, &t) == -1) { perror("tcgetattr"); } t.c_lflag &= ~(tcflag_t)(ECHO|ECHOE|ECHOK|ECHONL); if (tcsetattr(s, TCSANOW, &t) == -1) { perror("tcsetattr"); } if (write(m, "a", 1) == -1) { perror("write"); exit(1); } if (close(s) == -1) { perror("close"); exit(1); } return 0; } ruby ならこうです。 % ./ruby -rio/console -rpty -e ' m, s = PTY.open s.echo = false m.write "a" s.close ' では、test_script_from_stdin でも、というと、そこが微妙です。 PTY.spawn は slave tty を教えてくれないので、 slave tty に tcsetattr ができません。 FreeBSD だと以下のように master 側に tcsetattr を発行しても動くんですが、 これはポータブルではありません。[ruby-list:28382] % svn diff --diff-cmd diff -x -u test/ruby/test_rubyoptions.rb Index: test/ruby/test_rubyoptions.rb =================================================================== --- test/ruby/test_rubyoptions.rb (revision 28906) +++ test/ruby/test_rubyoptions.rb (working copy) @@ -436,6 +436,7 @@ result = nil s, w = IO.pipe PTY.spawn(EnvUtil.rubybin, out: w) do |r, m| + m.echo = false w.close m.print("\C-d") assert_nothing_raised('[ruby-dev:37798]') do @@ -446,6 +447,7 @@ assert_equal("", result, '[ruby-dev:37798]') s, w = IO.pipe PTY.spawn(EnvUtil.rubybin, out: w) do |r, m| + m.echo = false w.close m.print("$stdin.read; p $stdin.gets\n\C-d") m.print("abc\n\C-d") このテストでは制御端末を変える必要はないと思うので、 PTY.open を使って書き直すのがいいかなぁ。 -- [田中 哲][たなか あきら][Tanaka Akira]