仕事で使うMacBook Proの設定(ユーティリティ篇)

前回はセキュリティの設定についてまとめましたが、今回はユーティリティ篇です。
元々はWindowsで作業することが多かったこともあり、使い勝手の改善のために、幾つかのユーティリティ(開発ツールを含む)は必須でした。

日本語入力

http://www.google.co.jp/ime/index-mac.html
ことえり」は合わないですね。プライベートではATOKなのですが、今回はGoogle日本語入力を使用することにしました。キー入力への反応も良いし、キーバインドの変更で今のところは問題なしです。

Java

http://support.apple.com/kb/DL1572?viewlocale=ja_JP&locale=ja_JP
なんだかんだで必要ですね。
利用しようとすると、Oracleのサイトに誘導されてJDK7をインストールさせられそうになりますが、JDK6を入れたいので、その場合は上記のAppleのサイトからダウンロードしてインストールします。

Xcode

AppStroreからXcodeをインストール。Homebrewの導入など、要所要所で必要です。Windowsと比較してNativeの開発環境が簡単に用意できるのはいいですね。

Homebrew

これも無いと色々とコマンドの追加が面倒です。
今回の場合は、brewインストール時に command line developer tools を求められましたが、それ以外は特に問題なしです。

Emacs

エディタはこれです。以下のサイトを参考にしてインストールしました。
Mac OS X にインラインパッチの当たった Emacs を Homebrew でインストール

Firefox

シェアは落ちているようですが、なんだかんだで入れとく価値はあるかと。これまでの作業環境からの移行は、以下のとおり。

FreeMind

http://sourceforge.jp/projects/freemind/
MindMap作成ツール。Windowsなどでも動作するのでデータ交換も可能なのが良い所です。

astah * Community版

http://astah.change-vision.com/ja/product/astah-community.html
UML等のモデリングツール。シーケンス図を書いたりするのに、最もしっくり来ます。

構成管理リポジトリツール

gitとmercurialが利用できないと話になりません。コマンドラインを使うことが多いのですが、各branchの状況など全体を俯瞰的に見たい場合はGUIツールが便利です。この機会に SourceTreeを使用することにしました。

iTerm2

http://www.iterm2.com/#/section/home
標準terminalの代わりに利用します。標準でもだいたい大丈夫だけど、bookmark機能とか、画面分割とかもう一つ足りなかったことが、こっちだとできる感じ。標準terminalの方が使いやすい部分もあるんですけどね。

Caffeine

https://itunes.apple.com/jp/app/caffeine/id411246225
定番ですね。プレゼンなどの時に想定外の画面停止とか、バッテリー駆動中のちょっとした作業中のスリープとかを、防止するために便利です。

AppCleaner

http://www.freemacsoft.net/appcleaner/
アプリ削除用ユーティリティです。色々と試したいけど、Libraryに余計なファイルを残さないようにしたいので、これは入れておきます。まあ、設定などが残っていても問題になることは無いのでしょうが、やはり気持ち悪いので。

The Unarchiver

https://itunes.apple.com/jp/app/the-unarchiver/id425424353?mt=12
最近はZIPばかりだけど、これを入れておけば基本的に安心です。

CleanArchvier

http://www.sopht.jp/cleanarchiver/
3.0a6から、ファイル名のエンコーディング指定とzipパスワード付きの設定ができるようになってます。Mac固有ファイルを含めないようにするなどの設定もできるので、Windowsユーザとのやりとりに重宝します。

Display Menu

https://itunes.apple.com/jp/app/display-menu/id549083868
Display解像度を変更するためのユーティリティ。DotByDotを試してみたかったので入れてみたのですが、さすがに細かすぎたので調整。ちょっと小さめだけど、2048*1280 を利用しています。ただ、再起動時にはシステム上の上限である1920*1200に戻ってしまうのだけが残念です。

CheatSheet

http://www.grandtotal.biz/CheatSheet/
⌘キーを長押しすると、使えるショートカットキーを表示してくれます。
まだ、ショートカットを使い慣れないので非常に便利。慣れてきたら外しても良いかもしれないけど、新規ソフトを扱う際など何かと重宝しそう。

ClipMenu

http://www.clipmenu.com/ja/
クリップボードの履歴を利用可能にします。Shift+⌘+Vで呼び出せるし、スニペットの登録もできるので、ちょっとした定型文の利用にも便利。

XtraFinder

http://www.trankynam.com/xtrafinder/
OSXは良く出来ているけどFinderは気に入らないです。標準のままだと Windows Explorer の方が使いやすいと思います。なので、このソフトで拡張します。
「環境設定...」では最低限以下の設定をしておきます。

  • タブ:(以下を有効にする)
    • タブの項目は基本的に全部有効にする。
    • ショートカットでデュアルパネルのON/OFFを⌘Uでできるようにしておくと便利。
  • 機能:(以下を有効にする)
    • カット&ペースト
    • フォルダを先頭に配置
    • 名前欄の幅を自動調整
  • Finderメニューに項目を追加:(以下を有効にする)
    • パスをコピー
    • コンテンツ
    • 新規タブで開く
    • 不可視項目を表示(⌘Q)
    • 新規ファイル
    • 新規ターミナルで表示

PCKeyboardHack

https://pqrs.org/macosx/keyremap4macbook/pckeyboardhack.html.ja
キーを別のキー(コード)に変更するためのソフト。後述の KeyRemap4MacBook から分離したソフトらしいです。
私の場合はASCII配列のキーボードを使っていて、「CapsLockキーをControlキーへ変更」、「更に左下のcontrolキーはmouseの左クリックに割り当て」という事をやりたかったんです。(Controlキー配置は常識でしょうが、キーボードで左クリックができるようにするのも必須要件でした。ポインタを使った細かな作業では、クリックでマウスカーソルがズレたりするので。。。)
このソフトと KeyRemap4MacBook の合わせ技で上記の設定が実現できるので、導入しました。(他にもっと簡単なやり方があれば知りたいんですけど。。。)

  • CapsLockキーをControlキーへ変更:
    1. システム環境設定(キーボード)の「修飾キー...」からCapsLockキーを「アクションなし」に設定。
    2. PCKeyboardHack の Change Caps Lock を使用して Left Control (keycode=59) に設定。
  • 左下のcontrolキーをmouseの左クリックに割り当て:
    1. 左Controlキー(Control_L) はシステム環境設定の変更は必要なし
    2. PCKeyboardHack の Change Control_L を使用して PC Application Key (keycode=110) に割り当てる
    3. ここで割り当てた PC Application Key を更に mouseの左クリックに割り当てるが、これは後述の KeyRemap4MacBook 設定でおこなう

KeyRemap4MacBook

https://pqrs.org/macosx/keyremap4macbook/index.html.ja
OS X用の定番キーボードカスタマイズツールです。これでキーバインドしないと、やってられません。

Change Keyパネルで、以下の設定をおこないます。

  • For PC Users
    • Use PC Style Copy/Paste -> Control+C,V,X => Command_L+C,V,X
    • Use PC Style Undo -> Control+Z => Command_L+Z
    • Use PC Style Select All -> Control+A => Command_L+A
    • Use PC Style Save -> Control+S => Command_L+S
    • Use PC Style New Tab -> Control+T => Command_L+T
    • Use PC Style Find -> Control+F => Command_L+F
    • Change PC Application Key -> Application Key to LeftClick (*これで、前述のPCKeyboardHackの設定でApplication Keyに割り当てておいた左下Controlキーが、マウス左ボタンの扱いになります)
  • For Applications

Key Repeat パネルで以下の設定をおこなうことで、キーリピートの反応速度を上げます。

  • Basic Configurations
    • Key Repeat
      • Delay Until Repeat 100ms
      • Key Repeat 33ms

BetterTouchTool

http://www.boastr.de/
トラックパッドを有効利用するために利用、なんだけど使いこなしてはいません。Thinkpadトラックポイント至上主義(mouseは10年以上使っていません)だった私がトラックパッドOKになったのは、タッチパッドの精度向上が大きかったわけですが、この手のユーティリティを使いこなすことで色々と操作性向上への可能性が膨らみそうなのも気に入ったところです。
今のところ、Preferencesからは以下の設定しか使用していませんが、ショートカットについてはおいおい入れていきたいと思っています。

  • Action Settiongs
    • Window Snapping: Window Snapping Enabled で、画面端にウィンドウをsnapした際のresizeを有効にする。
    • Window Moving & Resizing: Move Windows を fnキーに割り当てることで、fnキーを押しながらカーソルを動かすだけで、Windowの移動が可能になる。
  • Gestures
    • Keyboardを選択し、対象のApplication に Finder を選択する。そして、+Add New Shortcut で ^D を入力し、Trigger Other Keyboard Shotrtcut に ⌘+deleteキー を指定する。これでControl+D でファイル削除ができるようになる。

※ BetterTouchToolのGesturesでは、面白いことにKeyboardもジェスチャ割り当ての対象となっているんですね。

おわりに

ここまでできれば基本的な作業については問題なし。あとはWindowsに特化した環境や、Office系のドキュメントのために、Windowsと関連アプリを仮想環境上に構築すればWindows中心の作業場でも溶け込めます。そっちはまあ良くある設定ですが、そのうちまとめるかもしれません。

コミック乱ツインズ 戦国武将列伝 2014/02号

今回で創刊5周年記念号らしい。
以前から通勤中に読み捨てている雑誌(創刊号から読んでた)なんだけど、今号は中々良かった。年末・年始で一番良かった漫画雑誌かもしれなくて、この感動を残しておきたい。

魔剣豪画劇 桃太郎!!

シグルイ」の山口貴由が描く袋とじカラー4Pの絵物語で、今回は桃太郎。鬼の正体とかはよく言われる話で、まあそれは良いんだけど、相変わらず描写がえぐい。犬・猿・雉は人間で、吉備団子を欲しがるのは罌粟の実入りだからとか、こんな話を巻頭にもってくる(しかも閉じてない方に罌粟の描写あり)センスが素晴らしい。

戦国自衛隊

我々の世代だと角川映画版が印象的な、半村良原作の「戦国自衛隊」を、森秀樹がコミカライズ。連載1回目の今回は、時震によるタイムスリップから野武士との戦闘直前までを描いている。森秀樹はちょっと(?)前まで、この雑誌で「腕KAINA〜駿河城御前試合〜」を連載していて、これは「シグルイ」と同じ「駿河城御前試合」を原作にしている。この二人がいるのだから、「駿河城御前試合」をテーマに特別編を競作(平田弘史とみ新蔵も一緒に)とかあると面白そうだけど、どんなもんでしょうかね。
話がずれたけど、今回は「富士の裾野」での演習中「天正十年」の時代にタイムスリップしたという設定になっている。手元の原作(角川文庫版でイラストが永井豪ダイナミックプロ)では「北陸(境川)」での演習中「永禄三年」にタイムスリップしているので、だいぶ様相が異なる。永禄三年が桶狭間の戦いの年であるのに対して、天正十年は本能寺の変の年。時の神が修正しようとしているのは、どんなズレか?この年に一気に躍り出て、歴史のカギとなり、なおかつ後世に子孫が残っていない人物となると、豊臣秀吉という事になるんだけど、そんな単純な展開になるかなあ。

島津戦記

今回は「耳川の戦い」が舞台となっているのだけど、なんと言っても島津義弘が弟の島津家久と甥の島津豊久に向かって「何かあった時 おまえも豊久も この俺が必ず守ってやる!」というセリフが感慨深い。直接的には、今回の話の中で、孤立した家久を義弘が単独で救いに行くストーリーへの布石になっているのだけど、関ヶ原での「島津の退き口」を想えば実に皮肉な話で、二十年以上の時を越えてエピソードをつなげる名台詞だなあと。

セキガハラ

関ヶ原では無くてカタカナのセキガハラ。無茶苦茶な設定で史実とは無縁なので気楽に読める。今回は前田利長の娘「満姫」が登場。どうすんだろうこれ。

無常草紙

今回が第十九話。今回もラストに向けて盛り上げる不穏な雰囲気がとんでもない。この作品は実は大傑作ではないかと思うんだよね。これだけにお金を払っても良いくらい。いつも余韻(それも苦い味わい)の残る終わり方だけど、今回はハッピーエンド(と言えるかは人を選びそうだけど)で、しんみりとした終わり方。巻末にピッタリだった。

今後も続けば良いのだけど。。。

他にもあるけど、印象的なものだけ(他も悪くはないです)だとこんな感じ。あと、今号は素晴らしかったんだけど、隔月刊なのでそれぞれの話をどんどん読めないのが難点。次号は2月26日(木)で、まだ1ヶ月以上ある。それから、今号はやたら値段が高騰している模様。(なんでだろう?)一応、アマゾンへのリンクは貼っておくけど、(まだ)書店やあるいはリイド社から直接購入するなどで普通に購入できるはずなので、興味がわいたら読んでみてほしい。こういうマイナーな雑誌は休刊・廃刊が怖いので、少しでも多くの人が購入すると良いなと。

仕事で使うMacBook Proの設定(セキュリティ篇)

