2017年9月16日土曜日

Raspberry PiのGUIが使いやすいかも

久しぶりにRaspberry PiのGUIを使ってみて、その使いやすさに驚きました。


必要な機能は揃っているし、軽快だし、シンプルだし…

あれ、Unityよりも使いやすいんじゃ…?

Ubuntuユーザーは、デスクトップ環境に関しては、旧GNOMEからUnityへの移行、そしてGNOMEへの回帰と若干翻弄され気味な感じがありますけど、あらためて軽量GUIを使ってみるといろいろ考えてしまいます。

もちろん、Linuxユーザーには選択の自由があるので、気に入ったGUIを使えばよいのだとは思います。

LXQtがうまく行ってくれると、Qtユーザーとしては嬉しいなぁ

2016年11月1日火曜日

Raspberry Pi2強制空冷

Raspberry Pi2にそれなりに重い処理をさせると熱が気になりますが、Webカメラサーバーと無線LANくらいであればヒートシンクをつけてケースに閉じ込めていても大丈夫なようです。

九州でも夏の間一度も停止することなく過ごすことが出来ました。RaspberryPiは意外と熱に強いようです。

ただ、やはりセンサー値で60℃を超えることもあるので、強制空冷を試してみました。

強制空冷と言っても、こんな感じでケースの上に8cmファンを直付しただけです。
ケースも下部に穴が開いているだけのものなので、ケースに直接風を当てることになります。

こんな雑な空冷意味がないだろうと思っていましたが、下のグラフのように10℃近く温度を下げることが出来ました。 空冷恐るべし。

この実験は秋に行ったものなので、夏場では外気温も上がり、もう少し効率は落ちるかもしれません。

ケースを殆ど密閉状態にしていても空冷できることがわかったので、基板がホコリまみれになる心配がなく、 長期的に安定稼働させられそうです。

さすがに常時回転させると気になるくらいの音が出るので、RaspberryPiからCPU温度によって回転数を制御してあげると良さそうです。

2016年7月23日土曜日

Inkscapeでグループ化した図形を変形するとtransform属性が付く

Inkscapeで作業している時に、水平半径と垂直半径を同じにしても矩形の角の丸みが同じにならない問題に悩まされました。

最初はバグかな、とも思ったのですが、XMLを見てみるとtransformという属性がグループに付いていることがわかりました。

グループ化されたオブジェクトを変形すると、transform属性がつくようです。 グループ化していることを忘れて単独の矩形を編集していたのが原因でした。

言われてみると当たり前なのですが、だいぶ悩まされてしまいました。

2016年6月28日火曜日

gitはC風に理解したらいいのかな?

gitをしばらく使ってみても、いまいちよくわからないままでした。 なんとなくスッキリしない感じでした。

でも、ふと「ポインタ」という用語を見て 「C言語風に理解できるんじゃないか?」 と考えてみると、少しだけ理解できたような気分になりました。

gitでのC言語で言うところの
tree,commit,blob,tag等のオブジェクト構造体の実体
ハッシュ構造体へのポインタ値
パス("refs/heads/master"など)構造体へのポインタへのポインタ値
ブランチ、軽量タグ構造体へのポインタ(型変数)
HEAD構造体へのポインタへのポインタ(型変数)
gitによる木、バージョン管理構造体のリンクリスト

という感じではないでしょうか? なんだか回りくどいような気もしますが…

追記1 : detached HEADの場合は直接コミットを指すようなので、HEADはvoid *型変数と言えるかもしれません。

2016年6月26日日曜日

KolourPaint

WindowsからUbuntuに移行しようと試してみたけど、戻ってしまった方も多いかと思います。 私も最初の頃はメインに使うまでにはなかなか至りませんでした。

理由は人によって様々だと思いますが、私の場合は「ペイントがない!」この一点です。

そんなあなたにこのソフト。「KolourPaint」です。

スクリーンショットを見ればきっと説明は不要だと思います。

今はWindowsでも見られないこの感じ。これぞペイント。

他にもペイント風ソフトはいくつかあるのですが、私はこれが一番しっくりきました。 Linux版ペイントをお探しの方はぜひ試してみてください。

