Armadilloフォーラム

シグナル受信処理について

n.yamamoto

2015年7月6日 16時09分

Yamamotoです。

Armadillo内部の確認ではないのですが・・・

子プロセス(fork)のsystem命令でNtpClientを実行して時刻合わせをしています。
その際、子プロセスが終了してゾンビ化しないようにシグナルを受信するように関数登録していますが
この関数内ではファイルのRead/Writeとかprintfとかを実施すべきではないのでしょうか?

調べたところ攻撃される要因になるという事で、行うべきではないと有りますが異常動作の要因になるような記載がありません。

調査中ですが、異常発生時の要因を調べるべくプロセス終了(強制終了含め)とシグナル受信にファイルへのログ出力処理を入れて実行していますが
何かの要因でデッドロック状態に陥っているように思われます。

で、もしかしてシグナル受信関数でファイルWriteを実行した際に、ひょっとして間が悪ければ処理が何処かへ行って戻ってこなくなっているのではないかと
考えて、想定が合っているか?
それとも、通常では有りえない事なのか知りたいので教えてください!

よろしくお願いいたします。

コメント

at_takenoshita

2015年7月6日 16時22分

竹之下です。

> その際、子プロセスが終了してゾンビ化しないようにシグナルを受信するように関数登録していますが
> この関数内ではファイルのRead/Writeとかprintfとかを実施すべきではないのでしょうか?
C言語でプログラムを記述しているという前提で考えた場合、シグナルハンドラの中ではprintfは実施すべきではないですね。

かなり昔の記事ですが、下記のブログが参考になると思います。
「なんでシグナルハンドラ内でprintfを読んではダメなのか?」についても記載があります。
UNIX上でのC++ソフトウェア設計の定石 (2) - memologue : http://d.hatena.ne.jp/yupo5656/20040712/p2
シグナルハンドラを使わないでシグナルをハンドルする - memologue : http://d.hatena.ne.jp/yupo5656/20060114/p1

参考になれば。

kes-konishi

2015年7月6日 17時00分

KES)小西です。

2つ問題があると思います。
printfは竹之下さんがおっしゃっているようにprintfは
async-signal-safe functionsではないため、保障外となります。
(http://linuxjm.osdn.jp/html/LDP_man-pages/man7/signal.7.html
でのasync-signal-safe functionsのリストが保障対象となります。実装されてないものがあったり。。。)

でファイルのアクセスは
http://linuxjm.osdn.jp/html/LDP_man-pages/man2/write.2.html
でのバグ報告より、3.13以前ではファイルオフセットの更新に関してはアトミック性が死んでいるため
メインのスレッドとシグナルハンドラで同時にアクセスすると
ファイルが壊れる可能性があるかと思います。
↑これが「デッドロック」ってことはないでしょうか?
もっとも、メインでオープンしたディスクリプタをシグナルハンドラからグローバルアクセスした場合になりますが。。。

n.yamamoto

2015年7月6日 20時28分

Yamamotoです。

やはり、竹ノ内さん小西さんのご意見のとおり、何かの条件に合うとデッドロックしてしまうんでしょうね?

今は試しにログなしで、これまで発生しているような条件で何度か試してOKになるか見ています。

izawa

2015年7月6日 17時41分

伊澤です。
こう言う話になるとしゃしゃり出ます。

> 子プロセス(fork)のsystem命令でNtpClientを実行して時刻合わせをしています。
まず確認ですが、非同期にNtpClientを実行したいだけでしょうか。
もしそうであるなら、fork()せずにsystem()にアンパサンド(&)つきの引き数を渡せばいいような気がします。
system()はシェルを起動しますので、シェルが良きに計らってくれます。
NtpClientは使ったことがありませんが、system("ntpclient -h ... -s &")のような感じでしょうか。

> その際、子プロセスが終了してゾンビ化しないようにシグナルを受信するように関数登録していますが
そうではなくて、敢えてfork&execイディオムで起動するということでしょうか。
その場合も、通常はwaitで死に水を取れば充分な気がします。
int pid = fork()したならwaitpid(pid, & status, 0)で終了までブロックしますが、
定期的に通過するポイントでwaitpid(pid, & status, WNOHANG)でしょうか。
本件ではNtpClientの再起動をするわけでもないでしょうから、signalは見なくてよさそうかと。
signal処理しなければ、printf()などの問題も杞憂ですね。

n.yamamoto

2015年7月6日 20時25分

Yamamotoです。

複数台の装置の時刻を出来るけ合わせたいの、10分に1回とか結構な頻度でNtpClientを実行します。

Shellで実行すると、NtpClientがデッドロック(以前終了しないことがあることを確認しています)で終了しないとプロセスが増え続ける可能性があるので、出来るだけ所定時間実行して終了しないと強制終了させたいのです。

伊澤さんの意見で言うとfork+waitpid(WNOHANG)ループが良かったかもしれません。
最後に試す価値ありです。

waitpidで所定回数待って、終わらなかったらforkの戻り値のPIDでKillすれば良いですよね?
(Kill後もゾンビ化しないようにwaitpidは必要と思いますが)

at_yashi

2015年7月7日 11時03分

> 複数台の装置の時刻を出来るけ合わせたいの、10分に1回とか結構な頻度でNtpClientを実行します。

そういうときは、ntpd を使う方がよいかもしれません。

at_hanada

2015年7月7日 21時24分

花田です。

> 複数台の装置の時刻を出来るけ合わせたいの、10分に1回とか結構な頻度でNtpClientを実行します。

目的としてはntpdも合いますが、先日のうるう秒挿入のときにも…

7/1の閏秒を迎えるにあたってLinuxでは何をすべきか? | Act as Professional
http://hiroki.jp/leap-second-2015
> ntpdの比較的新しいバージョンにSLEWモードで動作していても、うるう秒が挿入されるバグが発見されました。[redhat]

など最近でもいくつか問題出ておりまして、またDistに入っているものはGPLv2ライセンスなのに対し、2010年以降のバージョンはGPLv3ライセンスに変更されているなど、局面によって安易に勧めにくい状況です。

そのような理由から代替となるものとして、最近多くのディストリビューションに収録されるようになったchronyを、Atmark Distに移植してみました。Distへのパッチを添付します。

使い方については、ググれば出てきますが(^^;
とりあえずは下記あたりが必要な情報含まれおり参考にしやすいかと思います。

adjtimexもntpdも使えねーと思ったのは私だけではなかった:半歩先:So-netブログ
http://hanposaki.blog.so-net.ne.jp/2009-09-27

ファイル ファイルの説明
atmark-dist-20150618_chrony.patch