Armadilloフォーラム

カーネルの再起動

md-n58

2016年10月25日 15時56分

Armadillo-420を使い始めたばかりです。

<状況>
アプリを作成し、このアプリの動作モニタ用としてprintf()でコンソールに数秒毎に表示させています。
 ・アプリは、guestユーザーで動作させています。
 ・コンソールは、デフォルトから、ttymxc2に切り替えています。

突然、このモニタ用の表示が止まり、10秒程度で、
Hermit-At...
とカーネルの起動表示になります。

<質問>
これは、アプリの不具合で、カーネルがいきなり停止するという状況でしょうか。
(カーネル動作不能、ウォッチドッグタイムオーバー(10秒程度)、再起動という流れ)
アプリの不具合で、カーネルから何らかのメッセージは出ないのでしょうか。
コンソールを切り替えたため、システムからのメッセージが欠落するのでしょうか。

<補足>
アプリのソースを見直したところ、llocaltimne() を、イベント処理(タイマー100mS毎)と通常処理の
2か所で使用しているので競合した場合に落ちるのではと考えています。
(localtimne() は、スレッドセーフでないとのこと)

コメント

at_takashi.sasayama

2016年10月25日 17時40分

笹山です。

> <質問>
> これは、アプリの不具合で、カーネルがいきなり停止するという状況でしょうか。
> (カーネル動作不能、ウォッチドッグタイムオーバー(10秒程度)、再起動という流れ)
> アプリの不具合で、カーネルから何らかのメッセージは出ないのでしょうか。

カーネルがフリーズした際は、何もログ出力せず再起動する現象が発生する可能性があります。
(ハードウェアウォッチドッグタイマーによるリセット)

アプリの不具合の場合は、セグメンテーションフォルトなどはメッセージが出力されますが、
デッドロックなどの場合は、出力されない可能性があります。
ただ、ソフトウェアウォッチドッグタイマーなどをご使用になられていない限りは、
アプリの不具合の場合は基本的に再起動は発生しません。

> コンソールを切り替えたため、システムからのメッセージが欠落するのでしょうか。
コンソールを切り替えても、システムからのメッセージは欠落しないはずです。

数点ご確認をさせていただけないでしょうか?

1. ご使用のカーネルバージョンを教えていただけないでしょうか?

2. ソフトウェアウォッチドッグタイマーはご使用されているのでしょうか?
(その場合でしたら、アプリがデッドロックするなどして再起動がかかる可能性はあります)

3. こちらでも現象を再現させてみたいのですが、再現頻度はどの程度でしょうか?
また可能であれば再現用のソースコード等をいただくことはできないでしょうか?

> <補足>
> アプリのソースを見直したところ、llocaltimne() を、イベント処理(タイマー100mS毎)と通常処理の
> 2か所で使用しているので競合した場合に落ちるのではと考えています。
> (localtimne() は、スレッドセーフでないとのこと)

4. スレッドセーフ版 である localtime_r() をご使用された場合でも再起動は発生してしまいますでしょうか?

お手数ですがどうぞよろしくお願い致します。

> カーネルがフリーズした際は、何もログ出力せず再起動する現象が発生する可能性があります。
> (ハードウェアウォッチドッグタイマーによるリセット)
100mSのインターバルタイマーで、秒が変わるごとに、printf()させてます。
これが出なくなって10秒程で、再起動されるので、このリセットと見ています。

> アプリの不具合の場合は、セグメンテーションフォルトなどはメッセージが出力されますが、
> デッドロックなどの場合は、出力されない可能性があります。
セグメンテーションフォルトは出しました。このため、アプリ側の不具合ならメッセージが出るとものと思っていました。

> 1. ご使用のカーネルバージョンを教えていただけないでしょうか?
Linux version 3.14.36-at7

> 2. ソフトウェアウォッチドッグタイマーはご使用されているのでしょうか?
> (その場合でしたら、アプリがデッドロックするなどして再起動がかかる可能性はあります)
使用(起動)しています。再起動の直前では、数秒以内にクリヤしています。
タイムアウトが60秒なので、ソフトウェアウォッチドッグと異なると思います。

