前回のアップデートからかなり時間が空いてしまったので、最近またちょいちょい触り始めている。 ある程度変更が溜まった、というかブログを書いたほうがモチベーションが上がりそうなのでバージョンをあげてみた。 今後も、ある程度変更が溜まったらバージョンをあげてブログ書くみたいなやり方で当分やってみよとおもう。
前回からのコミット
- 6aa0f1c Use process group
- 3c2e1a7 Add SIGINT trap
- b7ac406 Resolve undefined types and constants in VSCode.
- 7ee7d9d Remove unused CFLAGS
- dab7edc Fix typo
- 995aeb9 Split builtin commands
- 09da21c typo
- 3056c6b Use mruby-2.1.2
- ce78dae Temporarily disable
- fe2d4cb Improve jobcontrol test
- 7797937 Handle command not found.
- 1bfca6b v0.5.0
プロセスグループを使うようにした
前回の記事で少し書いたプロセスグループを実装した。
プロセスグループとはプロセスをグルーピングする機能で、Linuxにおいてはこのプロセスループに対してシグナルを飛ばすことでグループに所属しているすべてのプロセスに透過的にシグナルを送ることができる。
プロセスグループは、 ps xao pid,pgid,comm
とかすると確認することができる。
プロセスグループはグループの中で最初に起動したプロセスのPIDにするのが一般的のようだ。
例えば A | B | C
みたいなコマンドラインにおいては、AのPIDがプロセスグループID(PGID)になり、BとCもそのPGIDに所属することになる。
プロセスグループに所属させるためにはsetpgid(2)を叩く必要があるのだが、CRubyではProcess#setpgidがあるのだがmrubyにおいてはなかったので実装した。 https://github.com/buty4649/reddish-shell/tree/master/mrbgems/mruby-process-pgrp/src
プロセスグループを使うようにしたのだが、ここでも問題が発生した。
プロセスグループに所属するためには、所属したいPGIDが存在していないといけないのだが、A | B | C
というコマンドラインにおいてBやCが起動してsetpgidする前にAの実行が終わってしまうとエラーになるということがあった。
これを回避するためにbashでは、どうもAのプロセスを起動してからAに渡すpipeをreadし続けて、Bが起動したらそのpipeを閉じてreadを終了させるみたいな手法を取っているように見えた。
https://github.com/bminor/bash/blob/76404c85d492c001f59f2644074333ffb7608532/jobs.c#L5089-L5108
これを真似したかったのだが、mrubyにおいてスレッドを使うのはかなり難しいので断念した。
次にkshの実装をみた。こちらはSIGTTINをAに送って、Aを一時停止して各プロセスの起動が終わったら復帰させるみたいな処理になっているように見えた。 https://github.com/ksh2020/ksh/blob/8cf92b281a8d5da075b28e4421ec7ec0ab0de1ce/src/cmd/ksh93/sh/jobs.c#L524-L537
結局、reddishではどうしたかというと・・・忘れた。 実装当時は覚えていたのだけど、この記事を書いている現在ではすっかり忘れてしまってどうしたのかは思い出せない。。 コードを見た感じ、bashやkshのようなことはやっておらずどうしたんだっけかなぁ・・・という感じになっている。 もう少しコードが複雑になってきたりしたら、もしかしたらここらへんバグるかもしれない…。
SIGINTをトラップするスレッドを起動するmgemを作った
前回まではmruby-single-threadを使ってトラップしてたのだが、SEGVしてしまうのでオミットしたと書いた。 これを復活させるために新たにmrb-signal-trapというmgemを作った。 かなりシンプルな作りで、スレッドを起動してsigwaitを呼び出しSIGINTを待ち続けて、SIGINTが飛んできたらプロセスグループに対してkillを送るという作りになっている。 プロセスグループの実装と、mrb-signal-trapの実装で正しくプロセスをCtrl+Cできるようになった。
murby-2.1.2にアップデートした
今までmruby-2.1.0でビルドしていたのだけど、これを2.1.2に上げた。 2.1.2にあげたことでひたすら壊れたとか、大幅な修正もなくてよかった。 アップデートによりmruby-io周りが変更されたようで、mrb_io_filenoの戻り値がintになったり、存在しないfiledescriptorをIO.newするとEBADFになるようになったのでIO.dup2してからIO.newするように変更した。
ジョブコントールのbintestを見直した
起動したプロセスにCtrl+Cを送って停止するかを確認するbintestがある。
今までは、forkして reddish -c signaltest
みたいなコマンドを実行してその後にSIGINTを送っていた。
しかしこの方法ではうまく動かなかった。
そこで、PTYモジュールを使ってPTYをreddishに割り当てて操作するように変更した。
ただし、PTYモジュールに変更しただけではうまく動かなかったので、linenoiseを使わないようにするオプション(-i)を追加した。
存在しないコマンドを実行したときにスタックする問題を解決した
このバグいつからあったんだろうなぁ…。