2013年12月30日月曜日

ビット演算:ビットの置換

ビットを指定して置換する例題です。

以下の例題では、8ビット内の開始位置、長さを指定して値を挿入しています。
挿入する値が指定した長さのビットの値より大きい場合には、値を変更しないようになっています(repBits 内 bitsthr でのチェック)。 実行結果

スレッドの停止:pthread_cancel/pthread_kill

pthread によるスレッド処理を強制的に停止する例題です。

POSIX スレッド標準を実装したライブラリ pthreads は、pthread_cancel によりスレッドを強制的に停止することが可能ですが、処理系によっては実装されていない場合があります。
※ Android の NDK では標準的に pthreads を利用できる形になりますが、pthread_cancel は実装されていません。
 ライブラリのサイズが大きくなるのと、OS 内でのプロセス管理が複雑になる等の理由により実装されていないようです。

実装されていない場合には pthread_kill により(USE_THCANCEL = 0)スレッドを停止することも考えられますが、この処理も処理系によっては上手く動作しない可能性があります。

以下の例題では、while 文での処理内に sleep による待ち時間を設けながら処理を行っているので、pthread_cancel が使えない場合には処理を即座に停止することが困難になります。
※ OSX/Linux 等では正常に動作するが、Android/NDK では正常にスレッドが停止されずに pthread_kill の部分でループが終了しない状態となります。
pthread_cancel が実装されていない Android/NDK にてこのような処理を行う場合には、pthread によるタイマー的な動作は諦めて、Java 側での Handler による遅延実行から NDK 処理を行う等の方法に切り替えることをお薦めします。 実行結果

2013年11月28日木曜日

rsyslog へのログ出力:ログファイルを変更して出力

rsyslog に対するログファイルを変更してログを出力する例題です。

syslog.h (ref. http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/syslog.3.html) に記載されている LOG_USER での出力は通常コメントアウトされている場合があります(/etc/rsyslog.d/50-default.conf 内)。 これを有効化するにはコメントを外し、以下のコメントを実行します。 こうすることにより、/var/log/user.log へ openlog/syslog 関数によりログが出力されるようになります(facility にて LOG_USER を指定した場合)。

一方、別のファイルとして出力したい場合には別の設定を追加する必要があります。
openlog で設定できるログのタイプにはローカルで利用できる facility が LOG_LOCAL0 から LOG_LOCAL7 まで用意されているので、そのいずれかを利用することによりカスタムのログファイルを利用することができます(他に利用されていないことが前提)。
LOCAL5 を利用する場合には rsyslog の設定ファイルに下記の行を追加し、rsyslog を再起動します(/var/log/custom.log へログ出力する設定)。 以下の例題では、LOCAL5 へとログ出力を行い /var/log/custom.log へログを出力するための例題です。
ログの出力を細かく制御する際には、/etc/rsyslog.d/50-default.conf にさらに追加のルールの記述が必要となりますが、今回の例題では全ての出力が同じように出力される結果となります。 実行結果

2013年11月27日水曜日

ネットワークインターフェースからのIPアドレスの取得

ネットワークインターフェースを指定して IP アドレスを取得する例題です。

Linux/Android では ifconfig/netcfg というコマンドがあり popen からその出力を取得する方法も考えられますが、例題ではネットワークインターフェースを指定して、必要なIPアドレスを直接取得しています。

また、192.168.x.x などの文字列ではなく、16進数の値としてアドレスを取得しています。 実行結果

2013年11月26日火曜日

設定ファイルの入出力

適当な区切り文字(「:」、「=」 etc.)で区切られて保存されている設定ファイルを入出力する例題です。

設定ファイルから読み込まれた値は全てメモリ内に動的に確保され、設定の変更後(キーによる検索、更新、追加)、元の設定ファイルに書き出すことができる例題となっています。

区切り文字の値(例題では「 = 」)を適宜変更して利用できます。

設定ファイルは値を書き込んだものを予め準備していますが、無い場合にはファイルを作成し、SetSettings にて設定した値が SaveSettings にてファイルに書き込まれます。 実行結果

2013年11月13日水曜日

libudev による USB デバイスのモニタリング

libudev により USB に接続されているデバイスをモニターする例題です。
※ テスト環境 Linux picuntu 3.0.8+:ディストリビューションによる動作の差異はあるかもしれませんが、Ubuntu 系なら問題なく動作するかと思います。

Linux では udevd というデーモンが起動しており、/etc/udev/rules.d/ 内に記述された udev ルールに応じて、USB に接続されたデバイス毎のプロセス起動やマウント等の処理を行うことができます。

USB メモリを挿入した際に実行する処理の記述例: 一方、挿入された際の処理(ACTION=add)に対して抜かれた際の処理に関しては(ACTION=remove)、デバイスの情報が取得できず処理が実行されない可能性があります(デバイスノードからデバイスディスクリプタが取得されないのが原因かと思われる)。

そこで、挿入された際のイベントに対して起動するプロセスに対して、終了する際のイベントに関してはデバイスが抜かれた際のデバイスへのノード(/dev/usbdev2.4 etc..)を調べ、挿された際のノードと一致するかによって目的のデバイスが抜かれたかを監視する方が無難な監視方法になるかとは思います。

以下の例題では、アプリ起動時に接続されているデバイスの一覧を表示し、目的のデバイスが挿入されているかを監視しています。
目的のデバイスが挿入された際に記憶しておいたノードの情報により、デバイスが抜かれたことを検知し、デバイスが挿入されていない状態になればアプリを終了しています。

この例題をそのままアプリ化して udev ルールから実行してデバイスが抜かれた際に終了するようにしても、上手く終了されない可能性があります(exit コマンドを行ってもアプリが終了しない)。
※ 原因はよく分からないのですが、デーモン用のプロセスとして実行しないと挿入されている間は常時プロセスが起動するような形で再実行されているのかもしれません。
その場合、start-stop-daemon コマンドを利用して下記のようなシェルスクリプトにて実行するようにすれば上手くアプリを終了させることができます。
パスやユーザーを適当に編集した上、udev ルールに下記スクリプトへのパスを設定した後に udevd をリスタートしてください。
※ [/path/udevMonitor.sh start] を RUN+= に追加
start-stop-daemon はアプリをデーモン化するためのコマンドですが、インストールされていない場合にはソースが配布されていますので、そちらをコンパイルしてインストールすることもできます。
start-stop-daemon

udevMonitor.sh コンパイルには libudev を利用しますが、インストールされていない場合には apt-get から libudev-dev をインストールしてください。 実行結果

シグナルハンドラによるプロセス終了時の処理

シグナルハンドラを設定することにより、アプリに対して SIGINT/SIGHUP/SIGTERM(キーボードからの割り込み/端末との切断/終了)などのプロセス終了のシグナルが送られた際、そのままアプリを終了するのではなく、終了前の片付け処理などを行うことができます。

普通にアプリを書いていれば何らかの判定処理によって終了を行うと思いますが、デーモン用のプロセスとして処理を記述する場合などでは、kill コマンドからプロセス終了が行われるためこのような処理が必要になる場合があります。

例題では、シグナルを受け取った後メッセージを表示し(daemon stopped.)、アプリを終了させています。
コマンドラインからは kill コマンドを送っていますが、この時点ではアプリは終了せず、アプリ内で明示的にアプリを終了しない限りはアプリは継続して動作します。
一方、kill -KILL にてアプリを強制終了させた場合にはシグナルの通知に関係なくアプリが終了されます。 実行結果

2013年10月27日日曜日

ファイルのカット:複数のファイルオープンを行わない方法

ファイルの一部分をカットし、保存し直す例題です。

ファイルの分割や、一部を切り出して別のファイルに書き出す等の例題では、fopen にてファイルポインタを複数用いて別ファイルとして書き出すことが考えられますが(例題を準備するまでもなくいくらでも検索にヒットするでしょう)、ファイルポインタの操作では、追加書き込みしかできないため、同一ファイル上で一部分をカットし保存し直すことが不可能です。
そこで、本例題ではファイルディスクリプタを用いて、同一ファイル上の操作を行っています。

本来はこの例題にて 64bit 長のファイルサイズにも対応したものを準備したかったのですが、開発環境側の準備も必要になりますのでそちらが準備できれば両環境に対応した例題に修正して再度掲載すると言う形になります(OSX ではこのままでも 64bit 対応可能のようです)。
※ 64bit 版として利用する場合には、off64_t/lseek64 への置き換えが必要(Linux etc.)
64bitファイル対応に関して

この例題では、ftruncate にてファイルサイズを切り詰めて保存しているのですが、この関数はファイルの後半部を切り詰めたり伸長することしか対応しておらず、while 分のようにファイルの後半部分を前半に移動し直すと言う作業が必要となっています。
後半部分だけをカットする場合にはこの関数のみでも対応は可能ですが、前半部分をカットできないために下記のような例題となります。
実行結果

64bitファイル対応に関して

最近のOSやプラットフォームでは64bitのファイルシステムが当たり前となっていますが、C言語(gcc/g++)においてそれがいつからサポートされているのかは開発環境のアップデートやら何やらをひと通り準備してからでないといけなくなります。

つい最近、Android にて 2G 以上のファイルが対応できない壁にぶち当たりちょっと気になっているところです。

Android/NDK での対応:現状サポートされていない
・SD が FAT32 フォーマット
・libc が 64bit をサポートしていない

Windows での 64bit 対応:
fseek/_fseeki64:VC++ にて利用可能

OSX や Linux での対応:
FSEEKO:コンパイラ等に -D_FILE_OFFSET_BITS=64 を指定して利用可能

一方、Snow Leopard 64bit で下記のソースを実行すると、コンパイラに何も指定しなくても普通にシークはできています(off_t のサイズも64bitになっています)。 実行結果: 私の環境では(Snow Leopard 64bit/Xcode 4.2)、lseek64/llseek/_llseek 等がサポートされていないのですが、XCode をインストールした際に 64bit 対応としてくれているのかもしれません。

LSEEK64 のページによると、
関数 lseek64() は glibc 2.1 以降で使用可能であり、 llseek() のエイリアスとして定義されている。
とあるので gcc 側の対応としてはそちらが正解なのでしょうが、Apple が 64bit に対応した gcc をインストールしてくれていると言うことになるんでしょうかね??

一応 MacPorts での gcc/glibc 等のアップデート方法を試みたのですが、libgcc のインストールにてコケてしまいあえなく中断・・現行のままで利用可能と言うことにしておきましょう。