コグノスケ


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

link もっと前
2025年5月14日 >>> 2025年5月1日
link もっと後

2025年5月14日

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

目次: C言語とlibc

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

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

今回は結果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/16 03:05)

コメント一覧

  • コメントはありません。
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/16 02:30)

コメント一覧

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



2025年5月5日

timespecの操作関数

目次: C言語とlibc

POSIXには時間を表す構造体が2つあります。マイクロ秒単位のstruct timevalと、ナノ秒単位のstruct timespecです。struct timespecの方が後発なのか、比較的新しいAPIはstruct timespecを使う傾向にありますが、基本的には混在しています。

timevalとtimespec

struct timeval {
    time_t      tv_sec;     /* 秒 */
    suseconds_t tv_usec;    /* マイクロ秒 */
};

struct timespec {
    time_t tv_sec;     /* 秒 */
    long   tv_nsec;    /* ナノ秒 */
};

見てのとおり構造体ですから、足し算や引き算をするにも桁上がりを考える必要があって一苦労必要かと思いきや、POSIXはtimeradd/timersubなどのマクロを用意しており、struct timevalの加減算と比較などが比較的簡単に行えます。便利ですね。

ではstruct timespecにも同様にtimespecadd/timespecsubがあると思いますよね?ところが一部のlibc(BSD系やnewlib)以外は実装していません。なぜ……!?

timeraddのtimespec版

#define timespecadd(a, b, res)					\
	do {							\
		(res)->tv_sec = (a)->tv_sec + (b)->tv_sec;	\
		(res)->tv_nsec = (a)->tv_nsec + (b)->tv_nsec;	\
		if ((res)->tv_nsec >= 1000000000) {		\
			(res)->tv_sec++;			\
			(res)->tv_nsec -= 1000000000;		\
		}						\
	} while (0)
#define timespecsub(a, b, res)					\
	do {							\
		(res)->tv_sec = (a)->tv_sec - (b)->tv_sec;	\
		(res)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec;	\
		if ((res)->tv_nsec < 0) {			\
			(res)->tv_sec--;			\
			(res)->tv_nsec += 1000000000;		\
		}						\
	} while (0)
#define timespecclear(tsp)    ((tsp)->tv_sec = (tsp)->tv_nsec = 0)
#define timespecisset(tsp)    ((tsp)->tv_sec || (tsp)->tv_nsec)
#define timespeccmp(a, b, cmp)					\
	(((a)->tv_sec == (b)->tv_sec) ?				\
		((a)->tv_nsec cmp (b)->tv_nsec) :		\
		((a)->tv_sec cmp (b)->tv_sec))

いつもstruct timespecを要求するAPIを使うたびにtimespecadd/timespecsubがなくて演算しづらさにイライラするので、コピペで使い回せるように実装例をメモしておきます。この程度なら誰が書いてもほとんど同じコードになると思いますが、気になる方のためにもし著作権が発生する場合はBSD 3条項ライセンス扱いでお願いします。

編集者:すずき(2025/05/16 23:40)

コメント一覧

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



2025年5月1日

首都高バトルSteam版、フルチューン後の姿 - その3

目次: ゲーム

首都高バトル(Steam版)高ランクの車をひたすらフルチューンするやつの続きです。前回と合わせて8車種ほどフルチューンしました。もう飽きました。


SUZUKI SWIFT Sport(ZC33S)フルチューン


TOYOTA SUPRA RZ(JZA80)フルチューン

検索用にフルチューン後の主要パラメータを書いておきます。ちなみにスピード指標はギア比を最高速重視にすると高い数値になるので、参考程度です。

車種最高出力最大トルクスピード指標重量
SWIFT Sport(ZC33S) '22241PS/5,600rpm42kg/2,800rpm348.40949kg
SUPRA RZ(JZA80) '97 435PS/5,600rpm74kg/3,600rpm419.781,480kg

あとはインプレッサのどれかをフルチューンするかもなあくらいですが、あまり気力が沸きません。

ローダウンの謎

アップデート前は車高は下げれば下げるほどカーブが速くなる謎システムでしたが、アップデート後は限界まで車高を下げるとストレートで地面と擦って火花が出る&逆に遅くなる変更が入ったそうです。キャプチャでは5段階のうち4まで下げた状態で撮っています。個人的には見た目は3か2くらいが一番バランスが良いですね。


RX-7のフロントRIDE HIGHT = -5

5まで下げるとおかしなことが起きる車もあって、RX-7はフロントを限界まで下げるとタイヤがフェンダーとボンネットを貫通して変な表示になります……。

編集者:すずき(2025/05/05 12:18)

コメント一覧

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



link もっと前
2025年5月14日 >>> 2025年5月1日
link もっと後

管理用メニュー

link 記事を新規作成

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

最近のコメント5件

  • link 25年2月19日
    katanaさん (03/21 05:30)
    「katana」
  • link 25年1月23日
    katanaさん (03/20 18:50)
    「katana」
  • link 24年12月9日
    すずきさん (03/14 00:42)
    「Thanks for your comm...」
  • link 24年12月9日
    hyfanさん (03/13 07:21)
    「Hello from Santa Cla...」
  • link 25年2月10日
    すずきさん (02/13 02:03)
    「解読しました。なるほど、exitの引数が...」

最近の記事20件

  • link 06年2月26日
    すずき (05/17 19:40)
    「あるプログラムのある箇所が動かず。これで合ってるはずなのに!って思ってよく見たら、フラグの判定が全部if (flags ...」
  • link 06年2月8日
    すずき (05/17 17:52)
    「[Samba over SSH] 環境はDebian/GNU Linux(Sarge)です。OpenSSHを用いたポートフォワ...」
  • link 04年2月28日
    すずき (05/17 03:50)
    「作業は一の矢、平砂の両畑を耕して終了です。畑もだいぶ拓けまして、まだゴミや木の根が多いですが、整備していけば目立たなくな...」
  • link 03年12月19日
    すずき (05/17 03:19)
    「[標準入力から1行読み込む処理] 目次: C言語とlibc簡単そうに見えてハマりました。固定バッファは溢れたりして厄介なので、...」
  • link 22年4月13日
    すずき (05/17 03:16)
    「[C言語とlibc - まとめリンク] 目次: C言語とlibcC言語について。プログラムの落とし穴、演算子の優先順位標準入力...」
  • link 03年11月6日
    すずき (05/17 03:00)
    「[プログラムの落とし穴、演算子の優先順位] 目次: C言語とlibc言語を学ぶにあたり演算子の優先順位が必ず出てくると思います...」
  • link 25年5月5日
    すずき (05/16 23:40)
    「[timespecの操作関数] 目次: C言語とlibcPOSIXには時間を表す構造体が2つあります。マイクロ秒単位のstru...」
  • link 24年7月29日
    すずき (05/16 10:42)
    「[OpenSBIを調べる - デバイスツリーの扱い(実機想定)] 目次: LinuxOpenSBIのブート部分を調べます。Op...」
  • link 25年5月14日
    すずき (05/16 03:05)
    「[signalの未定義動作を見る - その2] 目次: C言語とlibcシグナルマスク(sigprocmaskのマニュアル)の...」
  • link 15年5月14日
    すずき (05/16 03:04)
    「 ...」
  • link 25年5月12日
    すずき (05/16 02:30)
    「[signalの未定義動作を見る - その1] 目次: C言語とlibcシグナルマスクのマニュアル(sigprocmaskのマ...」
  • link 21年12月28日
    すずき (05/05 12:18)
    「[ゲーム - まとめリンク] 目次: ゲーム一覧が欲しくなったので作りました。Wizardry(囚われし亡霊の街)敵が強すぎる...」
  • link 25年5月1日
    すずき (05/05 12:18)
    「[首都高バトルSteam版、フルチューン後の姿 - その3] 目次: ゲーム首都高バトル(Steam版)高ランクの車をひたすら...」
  • link 25年4月18日
    すずき (04/24 00:26)
    「[Google Pixel 8a] 4年前に購入したGoogle Pixel 4aのバッテリーが劣化してきたらしく、頻繁に使う...」
  • link 25年4月10日
    すずき (04/17 01:30)
    「[udevルールのテスト方法] 目次: Linux何かudevの設定ルールを書いたときどうやってテストしたら良いでしょうか?u...」
  • link 25年4月11日
    すずき (04/16 01:01)
    「[udevルールのデバッグ例] 目次: Linux最後にudevルールのデバッグ例も書いておきましょう。/dev/ttyS0の...」
  • link 23年4月10日
    すずき (04/16 00:59)
    「[Linux - まとめリンク] 目次: Linux関係の深いまとめリンク。目次: RISC-V目次: ROCK64/ROCK...」
  • link 25年4月7日
    すずき (04/16 00:14)
    「[udevルールのマイナーな方] 目次: Linux最近udevを少しいじっていたので忘れないうちにメモします。Debianや...」
  • link 25年4月4日
    すずき (04/15 22:48)
    「[xrdpに接続しても画面がなかなか表示されない] 目次: Linuxネットワークが不安定な環境でLinux PCのxrdpサ...」
  • link 25年3月24日
    すずき (04/12 23:57)
    「[首都高バトルSteam版、フルチューン後の姿 - その1] 目次: ゲーム今の首都高バトル(Steam版)はWonderer...」
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

最終更新: 05/17 19:40