> 3. こちらでも現象を再現させてみたいのですが、再現頻度はどの程度でしょうか?
10分~10時間に一度、再起動が発生します。

> また可能であれば再現用のソースコード等をいただくことはできないでしょうか?
実機が1台で連続運転試験しているので、再現試験・再現用のソースコード作成に使えないです。
再起動するのは、メインループでlocaltime()を通過した箇所です。
メインループ中で、localtime()を使用して、タイマーイベント中でもlocaltime()を使用すれば発生すると考えています。
使用頻度が高いほど、再起動となる時間が短くなると考えます。

> 4. スレッドセーフ版 である localtime_r() をご使用された場合でも再起動は発生してしまいますでしょうか?
localtime()をイベント処理のみとして、評価試験を実施しています。とりあえず6時間は、再起動無で経過しました。
本当は、原因箇所のみを圧縮して、加速試験したいのですが、納期の関係で余分なことができません。
localtime_r()も評価する余裕がありません。

> カーネルがフリーズした際は、何もログ出力せず再起動する現象が発生する可能性があります。
> (ハードウェアウォッチドッグタイマーによるリセット)
100mSのインターバルタイマーで、秒が変わるごとに、printf()させてます。
これが出なくなって10秒程で、再起動されるので、このリセットと見ています。

> アプリの不具合の場合は、セグメンテーションフォルトなどはメッセージが出力されますが、
> デッドロックなどの場合は、出力されない可能性があります。
セグメンテーションフォルトは出しました。このため、アプリ側の不具合ならメッセージが出るとものと思っていました。

> 1. ご使用のカーネルバージョンを教えていただけないでしょうか?
Linux version 3.14.36-at7

> 2. ソフトウェアウォッチドッグタイマーはご使用されているのでしょうか?
> (その場合でしたら、アプリがデッドロックするなどして再起動がかかる可能性はあります)
使用(起動)しています。再起動の直前では、数秒以内にクリヤしています。
タイムアウトが60秒なので、ソフトウェアウォッチドッグと異なると思います。

> 3. こちらでも現象を再現させてみたいのですが、再現頻度はどの程度でしょうか?
10分~10時間に一度、再起動が発生します。

> また可能であれば再現用のソースコード等をいただくことはできないでしょうか?
実機が1台で連続運転試験しているので、再現試験・再現用のソースコード作成に使えないです。
再起動するのは、メインループでlocaltime()を通過した箇所です。
メインループ中で、localtime()を使用して、タイマーイベント中でもlocaltime()を使用すれば発生すると考えています。
使用頻度が高いほど、再起動となる時間が短くなると考えます。

> 4. スレッドセーフ版 である localtime_r() をご使用された場合でも再起動は発生してしまいますでしょうか?
localtime()をイベント処理のみとして、評価試験を実施しています。とりあえず6時間は、再起動無で経過しました。
本当は、原因箇所のみを圧縮して、加速試験したいのですが、納期の関係で余分なことができません。
localtime_r()も評価する余裕がありません。

> また可能であれば再現用のソースコード等をいただくことはできないでしょうか?
再現用のソースコードです。
実際のものに比べて各時間を短くして、発生までの時間を短縮しています。
10回動作させて、数秒~3分程度で、カーネルの再起動となります。

ログ表示は、
...
localtime start
INT localtime start
<約10秒経過>
Hermit-At v3.8.0 (armadillo4x0) compiled at 20:27:58, Oct 22 2015
...
となり、多重のlocaltime()により、カーネル停止と考えます。

メインループ内のlocaltime()を、コメントアウトしてあるlocaltime_t()に切り替えれば、カーネル停止となりません。

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <ctype.h>
 
struct tm tim;        //時刻(インターバルタイマでセット)
struct tm tim_b;    //通常処理用
int old_sec;
 
/*-------------------------------------------------------------------*/
/*  日時読み出し                                                */
/*-------------------------------------------------------------------*/
struct tm *localtime_t(void) {
    int    n=0;
    do {
    tim_b = tim;
    n++;
    } while((tim_b.tm_min!=tim.tm_min)||(tim_b.tm_sec!=tim.tm_sec));
    return &tim_b;
}
 
