コグノスケ


link 未来から過去へ表示(*)  link 過去から未来へ表示

link もっと前
2025年5月20日 >>> 2025年5月7日
link もっと後

2025年5月20日

glibcのsigprocmask()とpthread_sigmask()の実装

目次: C言語とlibc

以前、シグナルマスク(sigprocmaskのマニュアル)の「規定されていない」使い方をするとどうなるか?を見ました。前回までに確認したことは、5つのマルチスレッド(親スレッド+4つの子スレッド)で全員でsigwait()するのは共通で、誰がsigprocmask()を呼ぶか?を変えながら、下記4パターンを試しました。

  • 最初のスレッドがsigprocmask()
  • 最初のスレッド「以外」の1スレッドがsigprocmask()
  • 全スレッドがsigprocmask()
  • 全スレッドがpthread_sigmask()

結果は1と2はabortし、4は「正しい方法」なので正しく動くとして、なぜか3も正しく動いていました。予想としてはsigprocmask()とpthread_sigmask()は実装が似ていて偶然こうなるのでしょう。

やっと本題

今回はGNU libc(glibc-2.41)のソースコードを確認して予想の答え合わせをします。繰り返しますが、マルチスレッドでsigprocmask()を呼ぶのは未定義動作なので3番の方法が正しく動く保証はないし、将来実装が変わって動かなくなる可能性があります。

まずpthread_sigmask()のコードを見ます。pthreadの制御に使うシグナルのマスクを消して、rt_sigprocmaskシステムコールを呼ぶだけです。

glibcのpthread_sigmask()の実装

// 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のsigprocmask()の実装

// 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/08 11:14)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2025年5月16日

フォントがおかしいので直した

目次: 自宅サーバー

このブログ、フォント設定が2つほどおかしかったので直しました。今まで見づらくてすみません。

1つ目は一部の環境(Linux向けChromeなど)からこのサイトを見るとサンセリフ体(ゴシック体)ではなくセリフ体(明朝体)で表示される現象を直しました。セリフ体になってしまう現象は以前から認識していて不思議だな?とは思ったものの、原因が良くわからず放置していました。

Developer toolsでCSSを確認していたら、font-family: sans-serif, serifと指定していた箇所がありました。原因も何も、自分で指定してただけだったのか……。こんな単純なことに10年以上気づいていなかった。

2つ目はmonospace系の文字が表示されるpreタグなどの文字が小さかったのを直しました。今までfont-size: smallerにしていたんですが、monospaceは元々少し小さいサイズで表示されるブラウザが多くて、めちゃくちゃ字が小さくなっていました。

しかもこちらの現象は認識していませんでした。自分のFirefox環境はmonospaceのフォントサイズを少し大き目に指定している&そのことを忘れており、デフォルト設定のブラウザで見ると字が小さすぎることに気付いてなかったです。すまねぇ。

編集者:すずき(2025/05/23 00:37)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2025年5月14日

signalの未定義動作を見る - その2

目次: C言語とlibc

シグナルマスク(sigprocmaskのマニュアル)の「規定されていない」使い方をするとどうなるか?の続きです。5つのマルチスレッド(親スレッド+4つの子スレッド)で全員でsigwait()するのは共通で、誰がsigprocmask()を呼ぶか?を変えながら、下記4パターンを試します。

  • 最初のスレッドがsigprocmask()
  • 最初のスレッド「以外」の1スレッドがsigprocmask()
  • 全スレッドがsigprocmask()
  • 全スレッドがpthread_sigmask()

今回は結果3と4を紹介します。1〜3は定義されていない動作ですが、4は比較用に実施する「正しい方法」です。

結果3 - 全スレッドがsigprocmask()

全スレッドがsigprocmask()した場合です。ゆっくり5回シグナルを送ると、親スレッド(th 4)のsigwait()がシグナルを受け取り、子スレッド(th 0)のsigwait()はEINTRが返ります。

結果3 - シグナルが少ない場合
$ 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しないのも良いです。

結果3 - シグナルが多い場合
$ 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

良さそうですね。

結果4 - 全スレッドがpthread_sigmask()

正しい方法(全スレッドがpthread_sigmask())はどんな動きでしょうか?結果だけ先に書いてしまうと、全スレッドがsigprocmask()したときと同じ動きをするようです。

結果4 - シグナルが少ない場合
$ 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しません。

結果4 - シグナルが多い場合
$ 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のソースコードを見ればわかるはずなので、また今度に見ようと思います。

ソースコード

こちらからどうぞ。

編集者:すずき(2025/05/21 00:19)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2025年5月12日

signalの未定義動作を見る - その1

目次: C言語とlibc

シグナルマスクのマニュアル(sigprocmaskのマニュアル)を見ると下記のように「規定されていない」とあります。実際Linuxだとどうなるか気になります。

マルチスレッドのプロセスでsigprocmask()を使用した場合の動作は規定されていない。

実行環境は下記のとおりです。他のプラットフォームや過去/将来のバージョンのLinuxで今回の実験結果と同じ動作をするとは限りませんのでご注意ください。

  • Debian Testing (trixie/sid)
  • Linux 6.12.12-1

実験内容は5つのマルチスレッド(親スレッド+4つの子スレッド)で全員でsigwait()するのは共通、誰がsigprocmask()を呼ぶか?を変えながら、下記4パターンを試します。

  • 最初のスレッドがsigprocmask()
  • 最初のスレッド「以外」の1スレッドがsigprocmask()
  • 全スレッドがsigprocmask()
  • 全スレッドがpthread_sigmask()

最後のパターンはマルチスレッドでsigwait()する場合の正しい方法(全スレッドがpthread_sigmask())で、他の3つと動作を比較するためのものです。

プログラム

コードは長くなってしまったので最後にファイルへのリンクを張っておきます。

実験方法はコンパイル時にマクロを適宜切り替えて、生成された./a.outを起動し、別のターミナルからkillコマンドなどでSIGUSR1を送るだけです。

結果1 - 最初のスレッドがsigprocmask()

最初のスレッドがsigprocmask()した場合です。ゆっくり5回シグナルを送ると、親スレッド(th 4)のsigwait()がシグナルを受け取り、子スレッド(th 0)のsigwait()はEINTRが返ってきます。

結果1 - シグナルが少ない場合
$ 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します。ありゃりゃ。

結果1 - シグナルが多い場合
$ 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

一見動くように見えて、だめなパターンですね。

結果2 - 最初のスレッド「以外」の1スレッドがsigprocmask()

最初のスレッド「以外」の1スレッドがsigprocmask()した場合です。1回シグナルを送っただけでabortしました。

結果2 - シグナルが少ない場合
$ 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するのはそりゃそうだなと思います。

続きはまた今度やります。

ソースコード

こちらからどうぞ。

編集者:すずき(2025/05/21 00:17)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2025年5月9日

JavaとM5Stamp C3とBluetooth LE - Bluetoothデバイスとの通信改善

目次: 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/23 23:31)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
2025年5月20日 >>> 2025年5月7日
link もっと後

管理用メニュー

link 記事を新規作成

<2025>
<<<05>>>
----123
45678910
11121314151617
18192021222324
25262728293031

最近のコメント5件

  • link 25年11月28日
    hdkさん (12/02 08:02)
    「"停止直前に急にエンブレがほぼゼロになる...」
  • link 25年10月6日
    すずきさん (10/10 13:14)
    「ですね。ccはもはやコンパイラというより...」
  • link 25年10月6日
    hdkさん (10/10 08:27)
    「ただのHello, worldでも試して...」
  • link 25年9月29日
    すずきさん (10/03 00:29)
    「なんと、メタパッケージ入れてなかったです...」
  • link 25年9月29日
    hdkさん (10/02 06:51)
    「あれ、dkmsは自動ビルドされるのが便利...」