最近、仕事で使うNotePCをThinkPadからMacBook Proへ変更しました。公私ともにThinkPadを使って20年近くなりますが、MacBook Proはユニボディになってからはキーボードを含めたハード全体の品質は随分と良くなりました。更に、トラックパッドの性能向上(と私自身の慣れ)によりトラックポイントでなくても大丈夫になったことと、開発内容や普段の作業的にWindowsよりもOSXの方が使い勝手が良いことが決め手になりました。一般的には今更ですしプライベートでは併用していましたが、仕事用ではどうしても保守的になってしまいました。個人的にはNeXTstep由来の環境を再び仕事で使うというのは感慨深いです。

そのようなわけで、使えるようにセットアップをしているわけですが、何回かに分けて記録を残して置きたいと思います。今回はセキュリティ対策設定です。かつては、OSXは何もしなくてもセキュリティレベルが高いという話もありましたが、今は(ホントは以前から)適切な設定とサポートソフトが無いと、厳しいでしょう。これくらいはやっておくということで、挙げておきます。足りなければ後で足していきます。

ファームウェアパスワード設定

ファームウェアパスワードについては、筐体自体内部にアクセスして弄られれば意味がないという話もあるようですが、今のMacBook Proは出荷後の構成を「簡単」には弄れないので、かけとく意味はあるかと思います。
やり方は簡単だけど再起動が必要です。

  • 電源オンのあとcommand+rを押し続ける
  • メニューバーから[ユーティリティ]→[ファームウェアパスワードユーティリティ]を選択
  • パスワードを設定する

パスワード入力指定

起動後のログインや、スリープ解除時にはパスワード入力を必須とします。

  • システム環境設定->セキュリティとプライバシー
  • 一般 パネルを開く
  • スリープ解除/スクリーンセーバー解除にパスワードを要求、で「すぐに」を指定。
  • システム環境設定->ユーザとグループ
  • ログインオプション
  • 自動ログインを「切」にする

なお、パスワードは長めに英数記号で12文字以上とかにしています。

ディスク暗号化

これは基本でしょう。安定して評価も良いようなので、FileVaultで良いかと。

  • システム環境設定->セキュリティとプライバシー
  • FileVault パネルを開く
  • FileVaultを入にする (手元の環境では、再起動後にだいたい30分くらいかかりました)

覗き見の防止

ちょっと席を外した時などにデスクトップ画面を晒したままにならないようにスクリーンセーバを設定します。

  • システム環境設定->デスクトップとスクリーンセーバ
  • スクリーンセーバ パネルを開く
  • 開始までの時間:10分 に設定

また、意識的に隠せるように、トラックパッドのホットコーナー(例えば左下)やショートカットで「スクリーンセーバ開始」を割り当てておくと、なお良しですね。

ソフトウェアアップデートの確認

なんだかんだでセキュリティアップデートはあります。基本的には常に最新となるようにします。

  • システム環境設定->AppStore
  • アップデートを自動的に確認、他も基本的には設定しておく

ファイウォール設定

NotePCであっても、それほど持ち歩くことは少ないのですが、外部で利用する際はファイアウォール設定は必須でしょう。

紛失・盗難時の探索

これは定番の「Macを探す」ですね。

  • システム環境設定->iCloud
  • Macを探す」を有効にする。

ちなみに、iCloudで使っているのはこれだけ。有効に使っていない、という話もあるけど私の使い方だと問題なし。

ウィルス対策

Macでもウィルス対策は必要ですよね。一通りの機能があって評価も高いということで、ClamXavを利用します。AppStoreのものは古い&Sentryが無いので、公式サイトからダウンロードしてインストールします。初回スキャンはそれなりに時間がかかる(3時間とか)ので、余裕を持って作業した方が良いです。設定は基本的にほとんどを有効にします。定期的なアップデート&スキャン、ログイン時の起動を有効にしておくのが最低限必要でしょう。

次回

セキュリティ関係の設定はこんなところです。次はユーティリティソフト篇です。

入間航空祭

航空自衛隊入間基地の入間航空祭に行ってきました。
航空祭に行ったのは10年以上ぶり、入間基地は初めてだったので色々と新鮮でした。ブルーインパルスを始めとした展示飛行はまったく飽きがこないもので、やはり飛行機は良いなあと。

なぜか人だかりが一番多かったF2。塗装の派手さとか1機しか無かったから?

展示飛行のトリを飾ったF15。いかにも戦闘機といった感じ。

T4の離陸。こういうシーンを間近で見られるのが航空祭の良いところ。

T4はあちこちで展示が行われていて、これは翼や脚の可動実演をおこなっているところ。エンジンがかけられないので、外部電源から電気を供給して動作させている。機体から電線が何本か出ているのがそう。

今回は5機が来ていたブルーインパルス。空域にドクターヘリが飛行していたため途中で演技が中止になったのが残念。


何度も実際に飛んでいるのを見たことがあるはずなのに、今回はじめて良さを実感したのがC1。意外なほど身軽な動きや、驚異の短距離着陸などはちょっとした驚きでした。後継機のC2は大型化していて、これほどの機動はできないでしょうから、見られるうちにもっと見ておきたいものです。

P.S.
航空祭自体は良かったのですが、一部にマナーの悪い観客がいたのと、人混みが凄く(32万人とか)て帰りは所沢までで1.5時間近くかかったのだけが大変でした。

NHK連続人形劇プリンプリン物語・人形特別展

別に説明するまでもなく有名な「NHK人形劇プリンプリン物語」の人形作者である友永詔三さんの個人美術館である「深沢小さな美術館」に行ってきました。

ちょうど「NHK連続人形劇プリンプリン物語・人形特別展」が開催されていたからでもあるのですが、そもそも2年前から毎年秋には訪ねて行っているので、いつものことと言えばいつものことです。(建物の写真は去年のモノから)

武蔵五日市の駅から歩いて40分位でしょうか。ゆっくりと作品を見て、先生の奥さんが入れてくれるカフェでくつろいで帰るというのは、秋の贅沢の一つなわけです。天気が悪いと大変ですが、基本的には歩いていくのがお勧めです。

今回は、今年の5月に発売された「NHK連続人形劇 プリンプリン物語 メモリアル・ガイドブック」も購入して、サインもいただきました。


数々の設定画(?)や大量の人形を拝見できたことと併せて、実に楽しい時間でした。

今回の展覧会自体は終了のようですが、(例年通りであれば)主要な人形たちは常設されていますし、その他の作品も素晴らしいと思います。例えば、モニュメント『森の調べ』の原型は昨年から展示されていますが、今年も健在です。(完成形は東海大学菅生高等学校中等部に設置されているようです)

ちなみに、昨年購入したのが「聖少女幻想―友永詔三作品集」ですが、去年の展示で最もお気に入りの赤と白の卑弥呼も入っています。これは、また見たい作品の一つです。

冬の間(11月〜3月)は休館してしまうので、今年のうちにぜひ一度訪れてみればどうでしょうか。

# なお、各作品集についてはAmazonにリンク貼ってますが、「深沢小さな美術館」には在庫がありましたから、そちらで購入するのがお勧めです。そして、その際は小銭もお忘れなく。

Linux上のJavaプログラムから共有メモリアクセス

表題どおりの話。JNIで...とか面倒なので簡単な方法を。

環境

  • CentOS 6.4 (kernel-2.6.32-358.2.1) on X86_64
  • java version "1.6.0_24" OpenJDK Runtime Environment

共有メモリ作成

まずは、共有メモリを作成、これはCでPOSIX版インタフェースを用いて行う。SYSVは使用しない。

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <stdio.h>

#define FILE_NAME  "/posix_mem"
#define FILE_SIZE  (10*(4*1024))

void
create_shm(char *fname)
{
        int fd;
        struct stat stat;
        unsigned char *ptr;

        fd = shm_open(fname, O_RDWR|O_CREAT,
                      S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
        ftruncate(fd, FILE_SIZE);
        fstat(fd, &stat);

        ptr = mmap(NULL,
                   stat.st_size, PROT_READ|PROT_WRITE,
                   MAP_SHARED, fd, 0);

        if (NULL == ptr) {
                printf("NG => %s: %d: %x\n", fname, stat.st_size, ptr);
        } else {
                printf("OK => %s: %d: %x\n", fname, stat.st_size, ptr);
        }

        close(fd);
}

int
main(int ac, char* av[])
{
        char *fname = FILE_NAME;
        if (2 <= ac) {
                fname = av[1];
        }
        create_shm(fname);

        return 0;
}

実行する。ちゃんと/dev/shm/配下(tmpfs)にファイルができている。

$ gcc -lrt -o shm_create shm_create.c
$ ./shm_create
OK => /posix_mem: 40960: 5198c000
$ ls -l /dev/shm/
合計 0
-rw-rw-r--. 1 kahnn kahnn 40960  8月 31 15:55 2013 posix_mem

後始末の用意もしておく。

#include <sys/mman.h>

#include <stdio.h>
#include <errno.h>

#define FILE_NAME  "/posix_mem"

void
delete_shm(char *fname)
{
        int ret;
        ret = shm_unlink(fname);

        if (ret < 0) {
                printf("NG => %s: %d\n", fname, errno);
        } else {
                printf("OK => %s\n", fname);
        }
}

int
main(int ac, char* av[])
{
        char *fname = FILE_NAME;
        if (2 <= ac) {
                fname = av[1];
        }
        delete_shm(fname);

        return 0;
}

共有メモリ read/write 確認 (C)

まあ、今更だけど一応Cで作っておく。数字入力をoffset,valueとしてintデータをread/write する単純な処理。

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <stdio.h>
//#include <stdlib.h>

#define FILE_NAME  "/posix_mem"

void
read_shm(char *fname)
{
        int fd;
        struct stat stat;
        unsigned char *ptr;

        fd = shm_open(fname, O_RDWR,
                      S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
        fstat(fd, &stat);
        ptr = mmap(NULL,
                   stat.st_size, PROT_READ,
                   MAP_SHARED, fd, 0);

        if (MAP_FAILED == ptr) {
                printf("R NG => %s: %d: %x\n", fname, stat.st_size, ptr);
        } else {
                printf("R OK => %s: %d: %x\n", fname, stat.st_size, ptr);
        }

        for (;;) {
                int offset, val;
                char buf[32];

                fgets(buf, sizeof(buf), stdin);
                sscanf(buf, "%d", &offset);
                val = *((int*)ptr + offset);
                printf("  R => ptr(%d) = %d, %x\n", offset, val, val);
        }

        close(fd);
}

void
write_shm(char *fname)
{
        int fd;
        struct stat stat;
        unsigned char *ptr;

        fd = shm_open(fname, O_RDWR,
                      S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
        fstat(fd, &stat);
        ptr = mmap(NULL,
                   stat.st_size, PROT_READ|PROT_WRITE,
                   MAP_SHARED, fd, 0);

        if (MAP_FAILED == ptr) {
                printf("W NG => %s: %d: %x\n", fname, stat.st_size, ptr);
        } else {
                printf("W OK => %s: %d: %x\n", fname, stat.st_size, ptr);
        }

        for (;;) {
                int offset, val;
                char buf[32];

                fgets(buf, sizeof(buf), stdin);
                sscanf(buf, "%d %d", &offset, &val);
                *((int*)ptr + offset) = val;
                printf("  W => ptr(%d) = %d, %x\n", offset, val, val);
        }

        close(fd);
}

int
main(int ac, char* av[])
{
        char *mode = "r";
        char *fname = FILE_NAME;

        if (2 <= ac) {
                mode = av[1];
        }
        if (3 <= ac) {
                fname = av[2];
        }

        if ('r' == *mode) {
                read_shm(fname);
        } else {
                write_shm(fname);
        }

        return 0;
}

こんな感じで任意のoffsetの値を読み書きできる。

## WRITE側
$ ./shm_rw w
W OK => /posix_mem: 40960: eed8d000
0 123
  W => ptr(0) = 123, 7b
1 12345678
  W => ptr(1) = 12345678, bc614e
0 987654321
  W => ptr(0) = 987654321, 3ade68b1

## READ側
$ ./shm_rw
R OK => /posix_mem: 40960: 7205e000
0
  R => ptr(0) = 123, 7b
1
  R => ptr(1) = 12345678, bc614e
0
  R => ptr(0) = 987654321, 3ade68b1

共有メモリ read/write 確認 (Java)

Javaの場合は MappedByteBuffer を使用する。上記のC版と同等の read/write プログラムは以下のようになる。明示的にload()やforce()を行ってはいない。
また、X86上での動作となるので、ByteOrder.LITTLE_ENDIAN を指定している。

import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
///////
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class ShmRW {
    static private String FILE_NAME = "/dev/shm/posix_mem";

    static private void write(String fname)
        throws Exception
    {
        RandomAccessFile file = new RandomAccessFile(fname, "rw");
        FileChannel channel = file.getChannel();
        int length = (int)channel.size();
        MappedByteBuffer buffer
            = channel.map(FileChannel.MapMode.READ_WRITE, 0, length);
        buffer.order(ByteOrder.LITTLE_ENDIAN);

        System.out.println("W Open => " + fname
            + " : isDirect? => " + buffer.isDirect()
            + " : size => " + length
            + " : isLoaded => " + buffer.isLoaded()
            + " : order => " + buffer.order());

        BufferedReader input =
                new BufferedReader(new InputStreamReader(System.in));
        String line;
        while ((line = input.readLine()) != null) {
            if (line.equals(""))
                continue;
            StringTokenizer tok = new StringTokenizer(line," ");
            int offset = Integer.valueOf(tok.nextToken()).intValue();
            int value = Integer.valueOf(tok.nextToken()).intValue();

            buffer.putInt((offset * 4/* size of int */), value);
            System.out.println("  W => buffer(" + offset + ") = "
                + value + ", " + Integer.toHexString(value)
                + " (" + buffer.position() + ")");
        }
        input.close();

        channel.close();
        file.close();
    }

    static private void read(String fname)
        throws Exception
    {
        RandomAccessFile file = new RandomAccessFile(fname, "rw");
        FileChannel channel = file.getChannel();
        int length = (int)channel.size();
        MappedByteBuffer buffer
            = channel.map(FileChannel.MapMode.READ_WRITE, 0, length);
        buffer.order(ByteOrder.LITTLE_ENDIAN);

        System.out.println("R Open => " + fname
            + " : isDirect? => " + buffer.isDirect()
            + " : size => " + length
            + " : isLoaded => " + buffer.isLoaded()
            + " : order => " + buffer.order());

        BufferedReader input =
                new BufferedReader(new InputStreamReader(System.in));
        String line;
        while ((line = input.readLine()) != null) {
            if (line.equals(""))
                continue;
            int offset = Integer.valueOf(line).intValue();

            int value = buffer.getInt(offset * 4/* size of int */);
            System.out.println("  R => buffer(" + offset + ") = "
                + value + ", " + Integer.toHexString(value)
                + " (" + buffer.position() + ")");
        }
        input.close();

        channel.close();
        file.close();
    }

    static public void main(String[] args)
        throws Exception
    {
        String mode = "r";
        String fname = FILE_NAME;
        if (1 <= args.length) {
            mode = args[0];
        }
        if (2 <= args.length) {
            fname = args[1];
        }

        if (mode.equals("r")) {
            read(fname);
        } else {
            write(fname);
        }
    }
}

使い方はC版と同じで、Java同士でも共有メモリを介してやり取り可能だし、CとJavaの間でも問題ない。共有メモリにマッピングしたデータを、Javaを含めた言語間でやり取り可能ということになる。

まとめ

POSIX版共有メモリのインタフェースは面倒で使ったことが無かったが、SYSVと違ってファイルの存在を意識した仕様で、linuxの実装もまさにそのまま、という感じなので MappedByteBuffer が上手く使える。OpenJDKのソースを簡単に見た限り*1では、普通にmmap(MAP_SHARED)してアドレスでアクセスするようなので、コードの見た目ほどはオーバーヘッドは無いはず(と思いたい)。

LinuxPOSIX版共有メモリの実装に依存した方法ではあるが、簡単に利用可能なのはありがたい。使いどころは限られるけど、知っていれば何か役に立つこともあるかもしれない。(無いかもしれない)

*1:間違っているかもしれないが jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java, jdk/src/share/classes/java/nio/ByteBufferAs-X-Buffer.java, jdk/src/share/classes/java/nio/Direct-X-Buffer.java, jdk/src/solaris/native/sun/nio/ch/FileChannelImpl.c, jdk/src/solaris/native/java/nio/MappedByteBuffer.c, hotspot/src/share/vm/prims/unsafe.cpp 辺りとか

Apache Solr 4.4 日本語用設定

Solr に Amazon商品データを登録して色々と試したのだけど、検索キーに対して結果が想定と微妙に異なることがあった。fieldType の設定はデフォルトのまま流用したのだけど、やはりちゃんと理解したうえで調整も必要なのだろう、ということで設定について調査している。(Web上には色々と情報も出ているがまとめとかないと、また後でやるときが面倒)徐々に書き足す予定。

※ 2013/09/04 共通設定を分離

共通設定

N-GRAM形態素解析の両方で使える設定をまとめる。

種別 class 説明 オプション 参考
charFilter solr.MappingCharFilterFactory マッピングファイルに書かれたルールに従って文字を変換する。 mapping=mapping-ja.txt Mapping CharFilterFactoryの使い方
charFilter solr.PatternReplaceCharFilterFactory 正規表現で指定されたパターンに沿って文字を変換する。 pattern="([^a-z])" replacement="" Pattern ReplaceCharFilterFactoryの使い方
charFilter solr.HTMLStripCharFilterFactory テキストからHTMLタグを取り除く。 solr.HTMLStripCharFilterFactory
filter solr.LowerCaseFilterFactory 英大文字を小文字に変換する。 solr.LowerCaseFilterFactory
filter solr.CJKWidthFilterFactory 全角ASCII文字を半角に、半角カタカナを全角に変換する。solr.MappingCharFilterFactoryでこの手の変換を記述する必要は無いと言うことになる。 CJKWidthFilterFactory
filter solr.SynonymFilterFactory 類義語の展開と設定をおこなう。類義語定義の書式は参考のページに詳細が記述してある。 synonyms="syn.txt" ignoreCase="true" expand="false" solr.SynonymFilterFactory, solrの同義語の設定, kuromojiの辞書のメンテナンス

N-GRAM

N-GRAMを使用する場合は、以下の設定の組み合わせに共通設定を組み合わせてトライすれば良い。

種別 class 説明 オプション 参考
tokenizer solr.StandardTokenizerFactory 標準的なTokenizerで、Unicode standard annex UAX#29に従ってトークン分割する。これ自体はN-GRAMとは関係なく、solr.CJKBigramFilterFactoryの方がN-GRAM生成の本体。 maxTokenLength=255 solr.StandardTokenizerFactory
tokenizer solr.ICUTokenizerFactory solr.CJKBigramFilterFactoryへトークンを渡す際に挙げられていたtokenizerの一つ。ちゃんと見てないが、StandardTokenizerFactory で良いだろう。 solr.ICUTokenizerFactory
filter solr.WordDelimiterFilterFactory 英数字やデリミタ(ハイフン等)で構成される文字の表記揺れを吸収する。 参考ページ参照 solr.WordDelimiterFilterFactory
filter solr.CJKBigramFilterFactory bigramへの分割を行う。StandardTokenizer または ICUTokenizer と組み合わせる。文字種(ひらがな、カタカナ)毎に分割をおこなうかどうか、unigramの出力をおこなうかどうか、を属性で指定できる。 han="true" hiragana="true" katakana="true" hangul="true" outputUnigrams="false" CJKBigramFilterFactory

実際の設定では、以下の組み合わせをベースに変更していく。(2013-08-27現在、defaultのまま)

<fieldType name="text_cjk" class="solr.TextField" positionIncrementGap="100">
  <analyzer>
    <tokenizer class="solr.StandardTokenizerFactory"/>
    <filter class="solr.CJKWidthFilterFactory"/>
    <filter class="solr.LowerCaseFilterFactory"/>
    <filter class="solr.CJKBigramFilterFactory"/>
  </analyzer>
</fieldType>

形態素解析

形態素解析を使用する場合は、以下の設定の組み合わせに共通設定を組み合わせてトライすれば良い。

種別 class 説明 オプション 参考
tokenizer solr.JapaneseTokenizerFactory 形態素解析器、KuromojiをSolrから使えるようにしたもの。これが基本。 mode="search" userDictionary="lang/userdict_ja.txt" Java製形態素解析器「Kuromoji」を試してみる, solrの日本語形態素解析の設定, kuromojiの辞書のメンテナンス
filter solr.JapaneseBaseFormFilterFactory 動詞の活用形等を基本形に変換する(正規化) Class JapaneseBaseFormFilterFactory
filter solr.JapanesePartOfSpeechStopFilterFactory tagsで指定されたファイルに記述された品詞を除外する tags="lang/stoptags_ja.txt" Class JapanesePartOfSpeechStopFilterFactory
filter solr.StopFilterFactory wordsで指定されたファイルに記述された単語を除外する ignoreCase="true" words="lang/stopwords_ja.txt" solr.StopFilterFactory
filter solr.JapaneseKatakanaStemFilterFactory カタカナ文字列最後の長音(ー)を除去する。minimumLengthで指定した文字数以上のカタカナのみが対象。 minimumLength="4" Class JapaneseKatakanaStemFilterFactory

実際の設定では、以下の組み合わせをベースに変更していく。(2013-08-27現在、defaultのまま)

<fieldType name="text_ja" class="solr.TextField"
           positionIncrementGap="100" autoGeneratePhraseQueries="false">
  <analyzer>
    <tokenizer class="solr.JapaneseTokenizerFactory" mode="search"/>
    <filter class="solr.JapaneseBaseFormFilterFactory"/>
    <filter class="solr.JapanesePartOfSpeechStopFilterFactory"
            tags="lang/stoptags_ja.txt" />
    <filter class="solr.CJKWidthFilterFactory"/>
    <filter class="solr.StopFilterFactory"
            ignoreCase="true" words="lang/stopwords_ja.txt" />
    <filter class="solr.JapaneseKatakanaStemFilterFactory" minimumLength="4"/>
    <filter class="solr.LowerCaseFilterFactory"/>
  </analyzer>
</fieldType>