ujunのブログ

mruby 触り始めました

最近、mrubyを触り始めています。 自分用に最初に作ったのは、SQLite(Google Chromeの閲覧履歴データ)からselectするmrubyのコードをCに組み込んでみたものです。

成果物

github.com

取り急ぎ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のビルド

blog.matsumoto-r.jp

非常にわかりやすくて、とても助かります!!

  • 機能のアイディア

以下のアイディアを参考にさせていただき、CLIChromeの履歴を一覧表示する機能を作成しました。

github.com

日経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 テクニックバイブル ~Vim使いの魔法の杖

7章の中に、外部言語とインタフェースする方法が書いてあるのでif_perlを試してみているけど、if_perlで採用されるのは、システムperlで固定なのかな???

自分はplenvで複数バージョンを導入しているのだけど、切り替えが反映されず。。。

今のとこの自分の解釈は、vimスクリプト中で、

perl >>EOS

# perlスクリプト

EOS

で書いた部分は、if_perlshebang

#!/usr/bin/perl

をつけてスクリプトを実行しているようなものだと思っている。

vimのコード読むしかないかな。

いや、そもそも+perlvimを想定してプラグインをつくろうとする姿勢がダメかも。。

YAPC::Asia 2014(2日目)に行ってきて、straceを触ってみた。

 本ブログ、いつか書こう書こうと思っていて去年くらいにアカウントだけ作ったはいいけど、結局、初投稿がYAPC::Asia 2014のネタとなりました。

 一応会社ではインフラ担当という位置づけにいる自分ですが、仕事ではEnterprise臭のするプロダクトばっかり使うので、せめてプライベートではちょっと試してみたい技術は積極的に触ってここに書いてくようにしようとおもいます。

 YAPCに行くのは今年が初めてで、そもそも技術系の勉強会に出るように動き出したのも去年の終わりくらいから。きっかけのようなものは特になくて、上に書いたように、せめてプライベートでは、という思いが高まったからだったと思う。。。

 YAPC、なんというか、すごい、会場の雰囲気が。初参加のじぶんには、こんなに熱意があってちょっと普通じゃない雰囲気とか、明日からなにか変わるような感じとか、冗談じゃなくそういうのを感じました。むしろ自宅から程近い日吉なんて場所で、こんなにも胸熱なイベントが開催されていることが信じられない感すらありました。

 いろいろ貴重な話を聞けたのですが(特にRebuild.fmのリスナーとしては@さんや@さんがラフに話してた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に参加しなければ得られなかった知見です。発表者の方には本当に感謝です。すばらしい時間を過ごせました。

 せっかくだし、これを機にいろんな勉強会に足を運んでみたいと強く感じました。