/*-------------------------------------------------------------------*/
/*  インターバルタイマ                                                */
/*-------------------------------------------------------------------*/
void SignalHandler(int signum)
{
    ((void)signum);        //unusedのためのダミー
 
    time_t    t=time(NULL);
    printf("INT localtime start\n");
    tim = *localtime(&t);
    printf("INT localtime end\n");
 
    if (old_sec!=tim.tm_sec){
        printf("timer:%u sec\n", tim.tm_sec);
        old_sec = tim.tm_sec;
    }
}
 
/*-------------------------------------------------------------------*/
/*   インターバルタイマー初期化                                                     */
/*-------------------------------------------------------------------*/
int intr_init() {
    struct itimerval timer;
    struct sigaction action;
    memset(&action, 0, sizeof(action));
 
    /* set signal handler */
    action.sa_handler = SignalHandler;
    action.sa_flags = SA_RESTART;
    sigemptyset(&action.sa_mask);
    if(sigaction(SIGALRM, &action, NULL) < 0){
        perror("sigaction error");
        exit(1);
    }
 
    timer.it_value.tv_sec = 0;
    timer.it_value.tv_usec = 10000;
    timer.it_interval.tv_sec = 0;
    timer.it_interval.tv_usec = 10000;        //10,000uS=10mS
    if(setitimer(ITIMER_REAL, &timer, NULL) < 0){
        perror("setitimer error");
        exit(1);
    }
    return (0);
}
 
/*-------------------------------------------------------------------*/
/* メイン処理                                                        */
/*-------------------------------------------------------------------*/
int main() {
int    fd_wdt;
int    ret;
int    val=1;
char    dev_wdt[]="/dev/watchdog";
time_t    starttime;
struct    tm *startlocal;
struct    timespec req, rem;
 
    printf("program start\n");
 
    intr_init();        // インターバルタイマー初期化
 
    do {
 
        starttime = time(NULL);        // 時刻取得
        printf("localtime start\n");
//        startlocal = localtime(&starttime);    // 時刻変換
        startlocal = localtime_t();    // 時刻変換
        printf("localtime end\n");
 
        fd_wdt = open(dev_wdt,O_WRONLY);        //wdt 60s
        ret=write(fd_wdt,"!",1);
        close(fd_wdt);
 
        req.tv_sec = 0;
        req.tv_nsec = 10000000;        //wait 10msec
        rem.tv_sec = 0;
        rem.tv_nsec = 0;
 
        while (nanosleep(&req, &rem)){
            if(errno==EINTR){
                req.tv_sec = rem.tv_sec;
                req.tv_nsec = rem.tv_nsec;
            }
        }
 
    } while(val==1);
 
    fd_wdt = open(dev_wdt,O_WRONLY);    //wdt end
    ret=write(fd_wdt,"V",1);
    close(fd_wdt);
    printf("program end\n");    //end
}

at_takashi.sasayama

2016年10月26日 13時39分

笹山です。

再現確認用のソースコードのご提供有難うございました。
こちらでも再現試験を実施してみたいと思います。

すいません。サンプルソースは、
localtime()側が、コメントアウトになっていました。
startlocal = localtime(&starttime); // 時刻変換
//startlocal = localtime_t(); // 時刻変換
としてください。

at_takashi.sasayama

2016年10月27日 15時42分

笹山です。

再現試験を実施してみたところ、カーネル再起動の直接の原因は
デフォルトで有効のハードウェアウォッチドッグタイマーによるリセットですね。

そう判断した理由ですが、以下の様にウォッチドッグタイマー部を無効にすると
再起動せず、アプリが固まる現象のみ発生する為です。

                startlocal = localtime(&starttime);     // 時刻変換
                //startlocal = localtime_t();     // 時刻変換
                printf("localtime end\n");
 
                // 以下3行をコメントアウトしてソフトウェアウォッチドッグを無効
                //fd_wdt = open(dev_wdt,O_WRONLY);                //wdt 60s
                //ret=write(fd_wdt,"!",1);
                //close(fd_wdt);