2016年2月10日水曜日

motionが保存する動画が早回しになる

追記1 : github上に点在していたmotionの改変版がまとまりつつあるようです。今後はこちらのバージョンがメジャーになっていくものと思われます。

https://motion-project.github.io/

motionという、webカメラの画像に移動検出をかけたり、動画として保存したり、ストリーミング配信できたりする便利なソフトウェアがあります。 LinuxなどのOSで動作するのでRaspberry piで利用されることも多いようです。

しかし、debianとUbuntu上の現在のバージョン(3.2.12+git20140228-4など)には、カメラのフレームレートが高い場合、動画に保存される画像が毎秒3枚分に制限されてしまい、にもかかわらず動画のフレームレートにはカメラのフレームレートが設定されるという挙動が存在します。

この挙動の結果、カメラからの画像がほとんど保存されず、早回しの動画になってしまいます。

原因

これは、カメラのフレームレートが高速である場合に移動検出を毎秒3枚までに制限する機能の副作用によって発生しています。おそらく、設計時に意図されていたものとは異なる挙動であると考えられるので、バグの可能性があります。

対処

この問題はmotion内部の画像を保存するリングバッファーのサイズが1の場合に発生するようです。

従って/etc/motion/motion.confで、

  • pre_captureを1以上
  • minimum_motion_framesを2以上
のいずれかを設定することでこの挙動を抑制することができます。

また、この問題を発生させている部分を特定できたので、debian上のmotion_3.2.12+git20140228-4の場合、以下のパッチを適用することでこの挙動を抑制することができます。ただし、私の利用する範囲内での動作しか確認できていないため、他の問題を引き起こす可能性があります。

--- a/motion.c
+++ b/motion.c
@@ -1294,7 +1294,7 @@ static void *motion_loop(void *arg)
                 cnt->current_image->timestamp_tm = old_image->timestamp_tm;
                 cnt->current_image->shot = old_image->shot;
                 cnt->current_image->cent_dist = old_image->cent_dist;
-                cnt->current_image->flags = old_image->flags;
+                cnt->current_image->flags = 0;
                 cnt->current_image->location = old_image->location;
                 cnt->current_image->total_labels = old_image->total_labels;
             }

github上にはこの問題を修正した上で、さらに機能が追加されたmotionも公開されているので、そちらを利用するのも良いかもしれません。

2015年2月7日土曜日

C言語のinline

C言語のinlineが理解できていなかったので調べてみました.

この記事は個人的なノートであるため間違いが含まれている可能性があります. また,英語の解釈を間違っている部分がある可能性が高いです.

以下の内容はC11の規格に関するものです.古いコンパイラーで試す場合は標準をC11に切り替える必要があります。

何故かリンカーエラー

C言語でも関数にinlineを指定できると聞いて以下のコードを試してみたところリンカエラーになりました.

// 何故かリンカーエラー

inline int add( int l, int r )
{
    return l + r;
}

int main( void )
{
    add( 1, 1 );
}
/tmp/prog-aVTaGb.o: In function `main':
prog.c:(.text+0x19): undefined reference to `add'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

明らかに目の前に関数が存在するにも関わらず,addが未定義だと言っています. しかも,最適化レベルによってエラーになったりならなかったりします.

困った時は規格書を,ということでCの規格書を辞書を片手に調べてみました. 参照した規格はWG14 N1570です.

inlineは関数指定子

inlineは文法上,関数指定子に分類されます.なんとなくstaticやexternと仲間かと思っていましたが,別物のようです. 関数指定子は関数の識別子の宣言の中にのみ現れることができます.

非常に厄介なのは,inlineによって何が起こるかは関数の宣言で識別子のリンケージがどのように指定されたかに依存しているという点です.

インライン関数

まず,どのような場合においても一度でもinlineをつけて宣言された関数はインライン関数になります(6.7.4-6). インライン関数として関数を作ると、その関数への呼び出しは可能な限り高速である(にする?)とコンパイラーに示唆することができます. あくまで示唆であるためコンパイラは無視することも従うこともできます.inline関数の宣言のスコープ範囲内でのみ従うかもしれません. また,インライン関数として宣言された関数は,必ずその翻訳単位で定義されなければなりません.

