目次: Linux
WatchdogもしくはWatchdog Timerなどと呼ばれますが、システムがハングアップしたときに強制的に再起動する仕組みがあります(SW実装とHW実装があります)。Linuxも対応していますので、試しに使ってみます。
今回はROCK 3Cを使って実験したいと思います。ROCK 3Cの/dev以下を見ると/dev/watchdog0がありますから、HW Watchdogが存在していることがわかります。
$ ls /dev/watchdog* /dev/watchdog /dev/watchdog0
デバイスツリーのwatchdogのcompatibleを確認すると"snps,dw-wdt"でした。つまりsnps(= Synopsys社)のWatchdog IPを搭載しています。
# cat /proc/device-tree/watchdog@fe600000/compatible snps,dw-wdt
頭についているdw-はDesignWareの略でしょう。DesignWareはSynopsysが販売/提供しているIPのブランド名です。
準備は簡単でaptなどでwatchdogパッケージをインストールするだけです。環境はDebian GNU/Linux 11 (bullseye)です。
# apt-get install watchdog
インストールしたら設定ファイル/etc/watchdog.confの最後の行にwatchdog-device設定を付け加えて、restartします。
# Check for a running process/daemon by its PID file. For example,
# check if rsyslogd is still running by enabling the following line:
#pidfile = /var/run/rsyslogd.pid
watchdog-device = /dev/watchdog0
# apt-get restart watchdog
動作確認として、ソフトウェアの動作を完全に停止させて、Watchdogがハードウェア的にリセットを掛けてくれる様子を見ます。ソフトウェアを完全に止める簡単な方法は、Sysrqにcを書き込んでわざとLinuxカーネルをクラッシュさせることでしょう。他の方法でも構いません。
echo c > /proc/sysrq-trigger (89秒後に再起動されるはず)
再起動されない場合は設定ファイルの書き方が間違っていると思われます。ありがちな間違いとしては、
もし変な設定になっていたときはsystemctl status watchdogを見ると、エラーが出ているはずです。
#### 設定名を間違えたとき(alive=にデバイスファイル名が出ない) rock-3c watchdog[1398]: alive=[none] heartbeat=[none] to=root no_act=no force=no #### デバイスファイル名を間違えたとき rock-3c watchdog[1376]: cannot open /dev/watchdogeee (errno = 2 = 'No such file or directory')
正しく設定できているとalive=の部分にデバイスファイル名が出ます。
rock-3c watchdog[1419]: alive=/dev/watchdog0 heartbeat=[none] to=root no_act=no force=no rock-3c watchdog[1419]: watchdog now set to 89 seconds rock-3c watchdog[1419]: hardware watchdog identity: Synopsys DesignWare Watchdog
どのWatchdogデバイスでも必ずそうなるのかはわからないですが、私の環境だと再起動までの秒数と、ハードウェア名(Synopsys DesignWare Watchdog)も出ていました。
Newメガネを注文しました。今までどんなメガネかけてたっけ?と気になったので日記から掘り起こしてみました。古いものはいつ買ったのかすら怪しく、詳細も書き残していませんでした。メガネに関心がないことが良くわかりますね……。
フォーナインズ(999.9)の前にもメガネは持っていたのですが、記録がなくて何もわかりません。パリミキに行って購入記録を聞いてみたら上記のような感じでした。20年前でも残ってるんですね。
目次: 射的
JTSA Unlimitedの大会に参加しました。
「木」ステージが大失敗、「水」もダメダメで、高速ステージの「土」もいまいち振るわず、結果は75.69秒(前回は76.75秒)でした(総合82位/113人、LM 12位/21人)。70秒台前半くらいを出したかったけど無念ですね。
練習会の記録を見ると自己ベストは65.81秒ですが、その後はほとんど60秒台が出ないところをみると、75〜70秒くらいが実力と思われます。最近はあまり上達している感じがしません。そろそろ週1練習の限界かもしれませんが、今後もゆるゆると続けていきます。
金曜日、メガネの鼻当てが曲がってんなー?と思って、元の位置に戻そうと指で押したらパキっと音がして折れました。なにーー!?メガネの固定が甘くなって激しい動き、例えば走ったり階段の上り下りをすると、メガネが上下に揺れて視界がグワングワンします、目が回ります……。
直近で一番困ったのは、明日のシューティング大会JTSA Unlimitedです。サブのメガネもないし、メガネ外したら的が見えないですから、壊れたメガネのまま参加するしかありません。不幸中の幸いか、JTSA Unlimitedはアクションなし=その場から動かないで撃つタイプなのでメガネが揺れてどうこうなることはありません。
今日のシューティング練習会で試した限りでも、視界に大きな影響はなかったですし壊れたメガネで参加してきます。
目次: C言語とlibc
Cライブラリのデバッグをしたいときはあまりないと思いますが、Cライブラリにデバッグ情報やprintを追加して実行する方法を紹介します。今回使用するのはDebian Testingが採用しているglibc-2.40です。
システムにインストールされているバージョンと同じにしないと動作しないと思います。システムにインストールされているCライブラリの確認方法、ビルド方法は前回の日記(2025年5月22日の日記参照)と同様です。ソースコードは~/work/の下にあるとします。
Cライブラリをビルドしたら、テストプログラムをビルドします。スタティックリンクのときとは異なり-Lオプションは指定しません。ビルドは通常と一緒で、実行時にライブラリを差し替えるからです。-Dオプションには先程改変したpthread_sigmask()を呼ぶためのコンパイルスイッチを指定します。
$ gcc -g -O2 -Wall -DUSE_PTHREADSIGMASK test.c
実行すると追加したhogeが出力されるはずです。
$ LD_PRELOAD=~/work/glibc/__build/libc.so ./a.out Use pthread_sigmask th 0: sub (child ) thread start th 0: pthread_sigmask(block) th 1: sub (child ) thread start th 1: pthread_sigmask(block) hoge th 2: sub (child ) thread start th 2: pthread_sigmask(block) hoge hoge th 3: sub (child ) thread start th 3: pthread_sigmask(block) th 4: main (parent) thread start th 4: pthread_sigmask(block) hoge hoge th 3: loop start th 4: loop start th 2: loop start th 0: loop start th 1: loop start
デバッグしてみましょう。
$ gdb ./a.out (gdb) set env LD_PRELOAD ~/work/glibc/__build/libc.so (gdb) b pthread_sigmask (gdb) run Thread 2 "a.out" hit Breakpoint 2, __GI___pthread_sigmask (how=0, newmask=0x7ffff7dd2da0, oldmask=0x7ffff7dd2e20) at pthread_sigmask.c:29 29 printf("hoge\n"); (gdb) bt #0 __GI___pthread_sigmask (how=0, newmask=0x7ffff7dd2da0, oldmask=0x7ffff7dd2e20) at pthread_sigmask.c:29 #1 0x00005555555555e3 in thread_main (arg=0x7fffffffc880) at signal_thread.cpp:81 #2 0x00007ffff7e63332 in start_thread (arg=<optimized out>) at pthread_create.c:447 #3 0x00007ffff7edede8 in __GI___clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:78
環境変数を設定して実行すると、ソースコードが表示されること、先程追加したprintf()が見えることも確認できました。ダイナミックリンクもうまくいっていそうです。
目次: C言語とlibc
Cライブラリのデバッグをしたいときはあまりないと思いますが、Cライブラリにデバッグ情報やprintを追加して実行する方法を紹介します。今回使用するのはDebian Testingが採用しているglibc-2.40です。
バージョンはよほど古いバージョンでなければ動くと思いますが、最初はシステムにインストールされているバージョンと同じにしたほうがトラブルが少ないと思います。システムにインストールされているCライブラリの確認方法はディストリビューションによって違いますが、DebianやUbuntuならば下記のように確認できます。
$ dpkg -l libc6:amd64 Desired=Unknown/Install/Remove/Purge/Hold | Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend |/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad) ||/ Name Version Architecture Description +++-===============-============-============-================================= ii libc6:amd64 2.40-7 amd64 GNU C Library: Shared libraries
バージョンは2.40でした。次にglibc-2.40のソースコードをもってきてビルドします。ソースコードは~/work/の下にあるとします。
$ git clone git://sourceware.org/git/glibc.git $ cd glibc $ git checkout glibc-2.40 $ mkdir build $ cd build $ ../configure --disable-sanity-checks --enable-debug $ make -j16
実行したときに自分でビルドしたライブラリかどうか簡単にわかるようにpthread_sigmask()にprintf()を追加してからビルドします。
$ git diff
diff --git a/nptl/pthread_sigmask.c b/nptl/pthread_sigmask.c
index a39f3ca335..4c725592b6 100644
--- a/nptl/pthread_sigmask.c
+++ b/nptl/pthread_sigmask.c
@@ -19,12 +19,14 @@
#include <pthreadP.h>
#include <sysdep.h>
#include <shlib-compat.h>
+#include <stdio.h>
int
__pthread_sigmask (int how, const sigset_t *newmask, sigset_t *oldmask)
{
sigset_t local_newmask;
+ printf("hoge\n");
/* The only thing we have to make sure here is that SIGCANCEL and
SIGSETXID is not blocked. */
if (newmask != NULL
ビルドしたglibcとテストプログラムをスタティックリンクします。-Lオプションには先程glibcをビルドしたディレクトリを指定し、-Dオプションには先程改変したpthread_sigmask()を呼ぶためのコンパイルスイッチを指定します。テストプログラムは以前紹介したものです(2025年5月12日の日記参照)。
$ g++ -g -O2 -Wall -static -L ~/work/glibc/build -DUSE_PTHREADSIGMASK 20250512_signal_thread.cpp
実行すると追加したhogeが出力されるはずです。
$ ./a.out Use pthread_sigmask th 0: sub (child ) thread start th 0: pthread_sigmask(block) hoge th 1: sub (child ) thread start th 1: pthread_sigmask(block) hoge th 2: sub (child ) thread start th 2: pthread_sigmask(block) hoge th 4: main (parent) thread start th 4: pthread_sigmask(block) th 3: sub (child ) thread start th 3: pthread_sigmask(block) hoge hoge th 4: loop start th 1: loop start th 3: loop start th 2: loop start th 0: loop start
最後にデバッグしてソースコードが表示されるか試しましょう。
$ gdb a.out (gdb) b pthread_sigmask (gdb) run Thread 2 "a.out" hit Breakpoint 1, __pthread_sigmask (how=0, newmask=0x7ffff7ff7120, oldmask=0x7ffff7ff71a0) at pthread_sigmask.c:29 29 printf("hoge\n"); (gdb) bt #0 __pthread_sigmask (how=0, newmask=0x7ffff7ff7120, oldmask=0x7ffff7ff71a0) at pthread_sigmask.c:29 #1 0x0000000000401c43 in thread_main (arg=0x7fffffffc900) at signal_thread.cpp:81 #2 0x00000000004109cf in start_thread (arg=<optimized out>) at pthread_create.c:447 #3 0x0000000000422968 in __clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:78
ソースコードが表示されること、先程追加したprintf()が見えることも確認できました。良さそうです。
目次: C言語とlibc
以前、シグナルマスク(sigprocmaskのマニュアル)の「規定されていない」使い方をするとどうなるか?を見ました。前回までに確認したことは、5つのマルチスレッド(親スレッド+4つの子スレッド)で全員でsigwait()するのは共通で、誰がsigprocmask()を呼ぶか?を変えながら、下記4パターンを試しました。
結果は1と2はabortし、4は「正しい方法」なので正しく動くとして、なぜか3も正しく動いていました。予想としてはsigprocmask()とpthread_sigmask()は実装が似ていて偶然こうなるのでしょう。
今回はGNU libc(glibc-2.41)のソースコードを確認して予想の答え合わせをします。繰り返しますが、マルチスレッドでsigprocmask()を呼ぶのは未定義動作なので3番の方法が正しく動く保証はないし、将来実装が変わって動かなくなる可能性があります。
まずpthread_sigmask()のコードを見ます。pthreadの制御に使うシグナルのマスクを消して、rt_sigprocmaskシステムコールを呼ぶだけです。
// glibc/nptl/pthread_sigmask.c
int
__pthread_sigmask (int how, const sigset_t *newmask, sigset_t *oldmask)
{
sigset_t local_newmask;
/* The only thing we have to make sure here is that SIGCANCEL and
SIGSETXID is not blocked. */
if (newmask != NULL
&& (__glibc_unlikely (__sigismember (newmask, SIGCANCEL))
|| __glibc_unlikely (__sigismember (newmask, SIGSETXID))))
{
local_newmask = *newmask;
clear_internal_signals (&local_newmask);
newmask = &local_newmask;
}
/* We know that realtime signals are available if NPTL is used. */
int result = INTERNAL_SYSCALL_CALL (rt_sigprocmask, how, newmask,
oldmask, __NSIG_BYTES);
return (INTERNAL_SYSCALL_ERROR_P (result)
? INTERNAL_SYSCALL_ERRNO (result)
: 0);
}
libc_hidden_def (__pthread_sigmask)
versioned_symbol (libc, __pthread_sigmask, pthread_sigmask, GLIBC_2_32);
次にsigprocmask()のコードを見ます。見ての通りpthread_sigmask()を呼び出しているだけです。
// glibc/sysdeps/unix/sysv/linux/sigprocmask.c
/* Get and/or change the set of blocked signals. */
int
__sigprocmask (int how, const sigset_t *set, sigset_t *oset)
{
int result = __pthread_sigmask (how, set, oset);
if (result == 0)
return 0;
__set_errno (result);
return -1;
}
libc_hidden_def (__sigprocmask)
weak_alias (__sigprocmask, sigprocmask)
似ているどころか完全に一致していました。道理で同じ動作になるわけですね。
< | 2025 | > | ||||
<< | < | 06 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | - | - | - | - | - |
合計:
本日: