5

cocoa アプリが終了する前に、非常に重要な目的 c 操作を実行する必要があるとします (何らかの理由で、クラッシュまたは終了)。

すべてのクラッシュ イベントに応答して、任意の Objective-C コードの実行を保証することは可能ですか? ( SIGINTSIGBUSSIGSEGVなど。「キャッチできないもの」は無視します ( SIGSTOPSIGKILL、稲妻など))


フックが何をしようとしているのかを知っておくと役に立ちます。

例: アプリが動作するためには、変更可能なシステム全体の構成変数 X の値を絶対に変更する必要があるとしましょう。起動時に、アプリは X の現在の状態のスナップショットを取得し、それを変更します。アプリが正常に終了すると、終了する前に X を格納された元の値に戻すだけです。私の質問は次のとおりです。アプリがクラッシュした場合でも、X が確実に復元されるようにすることは可能ですか?

4

3 に答える 3

6

いいえ、特定のコードがアプリケーションの終了時に実行されることを保証する方法はありません。これは、アプリケーションがキャッチできない方法でユーザーがアプリケーションを終了する可能性があるためです (たとえば、デスクトップのプラグを抜くなど)。機械)。

あなたのコードは、予期せぬ終了に対して絶対に強化されなければなりません

どの程度強化する必要があるかは、アプリによって異なります。ドキュメント ベースのアプリ (最新の自動保存頻度モデルより前) では、最後の保存後にデータが失われることが予想されていました (そして面倒でした)。

アプリでは、永続化のコストと永続化されたデータの価値のバランスを取る必要があります。


キャッチできる終了シグナルにのみ興味があります。

「捕まえられる」と「役に立つことならなんでもできる」は違います。お気づきのように、多くの信号をキャッチできます。

ただし、シグナル ハンドラでは実際には何もできません。技術的には、メモリを割り当てることさえできません。

同様に、シグナルまたは例外は通常、アプリケーションが未定義の致命的な破損状態に入ったために生成されます。したがって、アプリケーション内の内部状態が使用可能であることに期待することはできません。

クラッシュが発生したときにアプリがユーザーの状態を保存しようとして、破損した状態を非常に陽気に書き込んでしまい、最終的にユーザーが大幅に多くのデータを失うことになるケースを数多く見てきました。

フックが何をしようとしているのかを知っておくと役に立ちます。


詳細については、sigaction の man ページを参照してください。

具体的には、安全に呼び出せる関数のリストには、_exit()、access()、alarm()、cfgetispeed()、cfgetospeed()、cfsetispeed()、cfsetospeed()、chdir()、chmod()、chown( )、close()、creat()、dup()、dup2()、execle()、execve()、fcntl()、fork()、fpathconf()、fstat()、fsync()、getegid()、 geteuid()、getgid()、getgroups()、getpgrp()、getpid()、getppid()、getuid()、kill()、link()、lseek()、mkdir()、mkfifo()、open( )、pathconf()、pause()、pipe()、raise()、read()、rename()、rmdir()、setgid()、setpgid()、setsid()、setuid()、sigaction()、 sigaddset()、sigdelset()、sigemptyset()、sigfillset()、sigismember()、signal()、sigpending()、sigprocmask()、sigsuspend()、sleep()、stat()、sysconf()、tcdrain( )、tcflow()、tcflush()、tcgetattr()、tcgetpgrp()、tcsendbreak()、tcsetattr()、tcsetpgrp()、time()、times()、umask()、uname()、unlink()、 utime()、wait()、waitpid()、write()、aio_error()、sigpause()、aio_return()、aio_suspend()、sem_post()、sigset()、strcpy()、strcat()、strncpy()、strncat( )、strlcpy()、および strlcat()。

上記のリストにないすべての関数は、シグナルに関して安全でないと見なされます。つまり、シグナルハンドラから呼び出されたときのそのような関数の動作は未定義です。ただし、一般に、シグナル ハンドラーはフラグを設定するだけのことを行う必要があります。他のほとんどのアクションは安全ではありません。

翻訳: シグナル ハンドラーで実際にできることはほとんどありません。

Mpte a;sp tjat sogma; ,au ne de;overed pm amu tjread amd ,ogjt ne de;overed nu 現在実行中の機能を中断しています。

入力しようとしていたこと: シグナル ハンドラーはランダムなスレッドで配信される可能性があり、現在実行中の関数も中断する可能性があることに注意してください。つまり、アプリの状態は多少不確定である可能性があります。

于 2012-09-30T18:46:38.543 に答える
3

短い答えはノーです。

より長い答えはまだいいえですが、システム全体の可変構成変数をリセットできない可能性を最小限に抑えることができます。概要:

  • 起動時に、アプリケーションがフェースレス バックグラウンド プロセスを生成するようにします。
  • 顔の見えないバックグラウンド プロセスは変数を変更し、wait(2)その親の有効期限が切れるまで (& friends のように) 待機する必要があります。
  • 親の有効期限が切れると、変数がリセットされ、それ自体が期限切れになります。

顔のないバックグラウンド アプリは短くてシンプルであるべきです。これは絶対確実というわけではなく、子殺しを考えているユーザーに対して安全というわけではありませんが、変数のリセットを妨害する機会を狭めます。

于 2012-09-30T21:21:28.133 に答える
1

シグナルをキャッチしたい場合は、シグナルハンドラを使用できます。

    signal(SIGBUS, signalHandler);
    signal(SIGSEGV, signalHandler);

void signalHandler(int signal)
{   
    NSMutableString *crashReport = [[NSMutableString alloc] init];
    void* callstack[128];
    int i, frames = backtrace(callstack, 128);
    char** strs = backtrace_symbols(callstack, frames);
    for (i = 0; i < frames; ++i) {
        //printf("%s\n", strs[i]);
        [crashReport appendFormat:@"%s\n",strs[i]];

    }
    free(strs);
    // write crashReport to file
    [crashReport release];
    exit(1);
}  

ただし、クラッシュが NSMutableString の alloc または release または free() 関数にある場合、このコードは機能しません ( bbumで説明されているように:
アプリケーションの終了時に特定のコードが実行されることを保証する方法はありません)

于 2012-10-01T05:25:12.437 に答える