インライン関数の宣言・定義には以下の3種類があると思います.

  • 内部リンケージを持つ宣言・外部定義
  • 外部リンケージを持つ宣言・外部定義
  • 外部リンケージを持つ宣言・インライン定義

これらは、上から順に以下のような使い方ができると思います。

  • 内部リンケージを持つ関数をinline指定したい(関数マクロのような機能)
  • 外部リンケージを持つ関数を、関数の定義が存在する翻訳単位内でのみinline指定したい(翻訳単位外ではインライン化して欲しくない)
  • 関数を利用するすべての翻訳単位でinline指定される関数を作りたい(インライン化可能な関数を提供するモジュールを作る)

それぞれ順番に考えてみようと思います.

内部リンケージを持つ宣言・外部定義

すべての内部リンケージをもつ関数はインライン関数になることができます(6.7.4-7).しかし,インライン関数とは単に呼び出しが高速である関数という意味しかないため,それ以外に特別なことは何もありません.普通の内部リンケージを持つ関数と同じ扱いです

// 内部リンケージを持つインライン関数の例

inline static int add( int l, int r )
{
    return l + r;
}

static int sub( int l, int r );
int sub( int l, int r );
inline extern int sub( int l, int r )
{
    return l - r;
}

int main( void )
{
    add( 1, 1 );
    sub( 3, 2 );
}

上の例ではaddもsubも内部リンケージを持つ関数です.内部リンケージを持っているため,翻訳単位外から使うことはできません.コンパイラーは関数を削除してmainに埋め込むかもしれませんし,埋め込まないかもしれません.

まとめ

内部リンケージを持つ普通の関数です.インライン関数化することで,コンパイラーがその関数を高速に呼び出す努力をしてくれる可能性があります.

あるモジュールの中だけで頻繁に使う関数はもちろん,関数マクロの代わりに使えるかもしれません. (追記 : 型は固定なので関数マクロという表現は適切ではありませんが、ここでは「呼び出しのオーバーヘッドがない関数をヘッダで提供する」という意味で使用しています) 以下のような関数をヘッダファイルに定義ごと書き,インクルードして使うようにすれば型チェックのかかる安全な関数マクロのようなものになる可能性があります.(内部リンケージを持つ定義なので複数の翻訳単位に現れていいと思うのですが,規格でどのように説明されているかよくわかりませんでした)

// inline_util.h 関数マクロの代わりになるインライン関数
inline static int inline_fast_add( int l, int r )
{
    return __ultra_fast_add( l, r );
}

外部リンケージをもつ宣言・外部定義

inline指定された宣言に加えてinline指定なしのstaticが指定されていないファイルスコープの宣言がひとつでもあるか,inlineとexternの両方が指定されたファイルスコープの宣言がひとつでもある場合,関数の定義は外部リンケージを持つインライン関数の外部定義となります(6.7.4-7). この場合も,インライン関数であるということを除けば単に外部リンケージを持つ関数と同じ扱いです.

// 外部リンケージを持つインライン関数の例

inline extern int add( int l, int r )
{
    return l + r;
}

int sub( int l, int r );
inline int sub( int l, int r )
{
    return l - r;
}

int main( void )
{
    add( 1, 1 );
    sub( 3, 2 );
}

条件を文章にすると非常にわかりにくくなってしまいますが,一般的によく行うプロトタイプ宣言を関数定義とは別に行う場合を考えると,上のコードのsubのように定義にだけinlineを指定した状態などが当てはまります.

add,subのいずれも単に外部リンケージを持つ関数であるので他の翻訳単位から呼び出すこともできます.

まとめ

外部リンケージを持つ普通の関数です.インライン関数化することで,同じ翻訳単位ではコンパイラーがその関数を高速に呼び出す努力をしてくれる可能性があります.

外部に関数の定義を提供する必要があるが,モジュール内では可能な限り高速に実行して欲しい場合に使えるかもしれません. その場合,ヘッダーのプロトタイプ宣言は変更せず,モジュール内の定義のみinline指定します.

