Home

CEREVO TechBlog

GIOChannelの使い方

こんにちは、稲垣@CEREVOです。今回はGTK+に関する (正確にはGLibに関することなのですが……) 話題で、GIOChannelの使い方を見てみたいと思います。

なぜGIOChannelを使うのか

GTK+でGUIアプリケーションを書くと、プログラムは基本的にイベントドリブンになります。つまり、メインループの中でイベントが起きるのを黙って待って、何か起きたらコールバックの中で処理します。こうしたフレームワークではファイルやソケットの読み書きでブロックされる(待ちが発生する) のは嬉しくありません。ブロックされている間は基本的に他の処理ができず、たとえば処理中のアニメーションが止まったりします。GIOchannelを使えば、ブロックされない状態になってから処理を開始することができます。

なお、今回はあまり関係ありませんが、テキストのエンコーディングを適当にUTF-8に変換してくれる機能もあります。

GIOChannelの使い方

  • g_io_channel_unix_newで生成 (unix系のシステムを想定しています……)
  • NONBLOCKに設定する (設定しないとG_IO_STATUS_AGAINが返るかわりにブロックされます)
  • g_io_add_watchでイベントソースをデフォルトメインループに追加
    (頻繁に掛け外しをする場合は、g_io_create_watchで作ったイベントソースを自分で扱った方がいいかも知れません)
  • コールバック
    • どんなイベントが起きたのか、GIOConditionを見て判断する
    • GIOChannelを読み書きしてG_IO_STATUS_NORMALやG_IO_STATUS_AGAINが返ってきたらTRUEを返す
      G_IO_STATUS_EOFやG_IO_STATUS_ERRORが返ってきたらFALSEを返してイベントソースを外す

読み込みサンプル

標準入力から読み込んでg_messageでメッセージを表示するサンプルです:


#include <glib.h>
static gboolean read_callback(GIOChannel *io, GIOCondition cond, gpointer user_data) {
  GMainLoop *loop = user_data;
  gboolean continue_to_watch = FALSE;
  if (cond & G_IO_IN) {
    GError *e = NULL;
    char *text;
    switch (g_io_channel_read_line(io, &text, NULL, NULL, &e)) {
    case G_IO_STATUS_NORMAL:
      g_message("%s: read line: %s", __func__, text);
      g_free(text);
      continue_to_watch = TRUE;
      break;
    case G_IO_STATUS_AGAIN:
      g_message("%s: AGAIN", __func__);
      continue_to_watch = TRUE;
      break;
    case G_IO_STATUS_ERROR:
      g_message("%s: error: %s", __func__, e->message);
      g_error_free(e);
      break;
    case G_IO_STATUS_EOF:
      g_message("%s: EOF", __func__);
      break;
    default:
      break;
    }
  }
  if (! continue_to_watch) {
    g_main_loop_quit(loop);
    g_main_loop_unref(loop);
  }
  return continue_to_watch;
}

static void sample_loop(int fd, GIOCondition cond, GIOFunc callback) {
  GMainLoop *loop = g_main_loop_new(NULL, FALSE);
  GIOChannel *io = g_io_channel_unix_new(fd);
  guint tag = g_io_add_watch(io, cond, callback, g_main_loop_ref(loop));
  g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK, NULL);
  g_io_channel_set_close_on_unref(io, TRUE);
  g_io_channel_set_encoding(io, NULL, NULL);
  g_main_loop_run(loop);
  g_io_channel_unref(io);
  g_main_loop_unref(loop);
}

int main(int argc, char *argv[]) {
  sample_loop(0, G_IO_IN, read_callback);
  return 0;
}

G_IO_INは読み込み可能を示すフラグです (読み込み専用のfdについてはこのフラグしか立たないようです――man poll参照)。ただし、読み込み可能と言っても、読んでみたらすぐにG_IO_STATUS_EOFが返ってくることもあります。EOFが返ってきたらもうチャンネルに用はないのでFALSEを返してイベントソースを外します。

書き込みサンプル

標準出力 (のバッファ) が書き込み可能になるのを待って書き込みまくるサンプルです。なおsample_loop関数は前節の関数をそのままコピーして使ってください。実行すると大量のメッセージが出力されますから、

./sample | read i

などとして、パイプが適当に閉じられるようにして実行してください:


#include <glib.h>
static gboolean write_callback(GIOChannel *io, GIOCondition cond, gpointer user_data) {
  GMainLoop *loop = user_data;
  gboolean continue_to_watch = FALSE;
  if (cond & (G_IO_ERR | G_IO_HUP))
    g_message("%s: channel is closed", __func__);
  else if (cond & G_IO_OUT) {
    GError *e = NULL;
    char *text = "BABEL\n";
    int len;
    switch (g_io_channel_write_chars(io, text, -1, &len, &e)) {
    case G_IO_STATUS_NORMAL:
      g_message("%s: wrote %d chars", __func__, len);
      continue_to_watch = TRUE;
      break;
    case G_IO_STATUS_AGAIN:
      g_message("%s: wrote %d chars, AGAIN", __func__, len);
      continue_to_watch = TRUE;
      break;
    case G_IO_STATUS_ERROR:
      g_message("%s: error: %s", __func__, e->message);
      g_error_free(e);
      break;
    case G_IO_STATUS_EOF:
      g_message("%s: EOF", __func__);
      break;
    default:
      break;
    }
  }
  if (! continue_to_watch) {
    g_main_loop_quit(loop);
    g_main_loop_unref(loop);
  }
  return continue_to_watch;
}

int main(int argc, char *argv[]) {
  sample_loop(1, G_IO_OUT | G_IO_ERR | G_IO_HUP, write_callback);
  return 0;
}

