目次: 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)
似ているどころか完全に一致していました。道理で同じ動作になるわけですね。
この記事にコメントする
目次: 自宅サーバー
このブログ、フォント設定が2つほどおかしかったので直しました。今まで見づらくてすみません。
1つ目は一部の環境(Linux向けChromeなど)からこのサイトを見るとサンセリフ体(ゴシック体)ではなくセリフ体(明朝体)で表示される現象を直しました。セリフ体になってしまう現象は以前から認識していて不思議だな?とは思ったものの、原因が良くわからず放置していました。
Developer toolsでCSSを確認していたら、font-family: sans-serif, serifと指定していた箇所がありました。原因も何も、自分で指定してただけだったのか……。こんな単純なことに10年以上気づいていなかった。
2つ目はmonospace系の文字が表示されるpreタグなどの文字が小さかったのを直しました。今までfont-size: smallerにしていたんですが、monospaceは元々少し小さいサイズで表示されるブラウザが多くて、めちゃくちゃ字が小さくなっていました。
しかもこちらの現象は認識していませんでした。自分のFirefox環境はmonospaceのフォントサイズを少し大き目に指定している&そのことを忘れており、デフォルト設定のブラウザで見ると字が小さすぎることに気付いてなかったです。すまねぇ。
この記事にコメントする
目次: C言語とlibc
シグナルマスク(sigprocmaskのマニュアル)の「規定されていない」使い方をするとどうなるか?の続きです。5つのマルチスレッド(親スレッド+4つの子スレッド)で全員でsigwait()するのは共通で、誰がsigprocmask()を呼ぶか?を変えながら、下記4パターンを試します。
今回は結果3と4を紹介します。1〜3は定義されていない動作ですが、4は比較用に実施する「正しい方法」です。
全スレッドがsigprocmask()した場合です。ゆっくり5回シグナルを送ると、親スレッド(th 4)のsigwait()がシグナルを受け取り、子スレッド(th 0)のsigwait()はEINTRが返ります。
$ g++ -Wall -g -O2 -DUSE_SIGPROCMASK_ALL -DID_MAINTHREAD=1 signal_thread.cpp && ./a.out Use sigprocmask th 0: sub (child ) thread start th 0: sigprocmask(block) th 1: main (child ) thread start th 1: sigprocmask(block) th 2: sub (child ) thread start th 2: sigprocmask(block) th 4: sub (parent) thread start th 4: sigprocmask(block) th 3: sub (child ) thread start th 3: sigprocmask(block) th 3: loop start th 0: loop start th 2: loop start th 1: loop start th 4: loop start th 4: got SIGUSR1 th 0: sigwait failed (Interrupted system call) th 4: got SIGUSR1 th 0: sigwait failed (Interrupted system call) th 4: got SIGUSR1 th 0: sigwait failed (Interrupted system call) th 4: got SIGUSR1 th 0: sigwait failed (Interrupted system call) th 4: got SIGUSR1 th 0: sigwait failed (Interrupted system call)
良い感じです。また結果1(親スレッドがsigprocmask())と異なり、大量にシグナルを送りつけてもabortしないのも良いです。
$ g++ -Wall -g -O2 -DUSE_SIGPROCMASK_ALL -DID_MAINTHREAD=1 signal_thread.cpp && ./a.out Use sigprocmask th 0: sub (child ) thread start th 0: sigprocmask(block) th 1: main (child ) thread start th 1: sigprocmask(block) th 2: sub (child ) thread start th 2: sigprocmask(block) th 4: sub (parent) thread start th 4: sigprocmask(block) th 3: sub (child ) thread start th 3: sigprocmask(block) th 3: loop start th 0: loop start th 2: loop start th 1: loop start th 4: loop start (...略...) th 2: sigwait failed (Interrupted system call) th 0: sigwait failed (Interrupted system call) th 0: got SIGUSR1 th 1: got SIGUSR1 th 2: got SIGUSR1 th 2: got SIGUSR1 th 1: sigwait failed (Interrupted system call) th 0: got SIGUSR1 th 4: sigwait failed (Interrupted system call) th 3: sigwait failed (Interrupted system call) th 2: sigwait failed (Interrupted system call) th 1: got SIGUSR1 (別ターミナルから) $ while :; do kill -USR1 123450; if [ $? -ne 0 ]; then break; fi; done
良さそうですね。
正しい方法(全スレッドがpthread_sigmask())はどんな動きでしょうか?結果だけ先に書いてしまうと、全スレッドがsigprocmask()したときと同じ動きをするようです。
$ g++ -Wall -g -O2 -DUSE_PTHREADSIGMASK -DID_MAINTHREAD=1 signal_thread.cpp && ./a.out Use pthread_sigmask th 0: sub (child ) thread start th 0: pthread_sigmask(block) th 1: main (child ) thread start th 1: pthread_sigmask(block) th 2: sub (child ) thread start th 2: pthread_sigmask(block) th 4: sub (parent) thread start th 4: pthread_sigmask(block) th 3: sub (child ) thread start th 3: pthread_sigmask(block) th 3: loop start th 2: loop start th 0: loop start th 1: loop start th 4: loop start th 4: got SIGUSR1 th 0: sigwait failed (Interrupted system call) th 4: got SIGUSR1 th 0: sigwait failed (Interrupted system call) th 4: got SIGUSR1 th 0: sigwait failed (Interrupted system call) th 4: got SIGUSR1 th 0: sigwait failed (Interrupted system call) th 4: got SIGUSR1 th 0: sigwait failed (Interrupted system call)
結果3(全員sigprocmask())と同じ動きをしています。当然ながら、大量にシグナルを送りつけてもabortしません。
$ g++ -Wall -g -O2 -DUSE_PTHREADSIGMASK -DID_MAINTHREAD=1 signal_thread.cpp && ./a.out Use pthread_sigmask th 0: sub (child ) thread start th 0: pthread_sigmask(block) th 1: main (child ) thread start th 1: pthread_sigmask(block) th 2: sub (child ) thread start th 2: pthread_sigmask(block) th 4: sub (parent) thread start th 4: pthread_sigmask(block) th 3: sub (child ) thread start th 3: pthread_sigmask(block) th 3: loop start th 2: loop start th 0: loop start th 1: loop start th 4: loop start (...略...) th 2: got SIGUSR1 th 0: sigwait failed (Interrupted system call) th 1: sigwait failed (Interrupted system call) th 4: sigwait failed (Interrupted system call) th 0: got SIGUSR1 th 4: got SIGUSR1 th 0: got SIGUSR1 th 3: sigwait failed (Interrupted system call) th 3: got SIGUSR1 th 2: sigwait failed (Interrupted system call) th 2: got SIGUSR1 th 0: sigwait failed (Interrupted system call) th 0: got SIGUSR1 th 1: sigwait failed (Interrupted system call) th 3: sigwait failed (Interrupted system call) th 3: got SIGUSR1 th 0: got SIGUSR1 th 4: sigwait failed (Interrupted system call) th 2: sigwait failed (Interrupted system call) th 1: sigwait failed (Interrupted system call) (別ターミナルから) $ while :; do kill -USR1 123450; if [ $? -ne 0 ]; then break; fi; done
4つの結果から推測するにpthread_sigmask()とsigprocmask()は同じシステムコールを使っているかもしれません。libcのソースコードを見ればわかるはずなので、また今度に見ようと思います。
こちらからどうぞ。
この記事にコメントする
目次: C言語とlibc
シグナルマスクのマニュアル(sigprocmaskのマニュアル)を見ると下記のように「規定されていない」とあります。実際Linuxだとどうなるか気になります。
マルチスレッドのプロセスでsigprocmask()を使用した場合の動作は規定されていない。
実行環境は下記のとおりです。他のプラットフォームや過去/将来のバージョンのLinuxで今回の実験結果と同じ動作をするとは限りませんのでご注意ください。
実験内容は5つのマルチスレッド(親スレッド+4つの子スレッド)で全員でsigwait()するのは共通、誰がsigprocmask()を呼ぶか?を変えながら、下記4パターンを試します。
最後のパターンはマルチスレッドでsigwait()する場合の正しい方法(全スレッドがpthread_sigmask())で、他の3つと動作を比較するためのものです。
コードは長くなってしまったので最後にファイルへのリンクを張っておきます。
実験方法はコンパイル時にマクロを適宜切り替えて、生成された./a.outを起動し、別のターミナルからkillコマンドなどでSIGUSR1を送るだけです。
最初のスレッドがsigprocmask()した場合です。ゆっくり5回シグナルを送ると、親スレッド(th 4)のsigwait()がシグナルを受け取り、子スレッド(th 0)のsigwait()はEINTRが返ってきます。
$ g++ -Wall -g -O2 -DUSE_SIGPROCMASK -DID_MAINTHREAD=4 signal_thread.cpp && ./a.out Use sigprocmask th 0: sub (child ) thread start th 1: sub (child ) thread start th 2: sub (child ) thread start th 4: main (parent) thread start th 4: sigprocmask(block) th 3: sub (child ) thread start th 3: loop start th 2: loop start th 4: loop start th 0: loop start th 1: loop start th 4: got SIGUSR1 th 0: sigwait failed (Interrupted system call) th 4: got SIGUSR1 th 0: sigwait failed (Interrupted system call) th 4: got SIGUSR1 th 0: sigwait failed (Interrupted system call) th 4: got SIGUSR1 th 0: sigwait failed (Interrupted system call) th 4: got SIGUSR1 th 0: sigwait failed (Interrupted system call)
一見すると良い感じに動くように見えますが、大量にシグナルを送りつけるとabortします。ありゃりゃ。
$ g++ -Wall -g -O2 -DUSE_SIGPROCMASK -DID_MAINTHREAD=4 signal_thread.cpp && ./a.out Use sigprocmask th 0: sub (child ) thread start th 1: sub (child ) thread start th 2: sub (child ) thread start th 4: main (parent) thread start th 4: sigprocmask(block) th 3: sub (child ) thread start th 3: loop start th 1: loop start th 0: loop start th 2: loop start th 4: loop start ユーザー定義シグナル1 (別ターミナルから) $ while :; do kill -USR1 123450; if [ $? -ne 0 ]; then break; fi; done
一見動くように見えて、だめなパターンですね。
最初のスレッド「以外」の1スレッドがsigprocmask()した場合です。1回シグナルを送っただけでabortしました。
$ g++ -Wall -g -O2 -DUSE_SIGPROCMASK -DID_MAINTHREAD=0 signal_thread.cpp && ./a.out Use sigprocmask th 0: main (child ) thread start th 0: sigprocmask(block) th 1: sub (child ) thread start th 2: sub (child ) thread start th 4: sub (parent) thread start th 3: sub (child ) thread start th 3: loop start th 0: loop start th 2: loop start th 1: loop start th 4: loop start ユーザー定義シグナル1
結果1の動きを見る限り納得の結果と言えるでしょう。シグナルは常に親スレッドにも飛んでいたので、シグナルをマスクしてない親スレッドはabortするのはそりゃそうだなと思います。
続きはまた今度やります。
こちらからどうぞ。
この記事にコメントする
目次: Arduino
M5Stamp C3 + Raspberry Piを組み合わせて作った的あてゲーム、秋葉原のTarget-1(お店のサイト)で壊れることなく1年ほど安定稼働しているようです。良かった良かった。しかし最近、故障ではないもののタイトル画面に戻ってしまう頻度が増えているそうです。
以前に日記で紹介したとおり、的あてゲームのシステムはM5Stamp C3をBluetooth LEデバイスにして、Linux PCもしくはRaspberry PiなどのLinux SBCと通信しています。Bluetoothの接続が切れるとタイトル画面に戻って再接続する実装にしていますので、不意にタイトル画面に戻る = Bluetoothの接続が切断されていることを意味します。
お店での運用を見ていると、ゲームリザルト画面で放置されることが多いです。今の実装ではリザルト画面でBluetoothの通信を一切行いません。あまりにも長い間通信しないとBluetooth接続が切れてしまう?のかもしれません。
とりあえず今回は小手先の対処として、リザルト画面でM5Stamp C3本体のLEDを点滅させる指令を送り続け、Bluetooth接続を維持するようにしました。家でテストしてみたところ1日以上放置しても接続を維持できています。
代償として消費電力が0.1Wくらい増えますが、何度もタイトル画面に戻されるよりはマシでしょう……。
先代のTSS(ターゲットシューティングシステム、作者さんの紹介サイト)は5年位稼働していたらしい(すごい!)ので、追いつくにはあと4年ですか、長いな〜……。
4年後を考えてみると、Linux側のマシンROCK 3Cはほぼ確実にEOL(End Of Life、生産終了、販売終了)だと思います。Linux側のシステムはHW依存は少ないし、SWも枯れたやつが多いので、そのとき販売されているお買い得なARM SBCボード(Raspberry Pi 6とか7とか?ROCK 3C後継のボードとか)への乗り換えは容易だと思います。
困るのはM5Stamp C3ですね。EOLになると別ボードへのSW移植とドッキングするためのボード再設計が必要でしょう。M5Stampは安くて良いんですけど、世代ごとに形がガンガン変わって互換性ゼロなのが良くない点ですね。作り直すとしたら、今のM5Stamp C3の2枚使い設計はダサいので、I/Oピン数の多いボード1枚に改めると思います。
この記事にコメントする
| < | 2025 | > | ||||
| << | < | 05 | > | >> | ||
| 日 | 月 | 火 | 水 | 木 | 金 | 土 |
| - | - | - | - | 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 | 31 |
25年10月15日
25年10月18日
22年5月5日
25年10月19日
23年4月11日
06年4月22日
25年10月17日
25年10月6日
25年10月13日
20年10月23日
25年10月12日
20年8月29日
19年1月13日
18年10月13日
18年9月3日
18年8月20日
18年7月23日
18年7月22日
18年10月14日
18年11月10日
wiki
Linux JM
Java API
2002年
2003年
2004年
2005年
2006年
2007年
2008年
2009年
2010年
2011年
2012年
2013年
2014年
2015年
2016年
2017年
2018年
2019年
2020年
2021年
2022年
2023年
2024年
2025年
過去日記について
アクセス統計
サーバ一覧
サイトの情報合計:
本日: