mcommit's message

ソフトウェア開発の仕事をしているsimotinといいます。記事の内容でご質問やご意見がありましたらお気軽にコメントしてください\^o^/

低レイヤプログラミングのためのRubyの拡張モジュール開発入門

この記事は、低レイヤのコードをRubyで書きたいという人のための拡張モジュール作成入門記事です。

Rubyが好きでC言語がある程度分かる方には読んで頂けると参考になるかと思います。※ただし、私が真面目に拡張モジュールを書いていたのはもう5年以上前なので知識が古くなっている可能性があります。

この記事を書いた動機ですが、RXマイコンのリンカをRubyで書いていて、Rubyで書いているとC言語の構造体へのアクセスが辛くなってきたので、FFIや拡張モジュールを使うべきだったと感じました。

久しぶりに拡張モジュールの書き方など復習してみたので記事にしてみました。

FFIも拡張モジュールもちょこちょこと触っているのですが、私のように

「やる気はないけど、進捗は欲しい」

という方には、RubyC言語を上手く組み合わせて開発をするというのは悪くない実装方針ではないでしょうか*1
この記事を含めて、できれば低レイヤのプログラミングの為に拡張モジュールやFFIを上手く使う方法として整理したいと思います。

目次

  • 目次
  • Rubyの拡張モジュールとは何か
  • 簡単な拡張モジュールを書いてみる
  • 事前準備
  • ビルドと実行手順
  • 解説
    • extconf.rb
    • Makefileの生成
    • 拡張モジュールのビルド
    • コード(extlib.c)解説
  • 参考サイト
  • まとめ

Rubyの拡張モジュールとは何か

拡張モジュールはC言語C++言語で実装したRuby用のライブラリです。
誰かに挨拶するコードをRubyで書く場合、

def hello name
  puts "hello " + name + "!"
end

hello("simotin") # => hello simotin!

のように .rb の拡張子のファイルに Ruby の文法に則って書くのが普通のRubyのプログラミングです。

これに対して拡張モジュールで実装する場合、

*1:私も試行錯誤中な感じですが・・・

続きを読む

C言語でEthernetFrameをpcap形式でダンプしてみる。

ネットワークの勉強のためだいぶ前に買っていた「ルーター自作でわかるパケットの流れ」を読んでいます。

ルーター自作でわかるパケットの流れ

ルーター自作でわかるパケットの流れ

書籍の中で、pcap形式について触れられていて、随分とシンプルなフォーマットだったので自分でもpcap形式のファイルをダンプしてみました。

準備

pcapに関する宣言などは、pcap.hでされているらしいのですが、pcap.hは標準ではインストールされていません。libpcap-dev のパッケージインストールを行います。


$ sudo apt install libpcap-dev

で pcap.hが/usr/includeにインストールされます。

pcap形式で出力するには struct pcap_file_header というファイルのヘッダ情報と
struct pcap_pkthdrというパケット単位のヘッダを付与し、後はフレームの中身を出力すればいいそうです。

ところで、struct pcap_file_header にいろいろ値を設定する必要があるがどこで定義されているかよくわかりません。
例えばEthernetフレームであれば、linktypeには LINKTYPE_ETHERNETとかを入れるようだがLINKTYPE_ETHERNETはpcap.hには見つからない。DLT_EN10MBでもよいらしいがこちらも見つからない。

いろいろ情報をあさっているとこちらのサイトがlibpcapの本家らしいということで、
www.tcpdump.org

とりあえずlibpcapのソースをダウンロードして調べてみます。
https://www.tcpdump.org/release/libpcap-1.9.0.tar.gz

grep してみると、
LINKTYPE_ETHERNETはpcap-common.cで宣言されている。ということはこの定義は非公開のようです。

一方、DLT_EN10MB は pcap/dlt.h で宣言されています。
こちらのファイルは apt で libpcap-dev をインストールしたときに /usr/include/pcap/dlt.h
としてインストールされていました。

ということで、ヘッダに必要な情報を適当に詰め込んで適当に動かしてみたところちゃんとwiresharkでも表示できました。

とりあえずキャプチャできてそうなコード

github.com

#include <stdio.h>
#include <net/if.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <linux/if_packet.h>
#include <pcap.h>

#define TCPDUMP_MAGIC (0xA1B2C3D4)

static int openNetworkInterface(char *ifname);