チャンネルが閉じられるとコンディションにG_IO_ERRのビットが立ちます。同時にG_IO_OUTビットが立つこともありますが、それは単にバッファに空きがあるというだけで、書き込んでも誰も見てくれないので無視するようにしました。

またg_io_channel_write_charsでは、本来は書き込まれたバイト数もチェックしなければならないのですが、長いデータを書き込まなければ途中で切れることはないようなので、今回はチェックしていません。

読み書き用の場合

ソケットは読み書き両用なので、必要なら読み書き両用のコールバックを書くことができます。そのときは上記のread_callback関数とwrite_callback関数を適当に結合させればいいでしょう。なお、ソケットの相手側が閉じられていても (コンディションにG_IO_ERRビットが立っていても)、バッファに読み込み可能なデータが残っていることがあります。プロトコルによりますが、必要ならG_IO_INビットをチェックしてG_IO_STATUS_EOFが返ってくるまで読み出してやることもできます。

おわり

GIOChannelを使えば、GLibのメインイベントループの中で、ブロックされることなく入出力を処理することができます。イベントドリブンなアプリケーションを書くために是非とも使い方を把握しておきたいものです。

Happy hacking!

MSP430のPWM出力を増やす方法

こんにちは、稲垣@Cerevoです。今回はまたMSP430の話題です。

※一応、使っているのはMSP430F247であると断わっておきます。

MSP430には二つのタイマが入っていて、PWMを自動で (CPUが割り込みの中で操作しなくても) やってくれます。今回は普通にタイマでPWMをする方法を紹介し、さらに頑張って普通はPWMに使えないポートでも半自動でPWMをやってみたいと思います。

タイマの概要 (コンペアモード)

タイマにはタイマレジスタとキャプチャ・コンペアブロック0から2 (もしくは0から7) があり、大体以下のように動作します:

  • タイマレジスタ
    • 値が0になるときに割り込みを発生させる (オーバーフロー割り込み)
    • タイマAでは16ビット幅で固定、タイマBでは16/12/10/8ビット幅で可変
    • 三つのタイマモード
      • ビット幅全てを使ってカウントアップ (連続モード)
      • コンペアレジスタ0の値までカウントアップ (アップモード)
      • コンペアレジスタ0の値までカウントアップ、さらに0までカウントダウン (アップダウンモード)
  • キャプチャ・コンペアブロック
    • キャプチャ・コンペアレジスタ (コンペアレジスタと略します) と出力ビットがある
    • コンペアレジスタの値がタイマレジスタの値と等しくなるとき割り込み (コンペア割り込み)
    • 同時に出力ビットをセット・リセットする
  • キャプチャ・コンペアブロック (0以外)
    • タイマレジスタの値がコンペアレジスタ0の値と等しくなるときに (すなわちコンペア割り込み0のタイミングで)、出力ビットをセット・リセット・トグルする (出力モードによる)

キャプチャ・コンペアブロック (0以外) には、出力ビットの変化するタイミングが

  • 自分のコンペア割り込み
  • ブロック0のコンペア割り込み

の二つあることに気づかれましたか。変化のタイミングが二つあるので、各ブロックごとに異なったデューティー比のPWM出力ができるわけです。つまり、普通のPWMとしての使い方は次のようです:

  • アップモード
  • コンペアレジスタ0: 変調周波数を決める
  • コンペアレジスタ0以外: デューティー比を決め、適当な出力モードでPWM出力

キャプチャ・コンペアブロック0でもPWM

上記のような特性から、ブロック0ではPWM出力はできません (パルス幅が0固定のPWMと言えなくもないのですが)。かといって、連続モードに設定して、割り込みが発生するたびにコンペアレジスタを設定しながらGPIOを叩くのもやや無駄な話です。そういうのは各ブロックで別々の割り込み周期を使いたい場合にすることです。もうちょっとCPUがサボりながらPWM出力をする方法があります。ハードウェアに足りない機能だけ、ソフトウェアで実現するべきです。

PWMのキモはこの特徴です:

コンペア割り込みのタイミングで出力ビットをセット・リセット・反転

これでデューティー比が決まるわけです。この特徴は全てのキャプチャ・コンペアブロックに存在します。活用しましょう。ただし、キャプチャ・コンペアブロック0で活用するには、連続モードにしなければいけません。代償として変調周波数は自由に設定できなくなります。

一方、キャプチャ・コンペアブロック0には、この機能がありません:

ブロック0のコンペア割り込みタイミングで出力ビットをセット・リセットする

当然ですね。この部分をオーバーフロー割り込みのタイミングでソフトウェアで処理すれば、キャプチャ・コンペアブロック0でもPWMができます。つまりオーバーフロー割り込みで、出力ビットを0または1に設定すればいいのです。

なお、出力ビットは、通常の出力モードでは値を設定できません。手動設定のモードに切り替えてから設定し、また通常のモードに戻すことになります。なかなか普通じゃない感があります。

そして自由な変調周波数

先に、「代償として変調周波数は自由に設定できなくなります」と書きましたが、自由にする方法が無いわけではありません。というのは、タイマAのタイマレジスタはビット幅が16ビット固定で、連続モードに設定すると周期がかなり長くなってしまうのです。通常の1MHzのクロックだとおよそ16Hz、8MHzのクロックを使ってもおよそ128Hzです。省電力な低速クロックではさらに遅くなりますし、そもそも16HzなんてPWMでLEDを光らせるには遅すぎます。

解決方法は簡単で、周期を短くしたければ、オーバーフロー割り込みの中でタイマレジスタの値を適当に大きくすればいいのです。例えば、0xff00を代入すれば、カウンタは8ビット幅になったも同然。こうして自由な変調周波数を手に入れることができます。コンペアレジスタの値も対応する値にしておきましょう。

まとめ

まとめると次のようにタイマを設定することになります:

  • 初期設定
    • 連続モード
    • コンペアレジスタ0にデューティー比を設定 (必要なら、(0×10000 – 周期 + デューティー比) の値にする)
    • 出力モードはトグル
  • オーバーフロー割り込み
    • 出力モードを手動に設定し、出力ビットを0 (ないし1) に初期化
    • 出力モードをトグルに設定し直す
    • 必要ならタイマレジスタの値を (0×10000 – 周期) の値に設定し直す

ちなみに、タイマのクロックがCPUのクロックと同期していない場合についてデータシートには色々と注意書きがありますが、今回のような使い方では特に気にするべきことはないようです。

おしまい

実は事の発端はタイマBの出力0にLEDをつなげてしまったことだったりします。プリント基板を作る前にチップの仕様をよく確認しないと、ソフトにしわ寄せがくるようですね (寄せることができるともいう)。
ともあれ、Happy hacking!

すごくシンプルなハミング距離計算

ハミング距離とはなんぞや……という話はWikipediaでも見ていだたくとして、要するに「ビット列を比較して値の異なる位置を数えたい」ということです。例えば01010011と01010111のハミング距離は1です。

異なるビット

二つのビット列のうち、ビットの異なる位置を抽出することは簡単です。基本です:

d = a ^ b;

ビットを数える

あとはここから立っているビットを数えるわけですが、普通に考えると

  • ビット列の幅の分だけループさせる
  • プロセッサのビットサーチ命令を使う

という方法を使うと思います。前者は当然遅いので嫌ですね。後者はインラインアセンブラを使ってプロセッサ依存にしないといけませんし、ビットマスクを作ってビットを消しながら数えなくてはならず、いかにも面倒です (←根拠もなくシフト命令は遅いと思っているヤツ)。

そこで私が使う方法は次のようなものです:

d &= d - 1; // 立っている最下位ビットを消す

全ビットが消えるまでループすればハミング距離が分かります:

d = a ^ b;
i = 0
while (d) {
  d &= d - 1;
  i++;
}

おしまい

このコードを何に使うのかというと、BCH(15,5)符号などをパターンマッチングで誤り訂正しようということだったりします。15ビットもあるのに内容は5ビット(32パターン)しかないので、ハミング距離が一番小さい符号語が訂正結果ということにした方が簡単なのではないかというわけです。別にガロア体がよく分からんとかそういう理由ではないんですよ。
Happy hacking!

モバイル機器開発Tips ~パワーマネージメント その2~

こんにちは、Cerevoの鈴木です。
今回はパワーマネージメントを支えるハードウェアということで電源回路を扱ってみます。
実際にどんな電源回路を選定すべきかにフォーカスしたいと思います。
と言っても、そんなに電気回路の知識が必要ない程度で紹介しますので、ご安心をw

電源回路の基本の基本

よく使用される電源回路はDC/DCコンバータという回路です。
この回路を使うことで、例えばリチウムイオンバッテリの電圧である+3.7Vから内部回路の動作に必要な各種電圧を生成するわけです。
DC/DCコンバータの実際の動作としては電力変換を行うのですが、残念ながら100%変換されるわけじゃありません。
性能としては変換時の「効率」が重要になります。ちなみに変換できなかった電力は…熱に変換されますw
では、実際の回路を見てみます。

DM355EVMの電源回路

実際に自分たちが使用しているCPUであるDM355に関する例を紹介してみます。
まずはDM355の評価ボードであるDM355EVMを見てみます。
DM355EVMの回路図は http://c6000.spectrumdigital.com/evmdm355/reve/files/EVMDM355_Schematics_RevE.pdf からDLできます。
P.25-26がDM355の電源回路部分になります。使用しているチップは TPS54310 ですね。
このチップのデータシート http://focus.tij.co.jp/jp/lit/ds/symlink/tps54310.pdf をちょっと見てみます。
まずはP.1にある「効率 対 負荷電流」のグラフを見てみます。
内部回路、この場合はDM355に対して、出力電圧+3.3Vで1Aの電流が流れた場合(つまりDM355に3.3Wの電力を供給した場合)に約93%ぐらいの効率であることが読み取れます。なかなか優秀ですね。
でも、DM355は省電力モードのあるCPUです。
例えば、省電力モードになって+3.3Vで1mAの負荷電流となった(つまりDM355に3.3mWの電力だけを供給することでよくなった)場合に、効率はどうなるか…もう少しデータシートを読み漁ってみましょう。
P.10に特性グラフが出てきました。図12に注目してください。
何と、負荷電流が200mAぐらいのところを境にして、急激に効率が65%程度まで低下しているじゃないですか!
この回路では省電力モード時に約35%の電力が電源チップで熱となって消費されてしまいます。
というか、このEVMでは残念ながら省電力モードの評価や実験はできないですねw

Leopard Boardの電源回路