なおデフォルトで有効のハードウェアウォッチドッグタイマーは、ソフトウェアウォッチドッグタイマーと同じ様な
インターフェース (/dev/watchdog, /dev/watchdog0) を提供しており、タイムアウト値は10秒です。

以下の内容で解説されている CONFIG_SOFT_WATCHDOG の有効化で使用できる、ソフトウェアウォッチドッグタイマーは
タイムアウト値は60秒です。

ソフトウェアウォッチドッグタイマーの使用方法
https://users.atmark-techno.com/blog/53/1319

例として、 CONFIG_SOFT_WATCHDOG を有効にした状態で、ソースを以下の様に変更すると
アプリ停止後、60秒でタイムアウトするのが確認できると思います。

char    dev_wdt[]="/dev/watchdog1"; # /dev/watchdog から /dev/watchdog1 に変更

再試験ありがとうございました。

> 4. スレッドセーフ版 である localtime_r() をご使用された場合でも再起動は発生してしまいますでしょうか?
localtime()を全てlocaltime_r()に置き換えたら、再起動は発生しませんでした。
(単純に置き換え可能と思ったら、インターフェースが違いました)

まとめると、今回の再起動は、
1.多重の(スレッドセーフでない)localtime()で、アプリが固まる。
 (localtime()から帰って来ない)
2.ウォッチドッグタイマーを動作させていたので、タイムアウトで再起動となる。
 (この時のタイムアウト時間は10秒)
ということであった。

そこで、さらに質問というか確認です。
> なおデフォルトで有効のハードウェアウォッチドッグタイマーは、ソフトウェアウォッチドッグタイマーと同じ様な
> インターフェース (/dev/watchdog, /dev/watchdog0) を提供しており、タイムアウト値は10秒です。
Armadillo-420において、
A./dev/watchdog, /dev/watchdog0 は、ハードウェアウォッチドッグタイマー(相当)であり、タイムアウト値は10秒
 このウォッチドッグタイマーは、アプリ側で起動/停止でき、アプリ側で起動しないと機能しない。
B./dev/watchdog1は、ソフトウェアウォッチドッグタイマーであり、タイムアウト値は60秒
 このウォッチドッグタイマーも、Aと同様に扱える。
C.上記の「デフォルトで有効のハードウェアウォッチドッグタイマー」とは、(/dev/watchdog, /dev/watchdog0) を指していて、
 カーネルコンフィグレーションで、「デフォルトで有効」になっているという意味である。
 この有効はウォッチドッグタイマーの機能が「有効(起動)」の意味ではない。
D.「ソフトウェアウォッチドッグタイマーの使用方法」中の、ハードウェアウォッチドッグタイマーは、
 カーネルが管理していて、アプリからは見えない。(少なくとの/dev/watchdog, /dev/watchdog0とは異なる)
 このハードウェアウォッチドッグタイマーは、カーネル/ブートローダ起動後は機能しており、動作停止できない。
 (カーネルのソース修正等で、機能削除可能らしい)
という認識でよかったでしょうか。

当方の現象である、アプリの動作停止後10秒で再起動は、
(アプリ停止なら起動したソフトウエアウオッチドッグのタイムアウト値60秒となるから)
10秒での再起動 = カーネルのハードウエアウオッチドッグのタイムアウト
→ カーネルのクラッシュ、と考えていました。

各マニュアルに、ハードウエアウオッチドッグ相当(タイムアウト値10秒)が用意されているという説明が無いため、
用意されているのは、全てソフトウエアウオッチドッグ(タイムアウト値はデフォルト60秒)だと思っていました。

at_takashi.sasayama

2016年11月1日 11時15分

笹山です。

> まとめると、今回の再起動は、
> 1.多重の(スレッドセーフでない)localtime()で、アプリが固まる。
>  (localtime()から帰って来ない)
> 2.ウォッチドッグタイマーを動作させていたので、タイムアウトで再起動となる。
>  (この時のタイムアウト時間は10秒)
> ということであった。

その通りだと考えています。

> そこで、さらに質問というか確認です。
> > なおデフォルトで有効のハードウェアウォッチドッグタイマーは、ソフトウェアウォッチドッグタイマーと同じ様な
> > インターフェース (/dev/watchdog, /dev/watchdog0) を提供しており、タイムアウト値は10秒です。
> Armadillo-420において、
> A./dev/watchdog, /dev/watchdog0 は、ハードウェアウォッチドッグタイマー(相当)であり、タイムアウト値は10秒
>  このウォッチドッグタイマーは、アプリ側で起動/停止でき、アプリ側で起動しないと機能しない。

アプリ側で起動しなくても、Armadillo 起動直後から機能しており、
カーネルが固まった際にハードウェアウォッチドッグタイマーリセットを発生させます。

> B./dev/watchdog1は、ソフトウェアウォッチドッグタイマーであり、タイムアウト値は60秒
>  このウォッチドッグタイマーも、Aと同様に扱える。

その通りです。

> C.上記の「デフォルトで有効のハードウェアウォッチドッグタイマー」とは、(/dev/watchdog, /dev/watchdog0) を指していて、
>  カーネルコンフィグレーションで、「デフォルトで有効」になっているという意味である。
>  この有効はウォッチドッグタイマーの機能が「有効(起動)」の意味ではない。

A.の回答と同じく、「デフォルトで有効」という意味は、ウォッチドッグタイマーの機能が有効になり、起動しているという意味です。

> D.「ソフトウェアウォッチドッグタイマーの使用方法」中の、ハードウェアウォッチドッグタイマーは、
>  カーネルが管理していて、アプリからは見えない。(少なくとの/dev/watchdog, /dev/watchdog0とは異なる)
>  このハードウェアウォッチドッグタイマーは、カーネル/ブートローダ起動後は機能しており、動作停止できない。
>  (カーネルのソース修正等で、機能削除可能らしい)
> という認識でよかったでしょうか。

カーネルが管理していますが、アプリからも /dev/watchdog, /dev/watchdog0 として見えます。

くどいですが、
> A./dev/watchdog, /dev/watchdog0 は、ハードウェアウォッチドッグタイマー(相当)であり、タイムアウト値は10秒
>  このウォッチドッグタイマーは、アプリ側で起動/停止でき、アプリ側で起動しないと機能しない。
となり(ごめんなさい、この箇所に対する明確な肯定表現がありませんでしたので再確認です)
さらに(マニュアルにも記載あるように)、
> アプリ側で起動しなくても、Armadillo 起動直後から機能しており、
> カーネルが固まった際にハードウェアウォッチドッグタイマーリセットを発生させます。
ということですね。

at_takashi.sasayama

2016年11月1日 13時15分

笹山です。

> > A./dev/watchdog, /dev/watchdog0 は、ハードウェアウォッチドッグタイマー(相当)であり、タイムアウト値は10秒
> >  このウォッチドッグタイマーは、アプリ側で起動/停止でき、アプリ側で起動しないと機能しない。
> となり(ごめんなさい、この箇所に対する明確な肯定表現がありませんでしたので再確認です)
> さらに(マニュアルにも記載あるように)、
> > アプリ側で起動しなくても、Armadillo 起動直後から機能しており、
> > カーネルが固まった際にハードウェアウォッチドッグタイマーリセットを発生させます。
> ということですね。

/dev/watchdog, /dev/watchdog0 は、ハードウェアウォッチドッグタイマーであり、タイムアウト値は10秒。
このウォッチドッグタイマーは、アプリ側で起動/停止でき、アプリ側で起動しなくても自動で起動しています。
その為、カーネルが固まった際にハードウェアウォッチドッグタイマーリセットを発生させます。

ということでしたらご認識の通りです。

了解しました。
長々とお付き合い、ありがとうございました。本件、当方はこれで終了いたします。
今後とも、よろしくお願いします。

中村です。

よくわからなかったところがあるので、質問させてください。

今回のハードウェアウォッチドッグタイマーによるリセットは、
カーネルクラッシュによるものではなく(カーネル内部で定期的に
たたく処理が停止したことによるものではなく)、
ハードウェアウォッチドッグタイマーのアプリケーション側のI/F
/dev/watchdog(/dev/watchdog0)に対してアプリケーションから
openとwriteをしてアプリケーション側からも使用していたため、
アプリケーションが固まったことにより、ハードウェアウォッチドッグが
作動した(リセットがかかった)という理解であってますか?

クローズ宣言の後の質問になりますが、
よろしくお願いいたします。

--
なかむら

質問者です。
質問者として、回答者の内容をまとめると、中村様の内容になると思います。

> そう判断した理由ですが、以下の様にウォッチドッグタイマー部を無効にすると
> 再起動せず、アプリが固まる現象のみ発生する為です。
当方では、確認しておりませんが、この現象から、アプリ側のみフリーズ、カーネルは動作していると判断しました。

> localtime()を全てlocaltime_r()に置き換えたら、再起動は発生しませんでした。
と示しましたが、サンプルソースを、localtime_r()に変更して、確認したら再起動が発生しました。

当方としては、再起動の理由が欲しかっただけなので、この件での質問はしません。

中村です。

> 質問者として、回答者の内容をまとめると、中村様の内容になると思います。

ご確認いただき、どうもありがとうございます。

> > そう判断した理由ですが、以下の様にウォッチドッグタイマー部を無効にすると
> > 再起動せず、アプリが固まる現象のみ発生する為です。

このときに提示されていたソース修正

                // 以下3行をコメントアウトしてソフトウェアウォッチドッグを無効
                //fd_wdt = open(dev_wdt,O_WRONLY);                //wdt 60s
                //ret=write(fd_wdt,"!",1);
                //close(fd_wdt);

のコメント「ソフトウェアウォッチドッグを無効」が、
今回のやりとりをみていて、よくわからなかったところでした。
このときはdev_wdtは/dev/watchdog(/dev/watchdog0)だったので、
これを「ソフトウェアウォッチドッグ」と言っていいのかどうか・・・
そのあとの説明で、
> A./dev/watchdog, /dev/watchdog0 は、ハードウェアウォッチドッグタイマー(相当)であり、タイムアウト値は10秒
というのがありました。

質問はしない、とのことですが・・・
> > localtime()を全てlocaltime_r()に置き換えたら、再起動は発生しませんでした。
> と示しましたが、サンプルソースを、localtime_r()に変更して、確認したら再起動が発生しました。
>
> 当方としては、再起動の理由が欲しかっただけなので、この件での質問はしません。

インターバルタイマのハンドラでセットする
struct tm tim; //時刻(インターバルタイマでセット)
をメインループ側でlocaltime_t()で参照していますけど、
排他制御をしていないのが原因とか・・・

--
なかむら

質問者です。
サンプルソース中の「localtime_t()」への変更でなく。スレッドセーフ版の「localtime_r()」へ変更した結果です。
グローバル変数は止めて、ローカル変数としています。

KES)小西です。

すでに終了していますが、私も気になったので。。。
的外れでしたらすみません。
> 当方としては、再起動の理由が欲しかっただけなので、この件での質問はしません。
の根本原因(ロックの原因)はlocaltime_rではないでしょうか?

> 4. スレッドセーフ版 である localtime_r() をご使用された場合でも再起動は発生してしまいますでしょうか?
使用している関数はシグナルハンドラ内のため、スレッドセーフでもダメです。
http://linuxjm.osdn.jp/html/LDP_man-pages/man7/signal.7.html#lbAK
よりシグナルハンドラ内で使用できる(保証されている)システムコールは限られています。

このため、根本解決をするのであれば、シグナルハンドラ内の処理から一旦pipe等で他のLWP(スレッド等)に通知を行い
そちらでシステムコールのlocaltimeをコールする等かと思います。

すみません、誤記がありました。

> そちらでシステムコールのlocaltimeをコールする等かと思います。

そちらでシステムコールのlocaltime_rをコールする等かと思います。

質問者です

> シグナルハンドラ内で使用できるシステムコールは限られています
根本原因は、シグナルハンドラ内で使用できないlocaltime_rですね。

シグナルハンドラ内で、localtime_rは必須でないのでを外します。
シグナル=イベント=割込 -> スレッドセーフと単純に考えていました。

重要なご指摘ありがとうございました。