最近の記事20件

  • link 25年11月29日
    すずき (12/02 01:15)
    「[バーベキュー@つくば] 筑波大関連の人が集まってバーベキューするイベントが年1回開かれていて、誘ってもらったので去年から参加...」
  • link 25年11月28日
    すずき (12/02 00:43)
    「[ジャガーさんの所感] 目次: 車購入してから4か月が経ちました。通勤で毎日乗っているためか走行距離が3,000kmを超えまし...」
  • link 23年5月15日
    すずき (12/02 00:43)
    「[車 - まとめリンク] 目次: 車三菱 FTO GPX '95の話。群馬県へのドライブ1群馬県へのドライブ2将来車を買い替え...」
  • link 25年9月7日
    すずき (12/01 22:03)
    「[ジャガーXEのタイヤ交換(FALKEN AZENIS)] 目次: 車タイヤのメーカーが左前だけ違うのと、溝の残りが4mmくら...」
  • link 25年7月20日
    すずき (12/01 22:02)
    「[ジャガーXEを買いました] 目次: 車車を買い替えました。ジャガーXE Sです。マイナー車すぎて会社の人たちもあまり知らなさ...」
  • link 10年9月3日
    すずき (12/01 22:01)
    「[レガシィの納車は明日] 目次: 車中古車屋さんから夕方電話がかかってきました。明日の納車だそうです。担当の方が忙しいかったの...」
  • link 10年9月4日
    すずき (12/01 22:00)
    「[今日の予定] 目次: 車今日の予定はこんな感じ。午前: 納車(茨木)昼: 退寮(高槻)午後: 同期のみなさんと食事(京都)実...」
  • link 25年11月21日
    すずき (12/01 21:55)
    「[ジャガーさんをぶつけた] 目次: 車家の近所の狭い道で路駐してたタクシーをかわして進もうと思したら、左前を電柱にぶつけました...」
  • link 25年11月9日
    すずき (11/23 14:16)
    「[タローマンのゲームTAS動画] 目次: ゲーム今年の夏ごろにシブヤフードダンジョンとタローマンがコラボしたイベント(シブヤフ...」
  • link 21年12月28日
    すずき (11/23 14:14)
    「[ゲーム - まとめリンク] 目次: ゲームNintendo DSを買ったパネルでポンDS最近の朝はパネポンDS聖剣伝説DSチ...」
  • link 25年5月1日
    すずき (11/07 13:52)
    「[首都高バトルSteam版、フルチューン後の姿 - その3] 目次: ゲーム首都高バトル(Steam版)高ランクの車をひたすら...」
  • link 23年4月10日
    すずき (11/04 16:20)
    「[Linux - まとめリンク] 目次: Linuxカーネル、ドライバ関連。Linux kernel 2.4 for ARMが...」
  • link 09年5月18日
    すずき (11/04 16:19)
    「[ffmpegとlibx264] 目次: LinuxせっかくDVDという長めの映像コンテンツが手元にあるので、ffmpegで変...」
  • link 25年10月31日
    すずき (11/02 03:19)
    「[GNU global + pygmentsトラブルシューティングUbuntu編] 目次: Linux先日(2025年10月2...」
  • link 25年10月22日
    すずき (11/02 02:58)
    「[NTPで時刻をすぐに合わせたい] 目次: LinuxNTPで時刻を調整する方法は2つあって、ズレている時間をジワジワ合わせて...」
  • link 23年6月1日
    すずき (11/02 02:42)
    「[自宅サーバー - まとめリンク] 目次: 自宅サーバーこの日記システム、Wikiの話。カウンターをPerlからPHPに移植日...」
  • link 05年11月23日
    すずき (11/02 02:41)
    「[NTPで時計合わせ、その2] 目次: 自宅サーバー11/23現在、未だGoogle先生に捕捉されていない奇跡。それはさておき...」
  • link 05年11月22日
    すずき (11/02 02:41)
    「[NTPで時計合わせ] 目次: 自宅サーバーパソコンの時計は勝手にどんどんずれていきます。放って置くと1分くらいずれていること...」
  • link 15年5月8日
    すずき (11/02 02:40)
    「[GPSは世界一正確な時計、その2] 目次: 自宅サーバー前回(2015年3月9日の日記参照)はGPSモジュールをPCと接続し...」
  • link 15年3月9日
    すずき (11/02 02:40)
    「[GPSは世界一正確な時計] 目次: 自宅サーバーGPSのレシーバーモジュールを買いました。Globalsat BU-353S...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 2025年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDFファイル RSS 1.0

最終更新: 12/02 08:02