int main(int argc, char **argv) {
	int sock;
	unsigned char recvBuf[1024*128];
	FILE *fpPcap;
	struct pcap_file_header pcapHeader;

	if (argc < 2) {
		fprintf(stderr, "must specify interface name.\n");
		exit(EXIT_FAILURE);
	}

	fprintf(stdout, "%s\n", argv[1]);
	sock = openNetworkInterface(argv[1]);
	if (sock < 0) {
		fprintf(stderr, "Open Interface [%s] failed.", argv[1]);
		exit(EXIT_FAILURE);
	}

	fpPcap = fopen("dump.pcap","wb");
	if (fpPcap == NULL)
	{
		fprintf(stderr, "Open Interface [%s] failed.", argv[1]);
		close(sock);
		exit(EXIT_FAILURE);
	}

	memset(&pcapHeader, 0, sizeof(struct pcap_file_header));
	pcapHeader.magic = TCPDUMP_MAGIC;
	pcapHeader.version_major = PCAP_VERSION_MAJOR;
	pcapHeader.version_minor = PCAP_VERSION_MINOR;
	pcapHeader.snaplen = 2048;
	pcapHeader.sigfigs = 0;
	pcapHeader.linktype = DLT_EN10MB;
	fwrite(&pcapHeader, sizeof(struct pcap_file_header), 1, fpPcap);
	while(1) {
		int recvSize;
		struct pcap_pkthdr pktHeader;

		recvSize = read(sock, recvBuf, sizeof(recvBuf));
		if (recvSize < 0)
		{
			fprintf(stderr, "read error [%d]\n", recvSize);
			continue;
		}

		fprintf(stdout, "recvSize:[%d]\n", recvSize);
		memset(&pktHeader, 0, sizeof(struct pcap_pkthdr));

		gettimeofday(&(pktHeader.ts), NULL);
		pktHeader.caplen = recvSize;
		pktHeader.len = recvSize;

		fwrite(&pktHeader, sizeof(struct pcap_pkthdr), 1, fpPcap);
		fwrite(recvBuf, recvSize, 1, fpPcap);
	}

	close(sock);
	fclose(fpPcap);
	exit(EXIT_SUCCESS);
}

int openNetworkInterface(char *ifname) {
	int ret;
	int sock;
	struct ifreq ifreq;
	struct sockaddr_ll sa;

	sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
	if (sock < 0) {
		fprintf(stderr, "socket failed %d\n", sock);
		return -1;
	}

	memset(&ifreq, 0, sizeof(struct ifreq));
	strncpy(ifreq.ifr_name, ifname, sizeof(ifreq.ifr_name)-1);

	ret = ioctl(sock, SIOCGIFINDEX, &ifreq);
	if (ret < 0) {
		fprintf(stderr, "ioctl failed [%d]\n", ret);
		close(sock);
		return -1;
	}

	sa.sll_family=PF_PACKET;
	sa.sll_protocol=htons(ETH_P_ALL);
	sa.sll_ifindex=ifreq.ifr_ifindex;

	ret = bind(sock, (struct sockaddr *)&sa, sizeof(struct sockaddr_ll));
	if (ret < 0) {
		fprintf(stderr, "bind failed [%d]\n", ret);
		close(sock);
		return -1;
	}

	return sock;
}

できていないこと

  1. フレームのタイプのチェックとかしてないけどいいんだろうか(ARPとかICMPとか・・・)
  2. 異常系あまりケアしてません。Ctrl-Cとかで止めると多分最後のパケットは適切にダンプされない。

関連書籍

著者の小俣さんの続編的な書籍!?が出版されていて、この本も買ってみたのですが、積本になってしまっています。
せっかく買ったので読みたいけど読めていない・・・

感想

自分でパケットキャプチャしてみて、ゴニョゴニョするのってなんかかっこいい。

マグネットペイントを使って木製ボードでタスク管理ができるようにしてみた

f:id:simotin13:20180729150921j:plain


メモの付箋を貼ったりして使おうとだいぶ前に壁掛けの木製のボードを買っていたのですが、付箋紙が直ぐにはがれてしまい全然活用できていませんでした。

最近嫁さんと話していると「マグネットペイント」なるものがあって、それを塗ると磁石がくっつくようになるということで、マグネットペイントを使ったタスク管理ボードの制作に挑戦してみました。

目次

  • 目次
  • マグネットペイントとは何か
    • お値段は?
  • 塗り方
  • 色合い
  • 仕上げ塗りをしてみた
  • 塗るときに注意すべきポイント
    • 塗料の粘り具合について
    • 刷毛で塗った仕上がり
    • 磁石のつきやすさについて
  • 結果

マグネットペイントとは何か

マグネットペイントとは文字通り「塗れる磁石」です。今回の私のケースのようにボードに塗って磁石が使えるようにしたり、壁紙に塗ったりすることもできるようです。

続きを読む

ユダヤ式Why思考法を読んだ

ここ最近、ユダヤ人というものに興味があり時々ユダヤ人・ユダヤ教に関する本を読んでいるのですが、ユダヤ人に関する本の読書録を書けていなかったので書いておきたいと思います。

ユダヤ人のイメージ

私は歴史が大好きなのでユダヤ人という集団のことは当然知ってました。ユダヤ人のイメージというと、

といったイメージがあります。

例えばアメリカの映画とかを見ていても上記のような優秀な人、お金持ちの人という人物像で登場することが多いように思います。ユダヤ人の成功者という面と受難者という面がとても興味深い存在です。

ユダヤ人に興味を持ったきっかけ

最近になって自分のユダヤ人に対する興味が強くなってきた理由ですが、お客さんと画像処理・顔認証に関する雑談をしていて、

今の時代、顔認証はイスラエルには勝てないよ。うちで使ってる商品も顔認証はライブラリをイスラエルの会社の製品を買っているんだ。

という一言を聞いたからでした。
*1

そこから、

どうしてユダヤ人には勝てないんだろう。ユダヤ人の強さの根源は一体何なんだろう?

という疑問や興味が強く自分の中で湧いてきたのでした。

読んでみた本

ということでいくつかユダヤ人に関する本を読んでみたのですが、今回はタイトルにある通り、「ユダヤ式Why思考法」という本を読んだので読書録を書いておきたいと思います。

ユダヤ式Why思考法

ユダヤ式Why思考法

概要

実はこの本を読む前に「ユダヤ人の成功哲学「タルムード」金言集」という本を読んでいました。

ユダヤ人の成功哲学「タルムード」金言集

ユダヤ人の成功哲学「タルムード」金言集

この本はタイトルの通り、ユダヤ人の聖典とされるタルムードについてエピソードなどを交えて紹介する書籍です。
結構面白かったです。*2
著者の石角さんは日本人でありながらユダヤ教徒に改宗!?された方ということで今回「ユダヤ式Why思考法」も読んでみました。

ユダヤ式Why思考法」は、様々な問い・質問が著者から出され、それに対し「あなたはどう考えるのか?」「なぜそう思うのか?」が読者にも求められながら、「ユダヤ人はこう考える」という解説と教訓が語られるというスタイルです。

登場する質問というのはクイズではありません。どちらかというと答えのないような問いが多いように思います。
例えば、「水はなぜ透明なの?」と子供に聞かれたらあなたなら何と答えるだろうか?
というような質問が著者から投げかけられます。

著者の石角さんが訴えたいことはそれぞれの問に対して正解となる答えを期待しているというよりも、

自分の頭で考え抜く習慣を持つべきだ

ということを主張されているように全体を通して感じました。

日本批判

本書を読んでいて気になった点として全体的に現在の日本人に対する批判が多くみられます。*3
指摘の内容は確かにもっともな内容で、愛ゆえの指摘なのかもしれませんが、私には少し論点がずれているように感じる点もありました。

例えば「日本人はiphoneをなぜ作れなかったのか?」章が最後に書かれています。
iphoneににたザウルス(シャープ)を作っていた日本人がiphoneを作れなかった理由を、開発者や企業「日本社会の空気を読みすぎたためではないか」という風に分析されています。
それは確かに理由の一つとしてそのような理由はあるかもしれませんが、iphoneを作れなかったのは日本人だけではなくフランス人もドイツ人もイギリス人も中国人も同じです。ユダヤ人も作れていません。

iphoneをアップル以外のアメリカの企業はなぜ作れなかったのでしょうか。

という疑問がわきます。*4

物つくりというのは個人の才能や能力によるところが大きいのでiphoneのような一商品と日本人という集団を比較に出すのは例として説得力に書けるように思いました。

感想

ユダヤ人のものの考え方、習慣を知ることができて面白い書籍でした。

なんとなくですが、ユダヤ人に優秀な人が多い理由が分かった気がします。あと差別されてきた理由も・・・

ユダヤ人のように自分の頭で考え抜くということにこだわる姿勢は自分も真似していきたいです。

他にもユダヤ人について書かれた本をいくつか読んだので少しずつ読書録とかまとめの記事を書いてみたいと思います。

*1:たしか日本の自動車メーカーが自動運転のための認識用ソフトの自社開発を諦めたみたいなニュースを聞いたのもこのころだったと思います。

*2:今回読んだ「ユダヤ式Why思考法」の内容と少しですが被る部分もありました

*3:これは「ユダヤ人の成功哲学「タルムード」金言集」でも同様でした

*4:アップル以外のアメリカ企業では日本人をたくさん雇っていたのかもしれませんね・・・

数学素人が線形代数を学ぶときに読んでみてよかった書籍

今年から帝京大学の通信教育課程に入学して勉強しているのですが、線形代数の講義*1を受けるにあたって関連する本を何冊か読んでみました。

せっかくなので、勉強していて躓いたときに助けになった本の紹介記事を書いておきたいと思います。*2

動機

そもそも、本をいくつか読んだ理由ですが、講義で指定教科書をベースに演習問題をこなすことが求められるのですが、個人的に躓いたところの解説が指定教科書には書かれていなかったり説明が少なかったりしたので、躓いたところの答えや考え方を示してくれている書籍を買い漁りました。

躓いたところ

行列式の計算

実際に線形代数を学んでみて分かったのですが、理解すべきポイントは行列式の計算をマスターすることなのかなと感じました。

行列式の計算は3次の正方行列であればサラスの公式が使えます。
サラスの公式という公式はありますが、2次の正方行列と同様に、たすき掛けに掛け算して和と差をとるだけです。

ここまではすんなり理解できたのですが、4次の正方行列の行列式を求めるとなると、劇的に難易度が高くなります。

4次の正方行列では、余因子行列毎の行列式を求めて足したり引いたりするのですが、この足したり引いたりの符号の向きが、どのように決まるのか教科書を読んだだけでは理解できませんでした。

色々書籍を買って、この部分が理解できたのは、「マンガでわかる線形代数の関連箇所をじっくり読んでみたときでした。

マンガでわかる線形代数

マンガでわかる線形代数

この書籍はm文字通りマンガをベースとして難しい数学や物理などの解説をしてくれるシリーズの線形代数版です。
この書籍では、第4章 行列(続き) で4次の行列式の計算について触れられています。

他にもいくつか線形代数の本を買い漁って関連する章を読んでみたのですが、行列式の計算については、この書籍が一番しっくりきました。

固有値

固有値固有ベクトルに関するについても、よくわからずに躓いたのですが、固有値については、

線形代数がわかる」という書籍での説明が分かりやすかったように思います。

線形代数がわかる (ファーストブック)

線形代数がわかる (ファーストブック)

これから読んでみる本

そもそも線形代数を本格的に勉強する前に「プログラミングのための線形代数

プログラミングのための線形代数

プログラミングのための線形代数

という本を買っていたのですが、一切読まずに積み本化していました。

一通り、線形代数に関する基本的な内容は勉強できた(はず!?)なのでこれからこの本も読んでみたいと思います。
注意点として、この本は、目次をざっと見た限りでは「プログラミングで線形代数を活用してみる」というコンセプトではなくて「線形代数を勉強するのにプログラミング(コンピュータで計算)を活用する」という構成になっているようです。

ベクトルの可視化とかあるので、とりあえず一通り学んだ今の自分にはちょうど良いかもしれません。

その他

演習問題の計算をするときによくこちらのサイトのお世話になっておりました。

keisan.casio.jp

このサイト、行列計算に限らず単位換算とかでよくお世話になっていたのですが、電卓のカシオが運営されているんですね。*3

*1:講義といっても通信教育なのでほぼ独学なのですが・・・

*2:帝京大学の通信教育課程については折を見てまた別の記事を書きたいと思います

*3:カシオ様いつも利用させて頂きありがとうございます!感謝

ARMの命令セットの条件指定について (2)

昨日の続き。

mcommit.hatenadiary.com

ARMの命令では最上位4ビットで条件指定ができることがわかりましたがAL(無条件実行)以外の使い方がよく分かりませんでした。

命令の実行条件を指定するというのはイメージ的には

r0 = 1 if flag == true
r0 += 2 

のようにRubyの後置ifのようなおしゃれなイメージがあります。
何が嬉しいかというとこの書き方だとelseを書かなくてよいのでソースコードレベルでのステップ数は削減できます。
分岐の結果に関わらず後続の処理を必ず行う場合はこの条件指定を使ったほうがよさそうに見えます。

さて、ARMの命令として具体的にどのように条件指定をするかを調べてみたところ、アセンブラだと命令の後ろに条件のサフィックスを指定するようです。

例えば、

MOV r0, #1

であれば

CMP       r0, #1
MOVEQ r0, #1

とすると、事前の比較命令の結果に応じてMOVを実行するか判断してくれるようです。
MOVEQ以外にもADDEQとかあります。

もちろん条件指定もEQ以外にあります。

