14

コマンドラインから起動された実行中のCまたはC++プログラムをバックグラウンドに置くための最良の方法は何ですか?これは、ユーザーがコマンドの最後に「&」を付けてUNIXシェルから起動した場合と同じです。(ただし、ユーザーは必要ありませんでした。)これはGUIアプリであり、シェルI / Oを必要としないため、起動後にシェルを拘束する理由はありません。しかし、シェルコマンドの起動を「&」なしで(またはWindowsで)自動バックグラウンド化する必要があります。

理想的には、Linux、OS X、およびWindowsのいずれでも機能するソリューションが必要です。(または、#ifdefで選択できる個別のソリューション。)これは、実行の途中ではなく、実行の最初に実行する必要があると想定してもかまいません。

1つの解決策は、メインプログラムを、実際のバイナリを起動するスクリプトにして、慎重にバックグラウンドに配置することです。しかし、これらの結合されたシェル/バイナリペアが必要なのは満足のいくものではないようです。

別の解決策は、同じコマンドライン引数を使用して、実行された別のバージョン('system'またはCreateProcessを使用)をすぐに起動することですが、子をバックグラウンドに置き、親を終了させます。しかし、これは、それ自体をバックグラウンドに置くプロセスと比較すると、不格好に思えます。

いくつかの回答の後に編集:はい、fork()(またはsystem()、またはWindowsのCreateProcess)は、これを行うための1つの方法であり、元の質問で示唆しました。ただし、これらのソリューションはすべて、バックグラウンドで実行される2番目のプロセスを作成してから、元のプロセスを終了します。EXISTINGプロセスをバックグラウンドに置く方法があるかどうか疑問に思いました。1つの違いは、アプリがプロセスIDを記録したスクリプトから起動された場合(おそらく後で強制終了するなどの目的で)、新しくフォークまたは作成されたプロセスは異なるIDを持つため、起動スクリプトで制御できないことです。あなたは私が何をしているのかわかります。

編集#2

fork()は、OS Xの適切なソリューションではありません。「fork」のマニュアルページには、特定のフレームワークまたはライブラリが使用されている場合は安全ではないと記載されています。試してみましたが、実行時にアプリが大声で文句を言います。「プロセスが分岐し、このCoreFoundation機能を安全に使用できません。exec()を実行する必要があります。」

私はdaemon()に興味をそそられましたが、OS Xで試してみると同じエラーメッセージが表示されたので、fork()の単なるラッパーであり、同じ制限があると思います。

OS Xの中道政治を失礼します、それはちょうど今私の目の前のシステムです。しかし、私は確かに3つのプラットフォームすべてに対する解決策を探しています。

4

20 に答える 20

11

私のアドバイス:少なくとも Linux/UNIX では、これを行わないでください。

Linux/UNIX での GUI プログラムは、伝統的に自動バックグラウンドを実行しません。これは初心者にとっては面倒かもしれませんが、多くの利点があります。

  • デバッグが必要なコア ダンプやその他の問題が発生した場合に、標準エラーを簡単に取得できます。

  • シェル スクリプトでプログラムを簡単に実行し、完了するまで待機できるようにします。

  • シェル スクリプトがプログラムをバックグラウンドで実行し、そのプロセス ID を取得することを容易にします。

    gui-program &
    pid=$!
    # do something with $pid later, such as check if the program is still running
    

    プログラムが自分自身を fork すると、この動作は壊れます。

「スクリプト可能性」は非常に多くの予期しない状況で役立ちます。GUI プログラムであっても、これらの動作を明示的に壊すことはためらいます。

Windows は別の話です。私の知る限り、Windows プログラムは、コマンド ウィンドウへのアクセスを明示的に要求しない限り、(コマンド シェルから呼び出された場合でも) バックグラウンドで自動的に実行されます。

于 2008-09-23T00:14:42.007 に答える
9

Linux では、あなたが正しく理解していれば、 daemon()が探しているものです。

于 2008-09-22T20:09:50.270 に答える
7

UnixライクなOSで通常行われる方法は、最初にfork()を実行し、親を終了することです。これはWindowsでは機能しませんが、フォークが存在する別のプロセスを起動するよりもはるかに洗練されています。

于 2008-09-22T18:46:05.167 に答える
6

3 つのことを行う必要があります。

fork
setsid
redirect STDIN, STDOUT and STDERR to /dev/null

これはPOSIXシステムに適用されます(あなたが言及したものはすべてPOSIXであると主張しています(ただし、Windowsは主張ビットで停止します))

于 2008-09-22T19:05:17.167 に答える
4

プロセスは、バックグラウンドとフォアグラウンドを担当するものではないため、バックグラウンドに配置することはできません。これが、プロセスの終了を待機しているシェルになります。最後にアンパサンド「&」を付けてプロセスを起動すると、シェルはプロセスの終了を待機しません。

ただし、プロセスがシェルから脱出できる唯一の方法は、別の子をフォークして、元の自己を終了して待機中のシェルに戻すことです。

シェルから、Control-Zを使用してプロセスをバックグラウンドで実行し、「bg」と入力できます。

于 2008-09-22T19:23:20.320 に答える
4

UNIX では、2 回続けて fork し、親を死なせる必要があります。

于 2008-09-22T18:56:15.567 に答える
3

プロセスのバックグラウンドはシェル関数であり、OS関数ではありません。

アプリをバックグラウンドで起動する場合、一般的なトリックは、アプリをバックグラウンドで起動するシェルスクリプトを作成して起動することです。

#! /bin/sh
/path/to/myGuiApplication &
于 2008-09-22T19:24:02.737 に答える
2

編集した質問をフォローアップするには:

EXISTINGプロセスをバックグラウンドに置く方法があるかどうか疑問に思いました。

UnixライクなOSでは、私が知っているようにこれを行う方法は実際にはありません。シェルは、子プロセスが終了するのを待って、wait()呼び出しのバリアントの1つを実行しているため、ブロックされています。子プロセスを実行し続ける方法はありませんが、どういうわけか、シェルのwait()を「監視を停止してください」というステータスで返します。子フォークがあり、元のフォークを終了する理由は、シェルがwait()から戻るためです。

于 2008-09-22T19:10:58.493 に答える
2

Linux/UNIXの擬似コードは次のとおりです。

initialization_code()
if(failure) exit(1)
if( fork() > 0 ) exit(0)
setsid()
setup_signal_handlers()
for(fd=0; fd<NOFILE; fd++) close(fd)
open("/dev/null", O_RDONLY)
open("/dev/null", O_WRONLY)
open("/dev/null", o_WRONLY)
chdir("/")

そしておめでとうございます。あなたのプログラムは、TTYを制御することなく、また標準的な入力または出力なしで、独立した「デーモン化された」プロセスとして継続します。

これで、Windowsでは、プログラムをmain()ではなくWinMain()を使用してWin32アプリケーションとしてビルドするだけで、コンソールなしで自動的に実行されます。サービスとして実行したい場合は、それを調べる必要があります。これは、私がサービスを作成したことがなく、それらがどのように機能するかがよくわからないためです。

于 2008-09-22T19:18:03.673 に答える
2

質問を編集しましたが、質問が一種の構文エラーであるという点を見逃している可能性があります。プロセスが最初からバックグラウンドに置かれておらず、PIDを同じままにしておきたい場合は、次のことができます。プロセスを開始したプログラムがそのPIDを待機しているという事実を無視します。これは、ほとんどフォアグラウンドにあること の定義です。

なぜバックグラウンドに何かを置き、PIDを同じに保ちたいのかを考える必要があると思います。おそらく、これらの制約の両方は必要ないことをお勧めします。

于 2008-09-23T01:50:06.147 に答える
1

バックグラウンド処理の最も単純な形式は次のとおりです。

if (fork() != 0) exit(0);

Unix で、tty からの関連付け解除を完全にバックグラウンドで行いたい場合は、次のようにします。

  1. tty にアクセスする可能性のあるすべての記述子 (通常は01、および2) を閉じます。
  2. if (fork() != 0) exit(0);
  3. setpgroup(0,getpid()); /* Might be necessary to prevent a SIGHUP on shell exit. */
  4. signal(SIGHUP,SIG_IGN); /* just in case, same as using nohup to launch program. */
  5. fd=open("/dev/tty",O_RDWR);
  6. ioctl(fd,TIOCNOTTY,0); /* Disassociates from the terminal */
  7. close(fd);
  8. if (fork() != 0) exit(0); /* just for good measure */

これにより、プログラムが完全にデーモン化されます。

于 2008-09-22T19:08:32.180 に答える
1

他の人が述べたように、fork() は *nix でそれを行う方法です。Windows では、MingW または Cygwin ライブラリを使用して fork() を取得できます。ただし、これらの場合、GCC をコンパイラとして使用するように切り替える必要があります。

純粋な Windows の世界では、CreateProcess (またはその派生の CreateProcessAsUser、CreateProcessWithLogonW のいずれか) を使用します。

于 2008-09-22T18:54:33.760 に答える
0

私は解決策を試していました。

親プロセスから必要なフォークは1つだけです。

最も重要な点は、フォークの後、親プロセスは呼び出すことによってではなく、呼び出すことによって死ぬ必要があるという_exit(0);ことですexit(0);

を使用する_exit(0);と、コマンドプロンプトはすぐにシェルに戻ります。

これがトリックです。

于 2010-03-24T10:21:09.377 に答える
0

Windowsでは、fork()に到達する最後のことは、プログラムをWindowsサービスとしてロードすることだと思います。

これがWindowsサービスの紹介記事へのリンクです... CodeProject:シンプルなWindowsサービスのサンプル

于 2008-09-22T19:50:05.950 に答える
0

The most common way of doing this under Linux is via forking. The same should work on Mac, as for Windows I'm not 100% sure but I believe they have something similar.

Basically what happens is the process splits itself into two processes, and then the original one exits (returning control to the shell or whatever), and the second process continues to run in the background.

于 2008-09-22T18:47:34.400 に答える
0

Unixでは、を使用してそれを行うことを学びましたfork()。実行中のプロセスをバックグラウンドに配置する場合は、fork2回実行します。

于 2009-02-23T12:28:03.370 に答える
0

したがって、あなたが言うように、 fork()ing だけではうまくいきません。次のコード サンプルのように、fork() と re-exec() を実行する必要があります。

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

#include <CoreFoundation/CoreFoundation.h>

int main(int argc, char **argv)
{
    int i, j;

    for (i=1; i<argc; i++)
        if (strcmp(argv[i], "--daemon") == 0)
        {
            for (j = i+1; j<argc; j++)
                argv[j-1] = argv[j];

            argv[argc - 1] = NULL;

            if (fork()) return 0;

            execv(argv[0], argv);

            return 0;
        }


    sleep(1);

    CFRunLoopRun();

    CFStringRef hello = CFSTR("Hello, world!");

    printf("str: %s\n", CFStringGetCStringPtr(hello, CFStringGetFastestEncoding(hello)));

    return 0;
}

ループは --daemon 引数をチェックすることです。存在する場合は、再実行する前に削除して、無限ループを回避します。

argv[0] は必ずしもフルパスではないため、バイナリがパスに配置されている場合、これは機能しないと思います。そのため、変更する必要があります。

于 2008-09-22T22:48:58.097 に答える
0

Windows についてはわかりませんが、UNIX のようなシステムではfork()setsid()フォークされたプロセスを端末に接続されていない新しいプロセス グループに移動できます。

于 2008-09-22T19:05:06.637 に答える
0

プログラムの PID を取得するスクリプトが必要な場合でも、フォーク後に取得できます。

fork するときは、子の PID を親プロセスに保存します。親プロセスを終了するときは、PID を に出力するSTD{OUT,ERR}か、単にreturn pid;ステートメントを の最後に置きmain()ます。その後、呼び出し元のスクリプトはプログラムの pid を取得できますが、プログラムがどのように動作するかについてある程度の知識が必要です。

于 2008-09-22T19:07:24.900 に答える
0
/**Deamonize*/

pid_t pid;
pid = fork(); /**father makes a little deamon(son)*/
if(pid>0)
exit(0); /**father dies*/
while(1){
printf("Hello I'm your little deamon %d\n",pid); /**The child deamon goes on*/
sleep(1)
}

/** try 'nohup' in linux(usage: nohup <command> &) */
于 2008-11-29T14:33:13.130 に答える