mruby 触り始めました
最近、mrubyを触り始めています。 自分用に最初に作ったのは、SQLite(Google Chromeの閲覧履歴データ)からselectするmrubyのコードをCに組み込んでみたものです。
成果物
取り急ぎOSXの動作しか考慮しておりません。。
以下のようにビルドするとバイナリができます。
gcc -o gchist -I../path/to/mruby/mrbgems -I../path/to/mruby/include -I/usr/include gchist.c -lsqlite3 ../path/to/mruby/build/host/lib/libmruby.a
動作としては、以下のように実行すると、Google Chromeの閲覧履歴からタイトルのリストを標準出力に出すだけ。
$ ./gchist Google GAE で Perl を動かせるなら - あくる日 QuickTime でサポートされるメディア形式を拡充する - Apple サポート QuickTime でサポートされるメディア形式を拡充する - Apple サポート QuickTime でサポートされるメディア形式を拡充する - Apple サポート Play Windows Media on your Mac | Telestream Flip4Mac | Overview Play Windows Media on your Mac | Telestream Flip4Mac | Overview firefox chrome - Google 検索 ・・・ ・・・
または、引数を指定すると、SQL文のLIKEのようにして、タイトルの文字列とマッチするものだけを表示します。
$ ./gchist centos Apache HTTP Server Test Page powered by CentOS 番外 VirtualBoxにインストールしたCentOS6の画面解像度を800×600以上に設定する方法 | Web 覚書ノート 【CentOS 5.5】Grubの設定ファイルが見つからない【カーネルパニック】 - Yahoo!知恵袋 ubuntu上のCentOSを再起動したときにカーネルパニックが発生する場合の対処 - think-t の晴耕雨読 Virtual PC 2007の導入記録(CentOS4.6) - 走り続けたい社内SEブログ [CentOS] Docker の Remote API を HTTP 経由で使えるようにする - Qiita Apache HTTP Server Test Page powered by CentOS
参考情報
- mrubyのビルド
非常にわかりやすくて、とても助かります!!
- 機能のアイディア
以下のアイディアを参考にさせていただき、CLIでChromeの履歴を一覧表示する機能を作成しました。
日経Linuxを立ち読みしていた時に知ったプロダクトです。
日経Linux - 本誌目次 - 2015年3月号:ITpro
mruby_config
SQLiteを使うので、
mgem add mruby-sqlite3
をしておきます。
いろいろなmgemを試していた名残で、不要なものもたくさん入ってしまっていますが、現在のbuild_config.rbは以下のよう。
MRuby::Build.new do |conf| toolchain :gcc conf.bins = %w(mrbc) # mruby's Core GEMs conf.gem 'mrbgems/mruby-bin-mirb' conf.gem 'mrbgems/mruby-bin-mruby' conf.gem 'mrbgems/mruby-array-ext' conf.gem 'mrbgems/mruby-enum-ext' conf.gem 'mrbgems/mruby-eval' conf.gem 'mrbgems/mruby-exit' conf.gem 'mrbgems/mruby-fiber' conf.gem 'mrbgems/mruby-hash-ext' conf.gem 'mrbgems/mruby-math' conf.gem 'mrbgems/mruby-numeric-ext' conf.gem 'mrbgems/mruby-object-ext' conf.gem 'mrbgems/mruby-objectspace' conf.gem 'mrbgems/mruby-print' conf.gem 'mrbgems/mruby-proc-ext' conf.gem 'mrbgems/mruby-random' conf.gem 'mrbgems/mruby-range-ext' conf.gem 'mrbgems/mruby-sprintf' conf.gem 'mrbgems/mruby-string-ext' conf.gem 'mrbgems/mruby-string-utf8' conf.gem 'mrbgems/mruby-struct' conf.gem 'mrbgems/mruby-symbol-ext' conf.gem 'mrbgems/mruby-time' conf.gem 'mrbgems/mruby-toplevel-ext' # user-defined GEMs conf.gem :git => 'https://github.com/iij/mruby-io' conf.gem :git => 'https://github.com/iij/mruby-dir' conf.gem :git => 'https://github.com/iij/mruby-pack' conf.gem :git => 'https://github.com/iij/mruby-socket' conf.gem :git => 'https://github.com/matsumoto-r/mruby-simplehttp' conf.gem :git => 'https://github.com/matsumoto-r/mruby-httprequest' conf.gem :git => 'https://github.com/mattn/mruby-json' conf.gem :git => 'https://github.com/mattn/mruby-http' conf.gem :git => 'https://github.com/matsumoto-r/mruby-mrbgem-template' conf.gem :git => 'https://github.com/matsumoto-r/mruby-sleep.git' conf.gem :git => 'https://github.com/mattn/mruby-sqlite3.git' load '/Users/juchino/myruby/src/ujun/mruby_cross_compiler/mruby-cross-compile-on-mac-osx/mrbgem.rake' end
雑感
mruby自体は、組み込み機器の文脈で話題になることが多いように思いますが、私の場合は、ちょっと目的が違います。
私の場合、制約があってRubyの処理系を導入できないような環境だけど、Rubyでツール書きたい!となることが結構あります。このようなときに、軽いバイナリ一個持って来ればrubyでいろいろ書けてしまうmrubyは、非常に有難いです。
せっかく触り始めたのだし、今後業務で活用していければと思います。
コマンド履歴をスリム化してpecoでインクリメンタルサーチするとき見やすく。
最近、職場でもプライベートでも、 これみたいな感じでpecoを使ってコマンド履歴ベースでいろいろ作業してる。 これが生命線、これがないと生きられない。
Zsh - pecoを使って、コマンド履歴をインクリメンタルに絞り込んでコマンド再実行 - Qiita
ただ、↓みたいに大量にllが残ってしまっているのでなんかなあと思っていた。
QUERY>ll ll ll ll mll ll ll ll ll vim hello_world.rb ll ll ll ll bundle install --path verndor/bundle jruby -S bundle install --path vendor/bundle
とりあえずは以下で、.zsh_historyのコマンドが重複しているところを削除。 (ちなみに、履歴系のシェルのオプションはzshのデフォルトのまま)
#!/usr/bin/env perl my %hash = (); while(<>) { print unless $hash{(split ";", $_)[1]}++; }
重複コマンドを排除するような設定などあるみたいなので、適用してみようかな。
そういえば、.zsh_historyを変更しても、ログインし直さないとhistoryコマンドの結果には反映されなかったな。 historyのキャッシュをリフレッシュする方法がないか探してみよう。
それにしても、自分はなんか考え事するときとか無意識にll連打してるみたいだな。
plenvな環境でのif_perl
先週くらいに、Vimscriptテクニックバイブルを購入し、読んでるんだけど、どうしても解決したいことが1つあるので、吐き出してみる。
Vim script テクニックバイブル ~Vim使いの魔法の杖
- 作者: Vim scriptサポーターズ
- 出版社/メーカー: 技術評論社
- 発売日: 2014/08/06
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
7章の中に、外部言語とインタフェースする方法が書いてあるのでif_perlを試してみているけど、if_perlで採用されるのは、システムperlで固定なのかな???
自分はplenvで複数バージョンを導入しているのだけど、切り替えが反映されず。。。
perl >>EOS # perlスクリプト EOS
#!/usr/bin/perl
をつけてスクリプトを実行しているようなものだと思っている。
vimのコード読むしかないかな。
YAPC::Asia 2014(2日目)に行ってきて、straceを触ってみた。
本ブログ、いつか書こう書こうと思っていて去年くらいにアカウントだけ作ったはいいけど、結局、初投稿がYAPC::Asia 2014のネタとなりました。
一応会社ではインフラ担当という位置づけにいる自分ですが、仕事ではEnterprise臭のするプロダクトばっかり使うので、せめてプライベートではちょっと試してみたい技術は積極的に触ってここに書いてくようにしようとおもいます。
YAPCに行くのは今年が初めてで、そもそも技術系の勉強会に出るように動き出したのも去年の終わりくらいから。きっかけのようなものは特になくて、上に書いたように、せめてプライベートでは、という思いが高まったからだったと思う。。。
YAPC、なんというか、すごい、会場の雰囲気が。初参加のじぶんには、こんなに熱意があってちょっと普通じゃない雰囲気とか、明日からなにか変わるような感じとか、冗談じゃなくそういうのを感じました。むしろ自宅から程近い日吉なんて場所で、こんなにも胸熱なイベントが開催されていることが信じられない感すらありました。
いろいろ貴重な話を聞けたのですが(特にRebuild.fmのリスナーとしては@miyagawaさんや@lestrratさんがラフに話してたPerlあるあるはすごい高まりました)、2014/08/30(土)の2日目のほうのid:zentooさんのトークについて、すぐに試せて有用そうな話題だったので、自分でも簡単に試してみました。
Perl Mongersのためのstrace入門
Perl Mongersのためのstrace入門 - YAPC::Asia Tokyo 2014
スライドは以下です。
帰ってからすごい簡単にですが触ってみたりしました。サンプルではPlackを使ったアプリケーションでしたが、自分はPlackを触ったことがないので、、、単純にApacheのプロセスで試してみました。ちなみに、インフラ担当でありながら、straceをまともに使ったことなかった。。
- やったこと
- わかったこと
- KeepAliveのOn/Offでのリクエストの捌き方の違いを実感。
まず、MPMはpreforkで、設定はデフォルトから変えていなくて、以下のよう。また、KeepAliveはOffの設定。
<IfModule prefork.c> StartServers 8 MinSpareServers 5 MaxSpareServers 20 ServerLimit 256 MaxClients 256 MaxRequestsPerChild 4000 </IfModule>
StartServersが8なので、起動直後は8個のプロセスがリクエストを待ち受ける。
$ ps aux | grep httpd | grep -v grep root 2312 0.0 0.6 177792 3888 ? Ss 06:27 0:00 /usr/sbin/httpd apache 2318 0.0 0.5 177928 3080 ? S 06:27 0:00 /usr/sbin/httpd apache 2319 0.0 0.5 177928 3080 ? S 06:27 0:00 /usr/sbin/httpd apache 2320 0.0 0.5 177928 3084 ? S 06:27 0:00 /usr/sbin/httpd apache 2321 0.0 0.5 177928 3080 ? S 06:27 0:00 /usr/sbin/httpd apache 2322 0.0 0.5 177928 3084 ? S 06:27 0:00 /usr/sbin/httpd apache 2323 0.0 0.5 177928 3088 ? S 06:27 0:00 /usr/sbin/httpd apache 2324 0.0 0.5 177928 3080 ? S 06:27 0:00 /usr/sbin/httpd apache 2325 0.0 0.5 177928 3080 ? S 06:27 0:00 /usr/sbin/httpd
次に、tmuxで8個仮想端末を作って、それぞれで上記のhttpdプロセスにstraceでアタッチ。
$ sudo strace -p 2318 ~ $ sudo strace -p 2325
サンプルとして、以下のコンテンツにブラウザから1回リクエストしてみます。
$ cat /var/www/html/index.html <html> <head> </head> <body> <img src="http://127.0.0.1:8080/sample1.png" /> <img src="http://127.0.0.1:8080/sample2.jpg" /> </body> </html>
すると、PID:2318、PID:2319、PID:2320のstraceの出力にそれぞれ以下の結果を確認。
PID:2318 accept4(4, {sa_family=AF_INET6, sin6_port=htons(45800), inet_pton(AF_INET6, "::ffff:10.0.2.2", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28], SOCK_CLOEXEC) = 10 getsockname(10, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "::ffff:10.0.2.15", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0 fcntl(10, F_GETFL) = 0x2 (flags O_RDWR) fcntl(10, F_SETFL, O_RDWR|O_NONBLOCK) = 0 read(10, "GET / HTTP/1.1\r\nHost: localhost:"..., 8000) = 418 stat("/var/www/html/", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 stat("/var/www/html/index.html", {st_mode=S_IFREG|0644, st_size=158, ...}) = 0 open("/var/www/html/index.html", O_RDONLY|O_CLOEXEC) = 11 fcntl(11, F_GETFD) = 0x1 (flags FD_CLOEXEC) fcntl(11, F_SETFD, FD_CLOEXEC) = 0 close(11) = 0 brk(0x7ffac326e000) = 0x7ffac326e000 writev(10, [{"HTTP/1.1 304 Not Modified\r\nDate:"..., 149}], 1) = 149 write(7, "10.0.2.2 - - [01/Sep/2014:07:26:"..., 148) = 148 shutdown(10, 1 /* send */) = 0 poll([{fd=10, events=POLLIN}], 1, 2000) = 1 ([{fd=10, revents=POLLIN|POLLHUP}]) read(10, "", 512) = 0 close(10) = 0 read(5, 0x7fff4f57657f, 1) = -1 EAGAIN (Resource temporarily unavailable) accept4(4,
PID:2319 accept4(4, {sa_family=AF_INET6, sin6_port=htons(45804), inet_pton(AF_INET6, "::ffff:10.0.2.2", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28], SOCK_CLOEXEC) = 10 getsockname(10, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "::ffff:10.0.2.15", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0 fcntl(10, F_GETFL) = 0x2 (flags O_RDWR) fcntl(10, F_SETFL, O_RDWR|O_NONBLOCK) = 0 read(10, "GET /sample1.png HTTP/1.1\r\nHost:"..., 8000) = 435 stat("/var/www/html/sample1.png", {st_mode=S_IFREG|0644, st_size=342085, ...}) = 0 open("/var/www/html/sample1.png", O_RDONLY|O_CLOEXEC) = 11 fcntl(11, F_GETFD) = 0x1 (flags FD_CLOEXEC) fcntl(11, F_SETFD, FD_CLOEXEC) = 0 close(11) = 0 brk(0x7ffac326e000) = 0x7ffac326e000 writev(10, [{"HTTP/1.1 304 Not Modified\r\nDate:"..., 152}], 1) = 152 write(7, "10.0.2.2 - - [01/Sep/2014:07:26:"..., 180) = 180 shutdown(10, 1 /* send */) = 0 poll([{fd=10, events=POLLIN}], 1, 2000) = 1 ([{fd=10, revents=POLLIN|POLLHUP}]) read(10, "", 512) = 0 close(10) = 0 read(5, 0x7fff4f57657f, 1) = -1 EAGAIN (Resource temporarily unavailable) accept4(4,
PID:2320 accept4(4, {sa_family=AF_INET6, sin6_port=htons(45799), inet_pton(AF_INET6, "::ffff:10.0.2.2", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28], SOCK_CLOEXEC) = 10 getsockname(10, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "::ffff:10.0.2.15", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0 fcntl(10, F_GETFL) = 0x2 (flags O_RDWR) fcntl(10, F_SETFL, O_RDWR|O_NONBLOCK) = 0 read(10, "GET /sample2.jpg HTTP/1.1\r\nHost:"..., 8000) = 435 stat("/var/www/html/sample2.jpg", {st_mode=S_IFREG|0644, st_size=77451, ...}) = 0 open("/var/www/html/sample2.jpg", O_RDONLY|O_CLOEXEC) = 11 fcntl(11, F_GETFD) = 0x1 (flags FD_CLOEXEC) fcntl(11, F_SETFD, FD_CLOEXEC) = 0 close(11) = 0 brk(0x7ffac326e000) = 0x7ffac326e000 writev(10, [{"HTTP/1.1 304 Not Modified\r\nDate:"..., 152}], 1) = 152 write(7, "10.0.2.2 - - [01/Sep/2014:07:26:"..., 180) = 180 shutdown(10, 1 /* send */) = 0 poll([{fd=10, events=POLLIN}], 1, 2000) = 1 ([{fd=10, revents=POLLIN|POLLHUP}]) read(10, "", 512) = 0 close(10) = 0 read(5, 0x7fff4f57657f, 1) = -1 EAGAIN (Resource temporarily unavailable) accept4(4,
なるほど、3つのTCPリクエスト(index.htmlへとsample1.pngへとsampl2.jpgへ)が来て別々のプロセスがレスポンスしているのですね。 トークでは、acceptで返ってきたファイルディスクリプタ(上だと10。以下、fd)を追うのが定石とのことでした。ざっくり見ると、fd10をreadしてGETリクエストを読み込んでから、リクエストされたリソースをfd11でオープンして、内容をfd10に書き込んでいるということでしょうか。最後にfd10をクローズしてからacceptで次回のリクエストを待機しています。
次に画面をリロードすると、今度はPID:2321、PID:2322、PID:2323のstraceに同じような挙動が。 リクエストがくる度に、ラウンドロビンで応答するプロセスを回しているんですね。
ここで、KeepAliveをOnにしてみました。
同じようにブラウザからアクセスすると、
PID:2586 accept4(4, {sa_family=AF_INET6, sin6_port=htons(45931), inet_pton(AF_INET6, "::ffff:10.0.2.2", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28], SOCK_CLOEXEC) = 10 getsockname(10, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "::ffff:10.0.2.15", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0 fcntl(10, F_GETFL) = 0x2 (flags O_RDWR) fcntl(10, F_SETFL, O_RDWR|O_NONBLOCK) = 0 read(10, "GET / HTTP/1.1\r\nHost: localhost:"..., 8000) = 418 stat("/var/www/html/", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0 stat("/var/www/html/index.html", {st_mode=S_IFREG|0644, st_size=158, ...}) = 0 open("/var/www/html/index.html", O_RDONLY|O_CLOEXEC) = 11 fcntl(11, F_GETFD) = 0x1 (flags FD_CLOEXEC) fcntl(11, F_SETFD, FD_CLOEXEC) = 0 close(11) = 0 writev(10, [{"HTTP/1.1 304 Not Modified\r\nDate:"..., 187}], 1) = 187 read(10, 0x7f3dfa150768, 8000) = -1 EAGAIN (Resource temporarily unavailable) write(7, "10.0.2.2 - - [01/Sep/2014:07:41:"..., 148) = 148 poll([{fd=10, events=POLLIN}], 1, 15000) = 1 ([{fd=10, revents=POLLIN}])
PID:2587 accept4(4, {sa_family=AF_INET6, sin6_port=htons(45932), inet_pton(AF_INET6, "::ffff:10.0.2.2", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28], SOCK_CLOEXEC) = 10 getsockname(10, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "::ffff:10.0.2.15", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0 fcntl(10, F_GETFL) = 0x2 (flags O_RDWR) fcntl(10, F_SETFL, O_RDWR|O_NONBLOCK) = 0 read(10, "GET /sample1.png HTTP/1.1\r\nHost:"..., 8000) = 435 stat("/var/www/html/sample1.png", {st_mode=S_IFREG|0644, st_size=342085, ...}) = 0 open("/var/www/html/sample1.png", O_RDONLY|O_CLOEXEC) = 11 fcntl(11, F_GETFD) = 0x1 (flags FD_CLOEXEC) fcntl(11, F_SETFD, FD_CLOEXEC) = 0 close(11) = 0 writev(10, [{"HTTP/1.1 304 Not Modified\r\nDate:"..., 190}], 1) = 190 read(10, 0x7f3dfa150768, 8000) = -1 EAGAIN (Resource temporarily unavailable) write(7, "10.0.2.2 - - [01/Sep/2014:07:41:"..., 180) = 180 poll([{fd=10, events=POLLIN}], 1, 15000) = 1 ([{fd=10, revents=POLLIN}])
PID:2588 accept4(4, {sa_family=AF_INET6, sin6_port=htons(45933), inet_pton(AF_INET6, "::ffff:10.0.2.2", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28], SOCK_CLOEXEC) = 10 getsockname(10, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, "::ffff:10.0.2.15", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0 fcntl(10, F_GETFL) = 0x2 (flags O_RDWR) fcntl(10, F_SETFL, O_RDWR|O_NONBLOCK) = 0 read(10, "GET /sample2.jpg HTTP/1.1\r\nHost:"..., 8000) = 435 stat("/var/www/html/sample2.jpg", {st_mode=S_IFREG|0644, st_size=77451, ...}) = 0 open("/var/www/html/sample2.jpg", O_RDONLY|O_CLOEXEC) = 11 fcntl(11, F_GETFD) = 0x1 (flags FD_CLOEXEC) fcntl(11, F_SETFD, FD_CLOEXEC) = 0 close(11) = 0 writev(10, [{"HTTP/1.1 304 Not Modified\r\nDate:"..., 190}], 1) = 190 read(10, 0x7f3dfa14c748, 8000) = -1 EAGAIN (Resource temporarily unavailable) write(7, "10.0.2.2 - - [01/Sep/2014:07:41:"..., 180) = 180 poll([{fd=10, events=POLLIN}], 1, 15000) = 1 ([{fd=10, revents=POLLIN}])
PID:2586、PID:2587、PID:2588に対してさっきと同じような出力を確認(ただし、fd10をクローズしていないな)。
で、画面をリロード。
すると、またもやPID:2586、PID:2587、PID:2588が反応。1回1回のリクエストごとに接続をクローズしないで、使いまわしているのがわかりました。KeepAliveTimeoutが15(上記の最後の出力で、pollのタイムアウト値が15000msとなっているのがわかる)なので、15秒以上空けてからリクエストすると、応答するプロセスがスイッチして、PID:2589以降のプロセスが処理します。また、MaxKeepAliveRequestが100なので、100回リロード後には、やはりPID:2589以降のプロセスが処理します。
個々の細かいシステムコールとかあんまり理解していませんが、今回はこんなところまでわかりました。
これがKeepAliveの設定の違いなのか。。今まで全然実感なくほいほい設定していたけど。こんなことも分かっていなかったのは自分だけなのかもしれませんが、実感を持ってミドルのチューニングするのは重要だと思うので、よい経験になりました。
Perlと全然関係ない内容になってしまいましたが、これもYAPC::Asiaに参加しなければ得られなかった知見です。発表者の方には本当に感謝です。すばらしい時間を過ごせました。
せっかくだし、これを機にいろんな勉強会に足を運んでみたいと強く感じました。