外部リンケージを持つ宣言・インライン定義

一番やっかいなのがインライン定義(inline definition)です(6.7.4-7).まず重要な点として,インライン定義は文法上外部定義ではありません(外部宣言ではあります)(6.9-5). 外部定義ではないので他の翻訳単位に定義を提供せず,他の翻訳単位での同名の関数の外部リンケージを持つ外部定義を禁止しません.

また,インライン定義は外部リンケージを持つ同名の関数の識別子の宣言でもあるという点も重要だと思います.

インライン定義になる条件は単純です.その翻訳単位上のすべてのファイルスコープを持つ宣言がinlineを含みexternを含まない場合,その関数のその翻訳単位での定義はインライン定義となります. また,その関数の識別子が式中で使われるのであればプログラムのどこかに必ずただひとつの外部定義が存在しなければなりません(6.9-5). (ただし,同じ翻訳単位に同じ識別子の複数のインライン定義が存在してはならないはずですが,規格のどこを読めばそう判断できるかはよくわかりませんでした)

これが一番最初に出てきたリンカーエラーとなったプログラムと同じ状況です.

// 外部リンケージを持つ外部宣言・インライン定義の例

inline int add( int l, int r )
{
    return l + r;
}

inline int sub( int l, int r )
{
    return l - r;
}

int main( void )
{
    int sub( int l, int r );
    
    add( 1, 1 );
    sub( 3, 2 );
}

明らかに目の前に関数の定義が存在しますが,存在するのはあくまでインライン定義です.従って,コンパイラーの最適化レベルによっては式中のaddとsubは外部定義を参照してしまい,リンカーエラーが発生します.

インライン定義の目的は,他の2つの定義とは若干異なるように感じます. インライン定義は,他のどこかに存在する外部定義の代替を提供するからです. 例えば,ライブラリなどで提供されるある関数の外部定義が存在するが,その関数の性能が良くない,またはコンパイラーの最適化によって(関数の呼び出しをなくすという意味での)インライン化を行いたい場合などに,インライン定義によって代替を用意することにより性能を向上させたりすることができます.

あまり適切な例ではないと思うのですが,このように外部の関数の代替を用意することができます.

// main.c
#include <stdio.h>

inline void func( void )
{
    puts( "inline" );
}

int main( void )
{
    func();
}
// func.c
#include <stdio.h>

void func( void )
{
    puts( "extern" );
}

関数funcのインライン定義をmain.cで提供し,外部定義をfunc.cで提供しています. 実験すると,最適化レベルによってmain.cのfuncが呼ばれたりfunc.cのfuncが呼ばれたりします.

このとき注意しなければならないのは,インライン定義の実行結果は外部定義の関数の実行結果と全く同じでなければなりません. さらに,インライン定義には静的記憶域期間を持つオブジェクトの定義を含んではいけないし,内部リンケージを持つ識別子への参照も含んではいけません(6.7.4-3). プログラマにはコンパイラがどちらの定義を用いるかわからないからです. 従って,上の例は規格に適合したプログラムではありません.

これで一番最初のプログラムがなぜリンカーエラーになったかがわかった気がします. 最初の例ではインライン定義になってしまうため,他の翻訳単位に外部定義が存在しなければならず,コンパイラーが外部定義を参照しリンカーエラーになったのだと思います.

ちなみに,最初の例に最適化(O2など)をかけるとコンパイルが通る場合があります. おそらく,コンパイラは外部定義でなくインライン定義を参照するようになり,リンカーの立場からすれば未解決の参照がないため問題なくリンクが完了するのだと思います. しかし,この状況では必ず外部定義が存在しなければならないので規格適合のプログラムではありません(6.9-5). 現行のコンパイラーでは警告してくれないようなので気をつける必要があります.

まとめ

インライン定義という特殊な関数です.他の翻訳単位に外部定義が存在する場合にのみ定義することができます.

外部定義された関数の高速な代替を作りたい場合に使えるかもしれません.ただし,ヘッダーからinline指定なしのプロトタイプ宣言を読み込んでしまうとインライン定義ではなく外部定義になってしまい,多重定義になってしまいます. 非常に扱いが難しい関数だと思います.