条件指定実行のメリット

  1. 分岐命令を使うより命令数が少なくできる場合がある→サイズの節約
  2. 条件指定実行だと、条件が成立していない場合は後続の命令が実行されるので、ジャンプ先アドレスを別途取得する必要がない。

といったところでしょうか。

条件指定実行のデメリット

よく分かりません・・・なんとなく使える状況では使って行ったほうがよい気がしますが。

実際

実験のために、raspberrypiでMOVEQを使ってくれそうなコードを書いてビルドしてみました。

int add(int oneOriginFlg, int a, int b)
{
   int ret = 0;
   if (oneOriginFlg == 1)
   {
      ret = 1;
   }
   ret += a;
   ret += b;
   return ret;
}

コンパイルの結果、残念ながら普通にコンパイルしただけでは、MOVEQを使うコードははいてくれませんでした。

gcc -Osとか O1とかつけるとMOVEQをつかってくれていたのでgccだと必要に迫られないとこういった条件指定実行のコードははいてくれないようです。
gccは多くのアーキテクチャをサポートしているので当然といえば当然かも知れません。

参考書籍

今回も調査にはARMで学ぶアセンブリ言語入門を参考にしました。

ARMで学ぶ アセンブリ言語入門

ARMで学ぶ アセンブリ言語入門

条件実行についてもP62-P63で説明されています。ARMのアセンブラを学習するのには重宝しています。

ARMの命令セットの条件指定について

ARMの命令セット(v7)について調べていたので備忘録のためメモを残しておきます。

■ARMv7のマニュアル

https://www.macs.hw.ac.uk/~hwloidl/Courses/F28HS/Docu/DDI0406C_C_arm_architecture_reference_manual.pdf

とりあえず命令セットのところを読んでみる。
参考用に簡単な加算と条件分岐を含むコードを書いてみる。

■サンプルコード hoge.c

int add(void)
{
    int a = 0;
    a += 0x10;
    if ( a == 0x10) {
      return a;
    }
    return 0;
}


$ arm-linux-gnueabi-gcc -g -c hoge.c
$ arm-linux-gnueabi-objdump -S hoge.o


■サンプルアセンブラコード

00000000 <add>:
int add(void)
{
   0:   e52db004        push    {fp}            ; (str fp, [sp, #-4]!)
   4:   e28db000        add     fp, sp, #0
   8:   e24dd00c        sub     sp, sp, #12
    int a = 0;
   c:   e3a03000        mov     r3, #0
  10:   e50b3008        str     r3, [fp, #-8]
    a += 0x10;
  14:   e51b3008        ldr     r3, [fp, #-8]
  18:   e2833010        add     r3, r3, #16
  1c:   e50b3008        str     r3, [fp, #-8]
    if ( a == 0x10) {
  20:   e51b3008        ldr     r3, [fp, #-8]
  24:   e3530010        cmp     r3, #16
  28:   1a000001        bne     34 <add+0x34>
      return a;
  2c:   e51b3008        ldr     r3, [fp, #-8]
  30:   ea000000        b       38 <add+0x38>
    }
    return 0;
  34:   e3a03000        mov     r3, #0
}
  38:   e1a00003        mov     r0, r3
  3c:   e24bd000        sub     sp, fp, #0
  40:   e49db004        pop     {fp}            ; (ldr fp, [sp], #4)
  44:   e12fff1e        bx      lr

条件指定について

ARMの命令のうち、最上位の4bitは条件指定のビット(cond)として割り当てられているそうです。

f:id:simotin13:20180606010019p:plain

アセンブラを見るとこのcondビットはほぼほぼ1111(0xE..)になっていることが多い。
ここまでは過去の経験として知っていたのですが、このcondビットの意味がいまいちよく分かっていませんでした。※そういえばpine64のダンプとか読んだときも少し調べていました。

mcommit.hatenadiary.com

結論ですが、ARMでは(ほとんどの)命令に対して実行するかしないかの条件をつけることができるそうです。これだけだとピンとこないのですが、条件と言うのは要するにフラグレジスタの各種フラグビット(ゼロフラグとかキャリーフラグ...etc)に応じて実行するかしないかを命令に対して指定できるという意味になります。

実際に、上記のhoge.oの逆アセンブルですとbneだとcondは1になっています。
条件指定実行ができるということは
addとかmov命令をフラグレジスタの値を元に実行するかしないか切り替えれるというような意味だと思います。*1

というわけで実際にどの命令で条件指定が可能なのか、適当に試してみたいと思います。

が、今日眠いので続きは明日にします。

[2018/06/06 追記]

条件実行について更に調べてみた続きです。

mcommit.hatenadiary.com

*1:なんかrubyの後置記法みたいでおしゃれ