web-dev-qa-db-ja.com

プログラム内からプログラムを再起動することはできますか?

私はC++プログラムを開発していますが、何らかの機能、スクリプト、またはプログラムを再起動するものを使用すると便利です。それは大きなプログラムなので、すべての変数を手動で再起動すると時間がかかります...

これを達成する方法があるかどうか、または可能かどうかはわかりません。

47
Julen Uranga

プログラム全体を再起動する必要がある場合(つまり、再び「閉じる」と「開く」)、「適切な」方法は、メインプログラムを再起動することだけを目的とした別のプログラムを使用することです。自動更新機能を備えた多くのアプリケーションは、この方法で動作します。したがって、メインプログラムを再起動する必要がある場合は、単に「リスタート」プログラムを呼び出して終了します。

61
SingerOfTheFall

main関数でループを使用できます。

int main()
{
    while(!i_want_to_exit_now) {
        // code
    }
}

または、プログラムを実際に再起動する場合は、ハーネスから実行します。

program "$@"
while [ $? -e 42 ]; do
    program "$@"
done

42は、「再起動してください」という意味のリターンコードです。

次に、プログラム内でrestart関数は次のようになります。

void restart() {
    std::exit(42);
}
45
krzaq

Unicies、またはexecveがある他の場所では、 manページで指定 のように機能します。atoiを使用したことで私を殺してください。これは、この種の場合を除いて、一般にひどいからです。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char** argv) {

  (void) argc;

  printf("arg: %s\n", argv[1]);
  int count = atoi(argv[1]);

  if ( getchar() == 'y' ) {

    ++count;

    char buf[20];
    sprintf(buf, "%d", count);

    char* newargv[3];
    newargv[0] = argv[0];
    newargv[1] = buf;
    newargv[2] = NULL;

    execve(argv[0], newargv, NULL);
  }

  return count;
}

例:

$ ./res 1
arg: 1
y
arg: 2
y
arg: 3
y
arg: 4
y
arg: 5
y
arg: 6
y
arg: 7
n

7 | $

(7は戻りコードでした)。

再帰も明示的なループもせず、代わりに自分自身を呼び出して、自分のメモリ空間を新しいバージョンに置き換えます。

このように、スタックはオーバーフローしませんが、以前のすべての変数は再呼び出しと同様に再宣言されます。getchar呼び出しは100%のCPU使用率を防ぎます。

自己更新バイナリの場合、バイナリ全体(少なくともUnix系では、Windowsについてはわかりません)が実行時にメモリにコピーされるため、execve(argv[0], ...呼び出しの前にファイルがディスク上で変更された場合、同じ古いものではなく、ディスク上で見つかった新しいバイナリが代わりに実行されます。

@CarstenSと@bishopがコメントで指摘しているように、Unixが設計された独自の方法により、オープンファイル記述子はfork/execに渡って保持され、その結果、リークを回避します。 execveの呼び出し全体でファイル記述子を開くには、execveの前にそれらを閉じるか、最初にeFD_CLOEXEC/O_CLOEXECで開く必要があります-詳細については、次を参照してください- ダンウォルシュのブログ

15
cat

これは非常にOS固有の質問です。 Windowsでは、 Application Restart API または MFC Restart Manager を使用できます。 Linuxでは、exec()を実行できます

しかし、ほとんどの場合、より良い解決策があります。他の回答で示唆されているように、ループを使用する方が良いでしょう。

これは間違ったアプローチのように聞こえます。すべての状態がグローバルであり、すべてをリセットする唯一の明確な方法は(各変数に「デフォルト」値を手動で割り当てる以外)、プログラム全体を再起動することです。

代わりに、状態はオブジェクト(クラスタイプなど)に保持する必要があります。これらのオブジェクトは、いつでも自由に作成および破棄できます。新しいオブジェクトにはそれぞれ、「デフォルト」値を持つ新しい状態があります。

C++と戦わないでください。これを使って!

おそらくループが必要です:

int main()
{
    while (true)
    {
        //.... Program....
    }
}

再起動する必要があるたびに、ループ内でcontinue;を呼び出し、プログラムを終了するにはbreak;を使用します。

6
Arnav Borborah

リアルタイムシステムを開発するとき、私のアプローチは通常「派生main()」であり、実際のmain()から呼び出されるすべてのコードを記述します。

main.cppプログラム:

int main (int argc, char *argv[])
{
   while (true)
   {
       if (programMain(argc, argv) == 1)
           break;
   }
}

すべてのコードが書かれているprogrammain.cpp:

int programMain(int argc, char *argv[])
{
    // Do whatever - the main logic goes here

    // When you need to restart the program, call
    return 0;

    // When you need to exit the program, call
    return 1;
}

この方法では、プログラムを終了するたびにプログラムが再起動されます。

詳細:すべての変数、グローバル、およびロジックは、programMain()内に記述する必要があります。再起動コントロールを除き、"main()"内には何も記述しないでください。

このアプローチは、LinuxシステムとWindowsシステムの両方で機能します。

4
Mendes

あなたは正しい質問をするためのコーディングについて十分に知らないので、間違った質問をしているように思えます。

あなたが求めているように思えるのは、不在着信時に初期状態にループバックし、通話/ロケーションシーケンス全体を再起動するコードを記述する方法です。この場合、 ステートマシン を使用する必要があります。それが何であるか、そしてそれをどのように書くかを調べてください。これは重要なソフトウェアの概念であり、教師が仕事に長けているかどうかを知っておく必要があります。

サイドノートとして、プログラムがすべての変数を初期化するのに5秒かかる場合、それを再起動すると5秒かかります。それをショートカットすることはできません。そのため、あなたはdo n'tが実際にあなたのプログラムを殺して再起動したい、あなたが望んでいない動作を正確に得るからです。ステートマシンを使用すると、システムの電源がオンになったばかりのコールドスタートアップ用の初期化状態と、ウォームリスタート用の2番目の初期化状態を設定できます。

ああ、6スレッドはそれほど多くありません! :)

2
Graham

あなたがプログラムを「再起動する」という意味によっては、いくつかの簡単な解決策があります。

1つは、プログラム全体を何らかの「プログラム」クラスに埋め込むことです。これにより、適切なプログラムを持つループが本質的に提供されます。プログラムを再起動する必要がある場合は、ループを再び開始する静的パブリックメソッド「Restart」を呼び出します。

また、プログラムを再起動して終了するシステム固有の呼び出しを試みることもできます。他の答えで示唆されているように、この唯一の目的のためにラッパープログラムを作成することができます(そして、終了コードまたは再起動するかどうかを知るためにリターンコードをチェックします)。

他の簡単なオプションは、gotoを使用することです。言及すれば人々が私を嫌うことはわかっていますが、それに直面しましょう。美しい定型文を使用せずに、単純なプログラムを作成したいのです。 元に戻ると破壊が保証されます です。したがって、先頭にラベルを付け、先頭に戻るだけの「再起動」機能を持つプログラムを作成できます。

選択するオプションが何であれ、それを適切に文書化してください。そうすれば、他の人(または将来あなたが)が使用するWTFが1つ少なくなります。

PS。 alain で述べたように、gotoはグローバルオブジェクトも静的オブジェクトも破棄しません。クラスを囲む場合も同様です。したがって、現在のプログラムの代わりに新しいプログラムを開始することを含まないアプローチは、グローバル/静的変数の使用を控えるか、それらを再設定するための適切なアクションをとる必要があります(ただし、各静的/グローバル、再起動ルーチンを変更する必要があります)。

1
MatthewRock