モジュールのヘッダー内のプロトタイプ宣言をインライン定義にしてしまうという使い方もありそうです。 モジュール外部に公開する必要があるが、可能な限り高速にしてほしい関数がある場合にインライン定義を使用すれば、その関数を使うそれぞれの翻訳単位でコンパイラが最適化してくれる可能性があります。コンパイラは外部定義を使っても良いしインライン定義を使っても良いので最適化の幅が広がりそうです。 ただし、モジュール内に外部定義を生成する必要があるので、モジュール内に宣言のみを書いて外部定義を生成させるという不思議な状況になります。

この方法が本来のインライン定義の使い方なのかもしれません。

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

// インライン関数の外部定義またはインライン定義
inline void module_inline( void )
{
    puts( "inline" );
}

// 普通の関数のプロトタイプ宣言
void module_func( void );
// module.c
#include "module.h"

// インライン関数の外部定義を生成するための宣言
inline extern void module_inline( void );

// 普通の関数
void module_func( void )
{
    puts( "func" );
}
// main.c
#include "module.h"

int main( void )
{
    // モジュールの使用
    module_inline();
    module_func();
}

module.cでわざわざinline指定しているのは、インライン定義を使用していることがわかりやすくなるかなと思ったからです。

実際にコンパイルしてみると、main内でmodule_inlineだけがインライン化されることが確認できました。

# main.s
    movl    $.L.str, %edi
    callq   puts
    callq   module_func
    xorl    %eax, %eax
    popq    %rdx
    ret

C++のインライン関数

C++の規格はほとんど理解していないのですが,言語リンケージなどでinlineを含むCのプログラムを取り込むと何が起こるのか知るために少し調べてみました.規格を直接読むのは大変なので,日本語でC++11を解説してくださっているサイトを参考にしました.

http://ezoeryou.github.io/cpp-book/C++11-Syntax-and-Feature.xhtml#dcl.fct.spec.inline

私の理解では,C++のinline指定はリンケージの影響は全く受けないが,inline指定された宣言が現れる翻訳単位には定義がなければならないという,Cのインライン関数に似た制限があるみたいです.

インライン定義が存在しないインライン関数という感じでしょうか.

少し異なるところは,C++では外部リンケージを持つ外部定義であっても,まったく同じ文であれば異なる翻訳単位に複数存在できるというところだと思います. Cの外部リンケージを持つインライン関数の外部定義は,どこかの翻訳単位に一つしか存在できません.しかし,C++では許されます.

モジュール内(.cファイルなど)の定義にはinlineが指定されているが、ヘッダーファイル内のプロトタイプ宣言にはinline指定されない関数がヘッダーファイルを通してC++にC言語リンケージで取り込まれた場合,単に外部リンケージを持つ識別子の宣言となり問題なくC側のインライン関数にリンクできると思います.

staticとinlineが指定され,定義された関数が含まれるヘッダーをC言語リンケージでC++に取り込んでも,単純にC言語リンケージと内部リンケージを持つインライン関数が定義されるので問題なくC++側で使用できそうです.

インライン定義を含むヘッダーがC++にC言語リンケージで取り込まれた場合は少しむずかしそうです。 C++では外部定義が生成されてしまいます。従って多重定義になるはずですが、ヘッダーで提供されたインライン定義ということは必ず同じ文字並びであるのでC++では合法になりそうです。

インライン関数のまとめ

インライン関数を調べるために規格を追いかけてみましたが,とても難しかったです. 一つのことを確かめるために規格の様々な場所を参照する必要があったり,表現が独特だったり引っかかるところが多いです.

一番使い勝手が良いのはやっぱり内部リンケージを持つインライン関数ではないでしょうか. 安全な関数マクロにもなるし,組み込み用途でも移植性のある記述ができそうです.

インライン関数はあくまでコンパイラーへの最適化のヒントでしかないので,最適化レベルやコンパイラによってインライン化されたりされなかったりします. 確実なインライン化が必要な場合,コンパイラーオプションの調整やアセンブリの確認が必要だと思います.