久しぶりにVivado
特定顧客向けのCosmo-Zのカスタム開発をはじめるため、Vivadoを再び使う。
久しぶりすぎて慣れない。最新版を探したりするだけで時間が過ぎていく
陽電子を放出する18F の放射線源が手に入ったので、NaIシンチレータ+PMTをCosmo-Z (Mini) につないで測ってみることにしました。
まず最初の感想として、すげー!のひとこと。
いままで宇宙線ばかり見てきたけれど放射性同位元素はやっぱり違う。シンチレータを近づけると毎秒20万カウント。家の中に帰って来るだけでカウント数が跳ね上がる。宇宙線やコンクリからのバックグラウンドが毎秒80カウントくらいしか出ていなかったので、いままで見たことのないカウント数。イベントカウンタを32bitにしておいてよかった。
まずは、18Fのない状態。4時間程度測っています。見えているのは40Kのピークのみ。
次に、かなり遠い場所に18Fがある場合。1046秒測定。
新たに表れたのが511keVのピークでしょう。
ここまでの解析はオフライン解析だったので、Cosmo-ZのFPGAを入れ替えてリアルタイムに見えるようにしてみます。
まず、18Fの線源から1メートルくらい離したときの計測。計測時間は178秒。
次に直近まで近づけたとき。わずか25秒でこのカウント数。うひょ。
さて、よくみるとピークの位置がすこし右側にずれています。
これはなんだろうということで生の波形を見てみると、パイルアップが多く起きていました。
パイルアップが起きない程度に離さないとピークの位置がずれますね。
あと、1us程度の幅の信号を80MHzでサンプリングしているのでノイズによるガタガタが目立ちます。波高値のみを見ているのでMCAのピークの幅が増えるという精度が悪化の原因になっているはずです。
そういうわけでTiming Filter AmpとBaseLineRecovererをONにしてみました。
波形がつるつるになってノイズが減っているわけですが、積分回路に通してこの波形をつくっているわけなので時系列的に見れば全体を平均化した値からピーク値を推定していることになるのでしょう。
TFAとBLRをONにしてMCAで測ってみたスペクトラムです。
少しだけ半値幅が減ったような気がしますが、パイルアップは消せません。
Cosmo-Zの次の目標としては、カウントレートが高い場合にパイルアップを消すためのフィルタを作るということと、リアルタイムMCAの使い勝手を向上させることだと思います。
さて、だいぶん一通りデータが取れたので、夜9時ごろの我が家の放射線のスペクトルを見てみます。
511keVと1461keVのピークがはっきりわかります。NaIの較正ができそうですね。
最後に翌朝に家の中を取ったものです。18Fの半減期は100分程度なので、翌朝にはほとんど残っていませんでした。
1日でほとんどすべて消えるというのも素晴らしいですね。
TCP/IPのリモート接続を待つサーバプログラムの起動や、ZYNQのハードウェアの初期化を行うためのプログラムを、これまで/etc/rc.localに書いてきましたが、rc.localに書くのは現在のLinuxでは非推奨であるようです。
やりたいことは2つあって、1つ目はTCP/IPのサーバを起動すること。もう一つはLinuxの起動時に1回だけ初期化を行うことです。TCP/IPのサーバなんかは予期せず停止した場合に再起動してくれるとありがたいです。それぞれ、cszserverとcszinitというサービスを作ることにします。
こういったことをするのにsystemdとかsystemctlといった仕組みを使うのがモダンな方法なので、その方法を調べてみました。
最初にやることはスクリプトを作ることです。
まず、TCP/IPのサーバプログラムのcszserverという実行ファイルがあって、これを起動するためのスクリプトを/opt/cszserver.shに作ります。
#!/bin/bash
/usr/local/bin/cszserver 1069
次に、1回だけ起動するスクリプトを/opt/cszinit.shとして作ります。
#!/bin/bash
/bin/chmod 666 /dev/uio0
/bin/chmod 666 /dev/uio1
/bin/echo enabled > /sys/devices/soc0/amba/e0000000.serial/tty/ttyPS0/power/wakeup
/home/share/autorun
cszinit.shでは、UIO(ユーザIOドライバ)を、一般ユーザでも使えるように実行権限を設定しているのと、ZYNQの低消費電力モードからの復帰機能を有効にしていること、それから、/home/share/autorunというスクリプトを起動しています。
シェルスクリプトファイルを作ったら実行権限を設定します。
$ chmod 755 /opt/cszserver.sh chmod 755 /opt/cszinit.sh
次に、/etc/systemd/system/にサービスの設定ファイルを書きます。
まずは、繰り返し起動したいサービスの定義、/etc/systemd/system/cszserver.service を書きます。
[Unit]
Description = Cosmo-Z API relay server
[Service]
ExecStart = /opt/cszserver.sh
Restart = always
Type = simple
[Install]
WantedBy = multi-user.target
この意味ですが、ExecStartでは先ほど作った起動スクリプトを指定し、Restart=alwaysでは、停止したら自動再起動することを指定します。type=simpleやWantedByの設定は深く考えなくてよいでしょう。
次に1回だけ起動したサービスの定義、/etc/systemd/system/cszsinit.service を書きます。
[Unit]
Description = Cosmo-Z initialize
[Service]
ExecStart = /opt/cszinit.sh
Restart = no
Type = oneshot
[Install]
WantedBy = multi-user.target
ExecStartと、RestartとTypeのところが異なります。
こうして/etc/systemd/systemの下に.serviceファイルを作ったら、
# systemctl list-unit-files --type=service | grep csz
でcszが付くサービスの一覧を表示させてみます。
root@cszmini:~# systemctl list-unit-files --type=service | grep csz
cszinit.service disabled
cszserver.service disabled
設定ファイルを作っただけなのでdisabledとして表示されました。
これらのサービスを有効にするにはsystemctl enable cszserverとsystemctl enable cszinitを実行します。
systemctl enable <サービス名>を実行するとシンボリックリンクが作成されます。
root@cszmini:~# systemctl enable cszserver
Created symlink /etc/systemd/system/multi-user.target.wants/cszserver.service → /etc/systemd/system/cszserver.service.
root@cszmini:~# systemctl enable cszinit
Created symlink /etc/systemd/system/multi-user.target.wants/cszinit.service → /etc/systemd/system/cszinit.service.
そして、 systemctl start cszserver と systemctl start cszinit を実行すれば、これらのサービスが起動します。
rebootしても自動的に起動するようになります。
systemdに登録して嬉しいことは、システムにちゃんとサービスとして登録されているため、systemctl status で表示されて「しっかりした製品だなという印象」を与えられること、
それから、サービスが予期せず停止してもすぐに再起動してくれることです。ためしにkill -9 でシグナルを送ってみるとすぐに再起動してくれます。
この自動再起動という特徴は、ハードウェアの監視や、リソースの管理プログラムを作る場合にはかなり重要です。
まとめると、
ということです。.serviceファイルの中の書き方次第で、強制再起動にもワンショットにもすることができます。
組み込みLinuxのマシンの名前を「ホスト名.local」で解決しようとして1秒待たされる原因は、どうやらLinuxが返すMDNSのパケットの違いにあることがわかってきました。
ZYNQの組み込みLinuxに対するPINGを無線LANでやってみしたところ、なんと、この1秒待たされる現象が発生しないことがわかりました。次の図はPINGを打ったときのパケットの解析です。Windowsマシンは最初、IPv4とIPv6の双方で、IPv4アドレスとIPv6アドレスを調べようとして4個のqueryを出しています。
それに対してLinuxマシンは
という3つのresponseを返しています。
この3つ目のレスポンスが帰ると即座にPINGのパケットが出始めます。
一方、同じ組み込みLinuxのマシンでも有線LANでつないだ場合は、「IPv6パケットに乗せたIPv6アドレス」の応答が出てこないのです。
これゆえWindowsはgetaddrinfo()で1秒間のタイムアウトをするまで待たされるのだと思われます。
では、Linuxのavahi-daemonが有線LANでバグっているのかというと、そういうわけではなさそうです。Linux側でtcpdumpをしてみると、
01:20:38.768658 IP 192.168.1.4.mdns > 224.0.0.251.mdns: 0 A (QM)? cszmini.local. (31)
01:20:38.770990 IP cszmini.mdns > 224.0.0.251.mdns: 0*- [0q] 1/0/0 (Cache flush) A 192.168.1.12 (41)
01:20:38.771974 IP6 fe80::***************.mdns > ff02::fb.mdns: 0 A (QM)? cszmini.local. (31)
01:20:38.774358 IP 192.168.1.4.mdns > 224.0.0.251.mdns: 0 AAAA (QM)? cszmini.local. (31)
01:20:38.776324 IP6 fe80::***************.mdns > ff02::fb.mdns: 0 AAAA (QM)? cszmini.local. (31)
01:20:38.777309 IP cszmini.mdns > 224.0.0.251.mdns: 0*- [0q] 1/0/0 (Cache flush) AAAA fe80::11:10ff:fe00:2201 (53)
01:20:38.789419 IP 192.168.1.4.mdns > 224.0.0.251.mdns: 0 A (QM)? cszmini.local. (31)
01:20:38.792155 IP6 fe80::***************.mdns > ff02::fb.mdns: 0 A (QM)? cszmini.local. (31)
01:20:38.794569 IP 192.168.1.4.mdns > 224.0.0.251.mdns: 0 AAAA (QM)? cszmini.local. (31)
01:20:38.796814 IP6 fe80::***************.mdns > ff02::fb.mdns: 0 AAAA (QM)? cszmini.local. (31)
01:20:39.803426 IP 192.168.1.4.mdns > 224.0.0.251.mdns: 0 A (QM)? cszmini.local. (31)
01:20:39.805998 IP cszmini.mdns > 224.0.0.251.mdns: 0*- [0q] 1/0/0 (Cache flush) A 192.168.1.12 (41)
01:20:39.809007 IP6 fe80::***************.mdns > ff02::fb.mdns: 0 A (QM)? cszmini.local. (31)
01:20:39.811748 IP 192.168.1.4.mdns > 224.0.0.251.mdns: 0 AAAA (QM)? cszmini.local. (31)
01:20:39.813931 IP6 fe80::***************.mdns > ff02::fb.mdns: 0 AAAA (QM)? cszmini.local. (31)
01:20:39.816796 IP 192.168.1.4 > cszmini: ICMP echo request, id 1, seq 16515, length 40
01:20:39.818957 IP cszmini.mdns > 224.0.0.251.mdns: 0*- [0q] 1/0/0 (Cache flush) AAAA fe80::11:10ff:fe00:2201 (53)
01:20:39.821750 IP cszmini > 192.168.1.4: ICMP echo reply, id 1, seq 16515, length 40
と、IP6でcszminiがソースになっているものがありません。
一方、無線LANのほうもtcpdumpしてみると、
01:23:44.921998 IP 192.168.1.4.mdns > 224.0.0.251.mdns: 0 A (QM)? cszmini.local. (31)
01:23:44.924618 IP cszmini.mdns > 224.0.0.251.mdns: 0*- [0q] 1/0/0 (Cache flush) A 192.168.1.10 (41)
01:23:44.927949 IP6 fe80::***************.mdns > ff02::fb.mdns: 0 A (QM)? cszmini.local. (31)
01:23:44.930494 IP 192.168.1.4.mdns > 224.0.0.251.mdns: 0 AAAA (QM)? cszmini.local. (31)
01:23:44.933371 IP6 fe80::***************.mdns > ff02::fb.mdns: 0 AAAA (QM)? cszmini.local. (31)
01:23:44.934356 IP cszmini.mdns > 224.0.0.251.mdns: 0*- [0q] 2/0/0 (Cache flush) AAAA 240b:10:ae21:3f00*****, (Cache flush) AAAA 240b:10:ae21:3f00:***** (81)
01:23:44.936394 IP 192.168.1.4.mdns > 224.0.0.251.mdns: 0 A (QM)? cszmini.local. (31)
01:23:44.937727 IP6 cszmini.mdns > ff02::fb.mdns: 0*- [0q] 2/0/0 (Cache flush) AAAA 240b:10:ae21:3f00*****, (Cache flush) AAAA 240b:10:ae21:3f00:***** (81)
01:23:44.940091 IP6 fe80::***************.mdns > ff02::fb.mdns: 0 A (QM)? cszmini.local. (31)
01:23:44.942336 IP 192.168.1.4.mdns > 224.0.0.251.mdns: 0 AAAA (QM)? cszmini.local. (31)
01:23:44.944420 IP6 fe80::***************.mdns > ff02::fb.mdns: 0 AAAA (QM)? cszmini.local. (31)
01:23:44.946751 IP6 fe80::*************** > cszmini: ICMP6, neighbor solicitation, who has cszmini, length 32
01:23:44.949187 IP6 cszmini > fe80::***************: ICMP6, neighbor advertisement, tgt is cszmini, length 24
となっていて、IPv6のパケットでMDNSを返しているんですね。
ネットワークの問題でパケットがロスしているわけではなくて、有線LANでは本当にIPv6のパケットが出ていなかったのです。
マジか?と思ってip aしてみると、
root@cszmini:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1
link/sit 0.0.0.0 brd 0.0.0.0
3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 02:11:10:00:22:01 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.12/24 brd 192.168.1.255 scope global dynamic eth0
valid_lft 3069021764sec preferred_lft 3069021764sec
inet6 fe80::11:10ff:fe00:2201/64 scope link
valid_lft forever preferred_lft forever
特に問題はなさそうなのですが、MACアドレスがプライベートMACアドレスなのが気になります。そのせいかIPv6のアドレスにはリンクローカルなアドレスしかありません。試しに02:11:10:00:22:01という設定を00:11:10:00:22:01に変更してみたところ、リンクローカルではないIPv6アドレスが生成されるようになって、
root@cszmini:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1
link/sit 0.0.0.0 brd 0.0.0.0
3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 10:11:10:00:22:01 brd ff:ff:ff:ff:ff:ff
inet 192.168.1.14/24 brd 192.168.1.255 scope global dynamic eth0
valid_lft 3068829252sec preferred_lft 3068829252sec
inet6 240b:10:ae21:3f00:1211:10ff:fe00:2201/64 scope global dynamic mngtmpaddr noprefixroute
valid_lft 3068829252sec preferred_lft 3068829252sec
inet6 fe80::1211:10ff:fe00:2201/64 scope link
valid_lft forever preferred_lft forever
PING -6 cszmini.localとやってみたところ、IPv6でのMDNSもちゃんと返していることがわかりました。
02:43:58.853565 IP 192.168.1.4.mdns > 224.0.0.251.mdns: 0 AAAA (QM)? cszmini.local. (31) // 1度目のQUERY
02:43:58.855804 IP6 fe80::***************.mdns > ff02::fb.mdns: 0 AAAA (QM)? cszmini.local. (31)
02:43:58.855992 IP cszmini.mdns > 224.0.0.251.mdns: 0*- [0q] 1/0/0 (Cache flush) AAAA 240b:10:ae21:3f00:1211:10ff:fe00:2201 (53)
02:43:58.859651 IP6 cszmini.mdns > ff02::fb.mdns: 0*- [0q] 1/0/0 (Cache flush) AAAA 240b:10:ae21:3f00:1211:10ff:fe00:2201 (53)
02:43:58.888202 IP 192.168.1.4.mdns > 224.0.0.251.mdns: 0 AAAA (QM)? cszmini.local. (31) // 2度目のQUERY
02:43:58.890554 IP6 fe80::***************.mdns > ff02::fb.mdns: 0 AAAA (QM)? cszmini.local. (31)
02:43:59.893692 IP 192.168.1.4.mdns > 224.0.0.251.mdns: 0 AAAA (QM)? cszmini.local. (31) // 3度目のQUERY
02:43:59.896185 IP6 fe80::***************.mdns > ff02::fb.mdns: 0 AAAA (QM)? cszmini.local. (31)
02:43:59.898695 IP6 cszmini.mdns > ff02::fb.mdns: 0*- [0q] 1/0/0 (Cache flush) AAAA 240b:10:ae21:3f00:1211:10ff:fe00:2201 (53)
02:43:59.901480 IP6 240b:10:ae21:3f00:*************** > cszmini: ICMP6, echo request, seq 178, length 40
02:43:59.904150 IP6 cszmini > 240b:10:ae21:3f00:***************: ICMP6, echo reply, seq 178, length 40
02:43:59.907243 IP cszmini.mdns > 224.0.0.251.mdns: 0*- [0q] 1/0/0 (Cache flush) AAAA 240b:10:ae21:3f00:1211:10ff:fe00:2201 (53)
02:44:00.908804 IP6 240b:10:ae21:3f00:*************** > cszmini: ICMP6, echo request, seq 179, length 40
02:44:00.910919 IP6 cszmini > 240b:10:ae21:3f00:***************: ICMP6, echo reply, seq 179, length 40
ただし、これを直してもWindowsのgetaddrinfo()が1秒待つ動作は治りませんでした。上のパケットキャプチャを見てもQUERYを3回も発行しています。IPv6のMDNSが返らないからというわけではなさそうです。まだ何か見落としているところがあるのでしょう。
もしかすると、あまりにも早く返ってきたMDNSのレスポンスは見れないのかもしれません。
そしてMACアドレスがプライベートアドレスならIPv6のMDNSを返さないというavahi-daemonの動作も十分に怪しいのですが、Windowsのgetaddrinfo()はNETBIOSやIPv4のMDNSなど他の手段でアドレスが得られているのに、わざわざIPv6のMDNSレスポンスが得られるまで1秒待つ動作も相当にムダで謎であると思えます。
結局のところ根本原因はわかりませんでしたが、TCP/IPのプログラムを組む時にはIPv4しか使わないと決めているなら、getaddrinfo()の際にAF_INETを指定してIPV4に限定してしまったほうがよいでしょう。
昨日の実験の結果では、WindowsのTCP/IPプログラミングではローカルネットワーク上のホスト名を名前解決をしようとしたときに、getaddrinfo()で1秒待たされてしまっていました。
長い間の試行錯誤の末、この「名前解決で1秒待たされる動作」を解決する方法がわかりました。
普通にgetaddrinfoを使ったプログラムは、
struct addrinfo hints, * res;
memset(&hints, 0, sizeof(hints));
int err = getaddrinfo(host, NULL , &hints, &res);
のようになります。
getaddrinfo()にはこれといってオプションがないように見えるのですが、よく見たらhintsという引数があって、どんな体系のアドレスで解決するかのヒントを与えることができるようです。
これには全部ゼロである構造体を入れてもよいのですが、hints.ai_family = AF_INET;と指定すると、1秒待たされる動作をしなくなることがわかりました。
struct addrinfo hints, * res;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
int err = getaddrinfo(host, NULL , &hints, &res);
これは、AF_INET、つまりIPv4の範囲内で名前解決をしなさいとヒントを与えているわけです。
デバッガで実行してみると
29ミリ秒でgetaddrinfoが終了しています。
このような指定をしてもMDNSのパケットはIPv4とIPv6の両方で出るのですが、AAAAレコードの問い合わせをしなくなります。
逆に、AF_INET6だけを指定するとAAAAレコードでの問い合わせだけが出るようになって1秒待たされます。
実はわざわざプログラムを組まなくてもPINGコマンドで同様の実験ができます。
PING -6 ホスト名
とやると、AAAAレコードを使ったIPv6アドレスの解決のみを行い、
PING -4 ホスト名
とやると、Aレコードを使ったIPv6アドレスの解決のみを行います。
つまり、Windowsは、ZYNQ Linuxのavahi-daemonが返したMDNSのレスポンスの何かが気に入らなくて1秒間タイムアウトしていたと考えられます。
根本的な解決はまだですが、getaddrinfoの前にIPv4で解決する
hints.ai_family = AF_INET;
を付けるだけで、getaddrinfo()で待たされる時間が大幅に減り、アプリの起動時間が短縮できました。
ZYNQで組み込みシステムを作って、Windowsで作ったプログラムからネットワーク経由でコントロールしたい場合に、アプリを作ることになりますがが、ホスト名の名前解決で1秒くらい待たされる現象が起きていました。
状況としては、
です。
こういう環境で、Windowsのプログラムから組み込みLinuxをネットワーク越しでコントロールしようとすると、プログラムの起動時に名前解決で1秒ほど固まってしまうのです。
TCP/IPのプログラムを作る際に、ホスト名をIPアドレスに変換する名前解決にはgetaddrinfo()を使いますが、Visual Studioのデバッガでステップ実行してみるとgetaddrinfo()で1秒ほど待たされていることがわかりました。
このgetaddrinfo()は、"cszmini"みたいなホスト名から192.168.1.12のようなIPアドレスに変換してくれます。
getaddrinfo()はプログラムの起動時に必ず実行するので、1秒間ひっかかるような動作をしてしまいます。
自作プログラムの固まる箇所はgetaddrinfo()にあることがわかったのですが、どうやら普通にpingを打つだけでも同様に最初に1秒間固まることがわかってきました。
しかしながらpingで固まるのは最初のICMPの送信が始まる前なので、PINGの遅延時間に表示されません。
おそらくPINGというシステムツールも、中ではgetaddrinfo()を呼び出しているのでしょう。getaddrinfo()のほかにGetAddrInfoEx()というAPI関数もあるのですが、結果は同じでした。要するにWindowsの名前解決では、ローカルネットワークで隣にある機械の名前解決に必ず1秒待たされるのです。
MS-DOSプロンプトを開いてping cszminiと打って、Wiresharkでパケットをキャプチャして調べてみると、DNS、mDNS、NETBIOSと3つの方法で調べていることがわかります。
上のパケットで何が起きているかを説明しますと、
という感じでした。PINGを起動したときから名前解決が走っているのですが、このプロセスが終了するまでに1.03秒かかっていることがわかります。しかもMDNSではすぐにAレコードとAAAAレコードを返している(17番と19番)のですが、Windowsはこれを無視してしまっています。
上の過程では、デフォルトゲートウェイに向けてDNSを送っていてわかりにくいので、DNSとPINGに絞ってみます。
PING開始時にMDNSを使って「cszmini.local」がいないかどうかを調べているのですが、1.37秒付近で名前解決を開始して、実際にPINGが飛んでいるのは2.39秒なので、名前解決で1.02秒待っていることがわかります。おそらく、MDNSのタイムアウトが1秒になっているのでしょう。
このようにターゲット機器がMDNSでホスト名を返しているし、NetBIOSでもホスト名を返しているのですが、Windowsのgetaddrinfo()は1秒ほど待つようになっていて、極めて体感速度が遅く感じられてしまうのです。
側面の右下を座標ゼロにするとよい。
フロントパネルの加工は、タカチのカスタムと、いつもお願いしている金属加工業者の2か所に出す。
タカチはインクジェット印刷
金属加工業者はシルク印刷
どちらが早いか。
最近のコメント