DM355が載っているボードとして、最近一部で話題のLeopard Boardがあります。
このボードはカメラ付きで$99と評価ボードとしては破格の価格のボードです。
早速、回路図を見てみましょう。回路図は http://www.leopardboard.org/uploads/DM355_LEOPARD_BOARD.pdf からDLできます。
P.13が電源回路になります。Leopard BoardではDM355EVMとは違って、電源統合チップ TPS65053 が使用されていますね。どうりで安いわけですね(ぉ
さてさて、どんな性能でしょうか…データシート http://focus.tij.co.jp/jp/lit/ds/symlink/tps65053.pdf を見てみましょう。
データシートのP.1には大体そのチップのFEATUREが書いてありますが、いきなり “Up To 95% Efficiency” と書いてありますね。期待できそうです。
問題は省電力モードになったとき、つまり低負荷時の効率です。
P.7のFigure1, Figure2に注目です。
どちらも効率と負荷電流の関係を表していますが、Figure1のほうが負荷電流が0.001A=1mAのときでも効率が90%程度を維持しています。
違いは何かというと、Figure1が”PWM/PFM Mode”なのに対して、Figure2が”PWM Mode”オンリーなことです。
このモードをどう切り替えるか、それもデータシートに書いてあります。
P.5に TPS65053 の各ピン毎の機能が “TERMINAL FUNCTIONS” にまとめられていますが、その中のMODEピンが該当します。

Select between Power Save Mode and forced PWM Mode for DCDC1 and DCDC2.
In Power Save Mode, PFM is used at light loads, PWM for higher loads.
If PIN is set to high level, forced PWM Mode is selected.
If Pin has low level, then the device operates in Power Save Mode.

つまり、通常時はMODEピンをHighにしておいて、DM355が省電力モードになるときにはLowにしてあげるとバッテリが効率良く使えますね。
電源回路はこうじゃないといけないです。

電源回路のポイントとまとめ

バッテリ動作するようなモバイル機器の電源回路は、とにかく高効率であることが重要です。
しかも、ON/OFFできるだけじゃなく、電源回路自体がパワーセーブモードを持つものじゃないと長生きできません。
今回はDM355の省電力モードと絡めて電源回路を紹介しましたが、Linuxが動作しているようなCPUが実際に省電力モードになるまでの動作に興味をもたれる方もいると思いますので、次回はその辺の具体例を紹介する予定です。

GTK+自作ウィジェットの描画処理を軽くする

グラフィックアクセラレータのない組み込み環境でGTK+を使う場合、描画処理はけっこう負荷の高い処理です。例えば画像を大量に描画すると、その負荷が高くてバックグラウンドで別の処理を進めることができないということも起こりえます。
そこでGtkDrawingやGtkLayoutに自作の描画ハンドラ (exposeイベントのシグナルハンドラ――以下、exposeハンドラ) を作るときに無駄な描画処理をしないためのテクニックを紹介したいと思います。

背景色を正しく設定する

ウィジェットのある部分についてexposeイベント (ひらたく言えば描画イベント) が発生すると、当該部分が背景色で塗り潰されてからexposeハンドラが呼ばれます。ここを別な色で塗り直すのは当然無駄なわけで、ウィジェットの生涯を通じて最も描画面積の大きい色を背景色として設定しておけば、描画処理は軽くなるでしょう。

クリッピングする

ウィジェットの重なりが変更されたり、GtkLabelなどの自分のGdkWindowを持たないウィジェットの内容が変更されたりすると、
下になっているウィジェットの重なっている部分が再描画されます。
exposeハンドラでは、GdkGCを設定して、ウィジェットの必要な部分だけを書き換えるようにしましょう。

GdkRegionを使う方法

この方法が推奨されているようです:

static gboolean
my_widget_expose_event_handler (GtkWidget *widget, GdkEventExpose *event) {
  GdkGC *gc = widget->style->fg_gc[GTK_WIDGET_STATE(widget)];
  gdk_gc_set_clip_region(gc, event->region);
  /* 描画処理 */
  gdk_gc_set_clip_region(gc, NULL);
  /* 他の処理 */
}

GdkRectangleを使う方法

static gboolean
my_widget_expose_event_handler (GtkWidget *widget, GdkEventExpose *event) {
  GdkGC *gc = widget->style->fg_gc[GTK_WIDGET_STATE(widget)];
   gdk_gc_set_clip_rectangle(gc, &event->area);
  /* 描画処理 */
  gdk_gc_set_clip_rectangle(gc, NULL);
  /* 他の処理 */
}

ウィジェット全体を再描画するのはやめよう

クリッピングとも関係のある話ですが、手抜きをしてgtk_widget_queue_redraw_areaを使うのは避けるべきです。gtk_widget_queue_redraw_area を多用している場合は設計を見直した方がいいかも知れません。
特に画像を重ねて描画している場合、効率よく再描画するために重なりを管理する処理を書くことになりがちです。それはGTK+が既に持っている機能なので自分で作るべきではありません。

バグのあるウィジェットは使わない

バグのあるウィジェットとはGtkFixedのことですが、子ウィジェットを動かすとGtkFixed全体が再描画されるというバグがあります。

具体的なことはgtk_fixed_moveとgtk_layout_moveの実装を比較すると分かりますし、直すことも難しくありません。しかしGtkFixedは設計も古く (よく言えば単純で)、あんまりメンテナンスされていないようなので使わない方がいいでしょう。

おしまい

ぶっちゃけGTK+は独学なのであまり偉そうなことは言えません。むしろ教えてください。
Happy hacking!

クラッシュ&移行 ~Cerevo社内開発用サーバ構築記~

まつけんです。

Beagle Boardの記事が好評だったので、そのままなにかBeagleネタで行こうかとおもったのですが、本日、開発マシンが突然死したので、哀しみの開発マシン移行記を今回は書いてみます。

事の発端

土曜日の午前中、結構集中してコーディングをしていました。マシン自体は、快調に動いており、問題なく作業を進めました。ふと、喉が渇いて、お茶を汲んで、トイレに行ってきました。かえってきたら、sshでログインしているターミナルがすべて反応しません。

あれっとおもって、マシンを見ると、CPUファンが回っていません。あれ、電源落ちた?と思い、近づきます。明らかに焦げ臭いにおいが漂ってきました。危険な感じがヒシヒシと伝わってきます。とりあえず、保存してない分のソースは藻屑と消えました、合掌。。。って、この時点でテンション-10です。

とりあえず、原因をさぐるために、マザーボードに顔を近づけます。どうも、臭いのもとはCPU周辺で、かつ、ヒートシンクは触れないくらい熱くなっています。CPUが焦げて再起不能になった感たっぷりです。

ちょっと余談ですが、自作PC系でオーバークロックとかを趣味にされている方を中心に、なんというかこの臭いを感じた瞬間のテンションの下がりかたは、経験のある人はわかるとおもうのですが、完全に脱力系です。

さて、そうなると、とりあえず、電源ボタン、というか、マザーに直刺ししているスイッチを押しても当然のように反応がありません。これはやばいな、ということで、とりあえず、電源ケーブルを抜きます。ここで、おもむろにヒートシンクを掴むとやけど確実に火傷します。とりあえず、はやる気持ちを抑え、心を落ち着けて、深呼吸しながら冷えるのを待ちます。何故かこの時間の間に自分で自分を無駄に責めます。エアフローダメすぎとか、いや、ていうか根本的にオーバークロックしてる時点で、とかぐるぐるしながら待ちます。

さて、こっからが問題なのですが、結局のところ、起動しない原因がCPUかどうかを確定するのは結構厄介です。わざわざほかのマシンに挿してみるのも面倒ですし、それでなんかショートとかしてて、その検証用マザーを壊した日には泣くに泣けません。

というわけで、とりあえず、もう一度だけ、電源をつないでみて、CPUを刺し直して、起動をためしてみました。やっぱりダメです。ここで、結論をだします。諦めてマシンを新調することにしました。

新旧マシン構成の変化点

事の発端が、むやみと長くなりました。さっきクラッシュして、マシン組み直したばかりなので、まだショックから抜け切れていないせいか、まだ妙にハイテンションです。そのせいもあり、どこかのだれかに共感してほしくて長く書いてしまいました。

さて、本題にもどって、新しいマシンにするなら、どういう構成にするかを決めて、必要なものを買い出しです。うちの会社はアキバのPCパーツ系ショップエリアから徒歩3分くらいなので、買い出しには困りません。この会社の位置は素晴らしいです。

結局、開発マシンで壊れている部分を考えると、CPUは間違いなさそう、マザーはどうだろう、微妙だな、という具合です。一番、やすく済ませるなら、とりあえずCPUだけ買ってきて変えてみるのも手なのですが、これを期に、ウチのほかのサーバ用マシンと構成をあわせるのもついでにやってしまおうということで、思い切ってAMDなPhenomからIntel C2Qに変更することにしました。そうすると、もれなくマザーも買い換えです。

なので、購入したのは以下の部品です。

  • Intel Core2Quad Q9550
  • Intel DQ45CB

なんで、DQ45CBかは、はてなさんのサーバが公開された時も紹介されていましたが、キーワードは消費電力とIntel AMTです。このへんの話題はまた来週のエントリを書く際にご紹介したいとおもいます。

もともとのCPUとマザーボードは、以下のような構成です。

  • Phenom 9950BE 3.0GHz (x15に倍率変更)
  • GIGABYTE GA-MA78GPM-DS2H

まあ、ご覧の通り、OCしてるし、VCoreもちょっと盛ってるので、壊れたのはまさに自業自得です。

それ以外のメモリ、HDD、電源などのパーツはすべて流用です。

Gentooの移行

マシンは昼飯ついでに買ってきて、さくさくっとくみ上げます。ざっと配線などをチェックして電源を入れます。うん、うまく動きました。とりあえず、予想通り、CPUかマザーが壊れているので正解だったようです。電源とかだったら、涙目になるところでした。

とりあえず、BIOSとAMTまわりをざーっと設定します。さて、あとは、OSの移行です。もともと、開発マシンはGentooがインストールされていたので、その環境を再現しましょう。

基本的に、すでに動いていたHDDを挿すので、そのまま起動するはず、と一瞬おもったんですが、よく考えたらCPU変えたけど、大丈夫かいな、とおもいつつ、起動します。まずは、不安的中、カーネルがIOMMUまわりで刺さります。

わーん、となげきつつ、しょうがないのでまずはカーネルを入れ替えるところをやります。とりあえず、USB-HDDから起動できるようにしたGentoo MinimalインストールCDイメージがあったので、そのHDDをつないで、そこから起動します。

起動できなかったHDDを適当なところにマウントします。まあ、インストール時と同じ、/mnt/gentooがわかりやすい気がします。

# mount /dev/sda1 /mnt/gentoo
# mount /dev/sda2 /mnt/gentoo/var
# mount -t proc none /mnt/gentoo/proc
# mount -o bind /dev /mnt/gentoo/dev

おもむろに、chrootします。うまくいったので、深く考えなかったんですが、ここで実行できるバイナリだったので、よかったです。chrootできないと結構悲惨でしたね。

# chroot /mnt/gentoo /bin/bash
# env-update

さて、カーネルが刺さるので、コンフィグを見直します。とりあえず、同じ構成でうごいているマシンがあるので、その.configをもってきます。バージョンが2.6.30.2->2.6.30.4だったので、

# make oldconfig

します。さて、ここで問題発生。いつになっても、oldconfigがおわりません。そんな時間のかかる処理だったっけ、とあまりよく考えず、3分くらい待ちました。やっぱり終わりません。ここまできて、ようやくtopとかvmstatを眺めます。cc1が100%のまま、ずーっと張り付いています。ここで激しく嫌な予感。もしかして、gccがPhenom向けにコンパイルされててうまく動いてない感じなのか、と気づきます。そういえば、gccのオプションを/etc/make.confで-march=nativeとかにしています。やばげ。

さて、どうにかしないといけません。とりあえず、作戦を変更して、カーネルだけほかのマシンからもってきて、grubを書き換えて起動します。nicとかがudevのせいで、eth1に変わったりしてちょっと変ですが、とりあえず、うまく起動しました。これで、gccもうごくといいなー、とまったく根拠のない希望をもちつつ、make oldconfigと叩きます。はい、やっぱりダメです。

うーん、これはなんとかして、このマシン上でも動くgccを用意するしかありません。これも、結局、他のマシンのgcc, glibcをもってくることにしました。しかし、どのファイルをもってくればいいのか、特定するのはめんどくさいです。

解決策は、Gentooならquickpkgをつかって、バイナリパッケージをtbz2で作成してくれます。

# quickpkg gcc glibc

そうすると、/usr/portage/packages以下にファイルができあがります。これを開発マシンにコピーします。そして、はじめは、淡い希望をもって、emerge経由でインストールできないもんかと試します。

# emerge -avk =sys-devel/gcc-4.3.3-r2

残念。やっぱりcc1が暴走して、永遠に終わりません。となると、バイナリパッケージって所詮、ただのtarで固められたアーカイブなので、思い切って上書きで展開します。なにかおこったら、諦めて最初から構築しなおしの覚悟を決めます。

# tar jxpvf gcc-4.3.3-r2.tbz2 -C /
# tar jxpvf glibc-2.9_p20081201-r2.tbz2 -C /

さて、とりあえず、gcc -vとかためして、入れ替わったか確認します。うん、上書きされたようです。そして、再度、make oldconfigします。やった、うまくいきました。

ここまでいけば、あとは、カーネル再構築して、バイナリを最適化されているものに入れ替えです。Gentooつかってると、emerge -eDN worldとかちょっとわくわくしますよね。とか書いて、そんなわけねーよと思われていたら、悲しいですが。

そして、現在、emerge worldの最中です。問題無く、コンパイルされているようです。あとは、待つのみ。

最後に

これで、なんとか開発マシンがもとの状態にもどるところまでたどり着きました。

今回の教訓。

CPUとか変更するなら、事前にもうちょっといろいろ考えろよ、自分、っていうところにつきますね。まあ、なんとかなったからいいか、とおもって、毎回、こういう行き当たりばったりなことをしている気がします。直そう。

まあ、とりあえず、こういうときにいろいろ手が考えられるところとかも含め、いろいろGentooは楽しいです。みんなUbuntuとか言わずに、Gentooにしようよ、と社内で布教中なのですが、だれも言うことを聞いてくれません。悲しいです。ていうか、Ubuntuを使っていると、このケースならトラブルなく移行できた気がしますね。負けました。

さて、次回のエントリは、Intel AMTで快適リモート操作〜IPMIなんてなくても頑張れる子 DQ45CB〜をご紹介したいと思います。

では、長々と書きましたが、ここまで読んでくれた方、大変ありがとうございました。

最後に新旧開発マシンはこんな感じ。

プリントサーバという名の開発マシン

Introduction of UBIFS

はじめまして、Cerevoの中河です。
ソフトウェア担当で主にLinuxカーネル/ドライバ回りを担当しています。
今回はUBIFSとういうLinuxで使用できるファイルシステムについて書きたいと思います。

UBIFSって?

UBIFSとはNANDフラッシュメモリ向けに開発されたファイルシステムです。
フラッシュメモリと言うと、一般的にはUSBメモリやSSDが連想されるかもしれませんが、それらのデバイスはハードディスクと同じような扱いが出きるようハードウェア的な仕組みが入っている為、ここでは該当しません。
UBIFSが対象としているのは、あくまでもCPUのNANDフラッシュメモリ・コントローラに直接接続されたNANDフラッシュメモリです。

何故UBIFS?

フラッシュメモリ上では、JFFS2というファイルシステムが広く使用されてきました。
しかし、JFFS2にはフラッシュメモリの容量に比例して、マウント時間やメモリ使用量が大きくなってしまうという設計上の問題があり、昨今の大容量NANDフラッシュメモリには対応できなくなってきました。
そういった問題を解決するために設計・開発されたのがUBIFSです。

JFFS2と比較すると、UBIFSには以下のような特徴があります。
- 高速マウント
JFFS2の様にマウント時にパーティション全体をスキャンする必要が無く、高速にマウントできる。
- 省メモリ
JFFS2はファイルシステムのインデックスをメモリ上に置いていたため、容量に比例して大量のメモリを消費していましたが、UBIFSではインデックスをフラッシュメモリ上に置いているため、メモリ消費量が少ない。
- Write-Backサポート
Write-Backをサポートしているため高速に書き込み処理が出来る。
- UBI
JFFS2はMTDと呼ばれるフラッシュメモリ・デバイスドライバを抽象化したサブシステム上で動作しますが、UBIFSはMTD上にさらにUBIという、ウェアレベリング※1と論理ボリュームを実現するレイヤをのせ、その上で動作します。

UBIFSを使用してみる

- まずホスト環境で、UBIツールをクロスビルドします。
$ git clone git://git.infradead.org/mtd-utils.git
$ cd mtd-utils/
$ CROSS=arm-uclinuxeabi- make

- ビルドしたflash_eraseallとubimkvolコマンドをターゲットにコピーしてください。
$ cp arm-uclinuxeabi/flash_eraseall [ターゲットのroot]/usr/bin
$ cp arm-uclinuxeabi/ubi-utils/ubimkvol [ターゲットのroot]/usr/bin

- 続いてターゲット環境上でUBIFSで使用するmtdパーティションを初期化します。
$ flash_eraseall /dev/mtd1

- 以下のブートパラメータを追加します。
ubi.mtd=1

- 最後にUBI論理ボリュームを作成 & マウントします。
$ ubimkvol /dev/ubi0 -n 0 -N ubifs -m
$ mount -t ubifs ubi0_0 /mnt

まとめ

駆け足でしたが、UBIFSを紹介してみました。
UBIFSは製品レベルで十分使用できるものと考えていますので、大容量NANDフラッシュメモリを使用する場合は、是非UBIFSの採用を検討してみてください。
またそのうち、UBIFSやUBIの内部構造もご紹介できればと思います。

参考
UBIFS – http://www.linux-mtd.infradead.org/doc/ubifs.html
UBI – http://www.linux-mtd.infradead.org/doc/ubi.html

※1 http://ja.wikipedia.org/wiki/%E3%82%A6%E3%82%A7%E3%82%A2%E3%83%AC%E3%83%99%E3%83%AA%E3%83%B3%E3%82%B0

モバイル機器開発Tips ~パワーマネージメント その1~

こんにちは、Cerevoの鈴木です。
今回はモバイル機器では必須のパワーマネージメントについて扱ってみます。

なぜパワーをマネージメントするのか?

ノートパソコンや携帯、デジカメなどのモバイル機器には電源としてバッテリが実装されています。
バッテリの容量というのは大小色々ありますが、有限です。
その限られた電力で出来るだけ長時間動作させたい、というのが目的になります。
(もっと大容量、もっと小型のバッテリが使えるようになると話は変わるのかも知れませんが)

良く使われるパワーマネージメント方法

出来るだけ電力消費を抑えるのが目的になるので、実際には機器内部のハードウェア・デバイスを色々と制御することになります。
いくつかの手法がありますが、世の中の常でプロコンがあります。

ソフトウェアスタンバイ

まずはデバイスの省電力機能を使うことが上げられます。
最近のデバイスはレジスタにアクセスすることで省電力モードになるものがあります。
「ソフトウェアスタンバイ」なんて言葉でこの機能が説明されている場合が多いです。
この手法はお手軽で通常の状態に復帰するのも早いですが、一方でそれほど消費電力が下がらないということがあります。

ハードウェアスタンバイ

次にデバイスのピンの電圧を変化させることで省電力状態にする手法があります。
これは上の例とは異なり、外部からターゲットとなるデバイスのピンに対してHigh or Lowをハード的に出力する手法になります。
「ハードウェアスタンバイ」なんて言葉で説明されている場合が多いです。
この手法はHigh or Lowを出力する仕組みが別途必要になるのでお手軽ではないのですが、大体の場合は消費電力が大幅に低下します。

電源制御

最後はターゲットとなるデバイスの電源をOFFにしてしまう手法です。
これは分かりやすいですね。消費電力は0になります。
ただし、電源ラインをON/OFFできるスイッチ(古くはリレー、最近はFETなんかで構成できます)とそのスイッチを制御する仕組みが別途必要になるのでハードウェア的には一番複雑になります。
しかも電源をOFFにしてしまうので、通常状態に復帰させるためには電源をONにして初期設定をし直さないといけません。
・・・でも、でも、「消費電力ゼロ」は魅力的なわけです。また、起動に1分かかるPCとは違って、起動して初期設定完了まで1~2秒程度、なんてのが一般的なので、意外に使えちゃったりするわけです。ケータイのカメラなんかは良い例ですね。

まとめ

概要は以上なのですが、実際には機器内部には多くのデバイスがあって、動作仕様によって今回の3つの手法を織り交ぜて使います。
パワーマネージメントはハードとソフトが協調して動かないといけないので、ちゃんと設計しないといけない雰囲気は伝わったかと思います。
次回は少し具体的な例を紹介しようと思います。

コンクリート壁への内装品固定方法

こんばんわ、岩佐です。
私は会社でコードを書いていないので、”Tech”はテックでもコードにまで落ちない、商品企画レベルでのテクノロジー系ネタを扱っていくことにします。あと、オフィス設営なんかに関する日曜大工的なTechTopicsなどなど。

というわけで第一弾はいきなり日曜大工系で「コンクリート壁への重量物取り付け」。具体的にはライティングレールなど(※配線には要電気工事2種資格)。

用意するのは電気ドリルとコンクリート用ドリル刃、コンクリートアンカープラグ、タッピングビス。コンクリート壁に通常のタッピングビスを打ってもすぐに抜け落ちてきてしまいますので、ドリルで下穴をあけてからアンカーを指し込み、タッピングビスを打ち込むことでコンクリート内でアンカーが広がり、固定されます。

ポイントはコンクリート専用ドリル刃というのが売っていますので、これを使うこと。柔らかいところであれば鉄鋼用の刃でも太刀打ちできますが、刃がすぐにダメになってしまいます。また、アンカープラグの長さよりも少し長いビスを使うこともポイント。プラグを貫通してプラグが大きく広がることで、がっちりと固定されます。

秋葉原で1本単位で購入するなら、西川電子部品のネジ部がおすすめです。

http://nishikawa.or.tv/map/index.htm

西川電子部品では1本単位のビスに値段が書いていませんが、店員さんに聞けばちゃんと教えてくれます。ちなみに鉛製オールプラグ(サンコーテクノ製)と、それに適合するM3のタッピングビスはそれぞれ10円でした。

なんとなく分かるgtkrcの書き方

こんにちは、稲垣@Cerevoです。
今回はアセンブリ言語から離れてGTK+に関することを書いてみたいと思います。

gtrkrcの書き方

gtkrcをちょっと書いてみようとして、ググったけどまともな解説がなくて挫折したという方、
けっこういるのではないでしょうか?
実は、書くときに必要な知識はGTK+のドキュメントに書いてあります。
http://library.gnome.org/devel/gtk/stable/gtk-Resource-Files.html

しかし他のドキュメントを参照しないと分からない部分もありますし、
そういう部分に限ってドキュメントのポインタが示されていなかったりするのも事実です。

そこで、gtkrcを書こうとしたけどよく分からなかったという人や
自分のプログラムでどう利用したらいいか分からないという人向けに、
gtkrcの書き方・使い方をまとめてみたいと思います。

スタイル

GTK+では、色やフォントの設定はスタイル (GtkStyle) としてまとめられています。
gtkrcでは次のような記述でスタイルを定義します。

style "hoge" {
   fg  [NORMAL] = "#fff"
   font_name = "M+1P+IPAG 12"
   xthickness = 2
   GtkWidget::focus-line-width = 3
}

fg bg text base bg_aaの部分の5種類の部分について、
NORMAL ACTIVE PRELIGHT SELECTED INSENSITIVEの各状態の色を指定します:

fg[NORMAL] = "#fff"

普段はNORMALの色、カーソルが当たったときはPRELIGHTの色などと使い分けることが想定されているようですが、
実際のところ、これらの色を実際にどこに配置するかはウィジェット次第です。
例えばGtkNotebookはちょっとそれ違わない?って感じの配色をしますね。
極端な話、GtkDrawingやGtkLayoutを使う場合は、5×5=25色のパレットだと思ってしまってもいいかもしれません。
お行儀よくはありませんが。

フォント

font_name = "M+1P+IPAG 12"

などと指定します。fontconfigが面倒を見てくれます。

厚み

xthicknessとythicknessはボタンの厚みやボックスのマージンなんかに使われる、
と思います。ほとんどいじったことはありません。

各クラス固有のスタイルプロパティ

普通のプロパティとは別にスタイルプロパティというものが各クラスに定義されており、
gtkrcで設定することができます。

たとえばGtkWidgetにはfocus-line-widthというスタイルプロパティがあり、

GtkWidget::focus-line-width = 3 

などと指定すると、
フォーカスの当たっているウィジェットに破線で描かれる枠が太くなります。

プログラムからの利用

ウィジェットはスタイルを持っており、公開されていますから、普通にポインタでアクセスできます。
例えばNORMAL状態の前景色のGdkGCは

widget->style->fg_gc[GTK_STATE_NORMAL]

としてアクセスできます。
プログラムの中でGdkGCやPangoを操作するのは面倒ですし 、
デザイン変更のたびにリコンパイルするのも大変ですから、
積極的に利用したいものです。

バインディング

バインディングはキーにアクションシグナルを結びつけます:

binding "fuga" {
  bind "Return" { "move-current" (next) }
  bind "Left" { "cancel" () }
}

解説を探してもEmacsキーバインディングにする方法しか書いてないことがほとんどで、
たとえば「どんな名前のキーがあるのか」ということはよく分からないという
ちょっと厄介な部分です。

バインディングも、スタイルと同様、一式を定義してからウィジエットに結びつけるという形をとります。

キーの名前

使うキーの名前は gdk/gdkkeysyms.h に書かれています。
たとえばGDK_Rightと書かれていたら、gtkrcには”Right”と書きます。
どのキーなのかは、おおむね見れば分かります。それで深く考えたことはありませんが、
どうしても気になるときは、GdkEventKeyをダンプするプログラムでも書いたら分かるんじゃないんでしょうか。

アクションシグナル

アクションシグナルの定義は、スタイルプロパティ同様、各クラス (と親クラス) のドキュメントに書かれています。
引数は、基本的に文字列か数か (上記の”move-current”に対するnextのような) シンボルです。
シンボルに関しては、アクションシグナルの定義をみると型が宣言されているので、それを見ればなんとなく分かります。

たとえばGtkMenuShellの”move-current”シグナルは引数にGtkMenuDirectionTypeを取るのですが、
その型は次のように宣言されています:

typedef enum
{
  GTK_MENU_DIR_PARENT,
  GTK_MENU_DIR_CHILD,
  GTK_MENU_DIR_NEXT,
  GTK_MENU_DIR_PREV
} GtkMenuDirectionType;

この場合、型がGtkMenuDirectionTypeなので、シンボルのGTK_MENU_DIRの部分は型名だと思って切り捨てて、
gtkrcには next と書きます。

例えば「キー”Right”にアクションシグナル”activate”をバインド、引数なし」という設定は
次のように書けます:
bind "Right" { "activate" () }
連続して複数のアクションシグナルを送ることもできますから (;で区切る必要はありません)、
ある程度複雑な動作をキーに割り当てることができると思います。

パスによる指定

定義したスタイルはウィジェットに”装備”させないと意味がありません。
ウィジェットは、ディレクトリと同様に階層構造を成しているので、
パス (path) とよく似たウィジェットパスによって特定することができます
(ウィジェットにはクラスがあるのでCSSのセレクタの方が近い)。
パスの区切り文字は “.” です。
いわゆるshell glob構文が使えると書いてあるのですが、[abc]みたいのは使えないようなので、
ワイルドカードに使えるのは?と*のようです。

GTK+2.10以降では、クラスを指定する部分に<someclass>のように書くと派生クラスにもマッチします。
例えば “<GtkBox>” は GtkHBoxにもGtkVBoxにもマッチします。

widget

例えば

widget "mainwindow.GtkHBox.okbutton" style "hoge"

のように、ウィジェットの名前でパスを指定します。
ウィジェットの名前はgtk_widget_set_nameで設定され、デフォルトではクラス名です。

widget_class

例えば

widget "GtkWindow.GtkHBox.GtkButton" style "hoge"

のように、パスをウィジェットのクラス名によって指定します。

class

例えば

class "GtkMenuShell" binding "fuga"

のように、階層構造を無視してウィジェット単体のクラス名によって指定します。

実際のところ、

widget_class "*GtkMenuShell" binding "fuga"

と書くのとほとんど変わらないと思うのですが……

まとめ

gtkrcを書くときに必要な知識を、分かりにくそうなことを中心になんとなくまとめてみました。
gtkrcを活用すればデザインをプログラムから分離することができ、開発が効率的になるでしょう。
Happy hacking!

Home

Search
Feeds
Meta

Return to page top