130

Linux では、停止できず、ファイルシステムの変更を監視するデーモンを追加したいと考えています。変更が検出された場合は、開始されたコンソールへのパスと改行を書き込む必要があります。

ファイルシステムを変更するコードはほぼ準備できていますが、デーモンの作成方法がわかりません。

私のコードはここからです: http://www.yolinux.com/TUTORIALS/ForkExecProcesses.html

フォークの後に何をしますか?

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

  pid_t pID = fork();
  if (pID == 0)  {              // child
          // Code only executed by child process    
      sIdentifier = "Child Process: ";
    }
    else if (pID < 0) {
        cerr << "Failed to fork" << endl;
        exit(1);
       // Throw exception
    }
    else                                   // parent
    {
      // Code only executed by parent process

      sIdentifier = "Parent Process:";
    }       

    return 0;
}
4

9 に答える 9

36

man 7 daemonデーモンの作成方法を詳細に説明します。私の答えは、このマニュアルからの抜粋です。

少なくとも 2 種類のデーモンがあります。

  1. 従来のSysVデーモン (古いスタイル)、
  2. systemdデーモン ( new-style )。

SysV デーモン

従来のSysVデーモンに関心がある場合は、次の手順を実装する必要があります。

  1. 標準入力出力、およびエラー(つまり、最初の 3 つのファイル記述子 0、1、2)を除く、開いているすべてのファイル記述子を閉じます。これにより、誤って渡されたファイル記述子がデーモン プロセスに残ることがなくなります。Linux では、これは/proc/self/fd、ファイル記述子 3 からgetrlimit()forによって返される値までの反復のフォールバックを使用して、 を反復することによって最適に実装されますRLIMIT_NOFILE
  2. すべてのシグナル ハンドラをデフォルトにリセットします。これは、利用可能な信号を の制限まで反復し、_NSIGそれらを にリセットすることによって行うのが最適SIG_DFLです。
  3. を使用して信号マスクをリセットしsigprocmask()ます。
  4. 環境ブロックをサニタイズし、デーモンのランタイムに悪影響を与える可能性のある環境変数を削除またはリセットします。
  5. を呼び出しfork()て、バックグラウンド プロセスを作成します。
  6. setsid()子では、任意の端末から切り離し、独立したセッションを作成するために呼び出します。
  7. 子プロセスで再度呼び出しfork()て、デーモンが端末を再取得できないようにします。
  8. 最初の子を呼び出しexit()て、2 番目の子 (実際のデーモン プロセス) だけが残るようにします。これにより、すべてのデーモンがそうであるように、デーモン プロセスが init/PID 1 に再親化されることが保証されます。
  9. デーモン プロセスでは/dev/null、標準入力出力、およびエラーに接続します。
  10. デーモン プロセスでは、 を 0 にリセットして、などにumask渡されるファイル モードが、作成されたファイルとディレクトリのアクセス モードを直接制御するようにします。open()mkdir()
  11. デーモン プロセスで、現在のディレクトリをルート ディレクトリ ( ) に変更/して、デーモンがマウント ポイントのアンマウントを不本意にブロックしないようにします。
  12. デーモン プロセスで、デーモンPID ( によって返されるgetpid()) を PID ファイルに書き込みます。たとえば、/run/foobar.pid(架空のデーモン「foobar」の場合) デーモンを複数回起動できないようにします。これは、PID ファイルに以前に保存された PID がもはや存在しないか、外部プロセスに属していないことが同時に検証されたときにのみ PID ファイルが更新されるように、競合のない方法で実装する必要があります。
  13. デーモン プロセスで、可能で該当する場合は特権を削除します。
  14. デーモンプロセスから、初期化が完了したことを開始した元のプロセスに通知します。これは、名前のないパイプまたは同様の通信チャネルを介して実装できます。この通信チャネルは、最初のプロセスの前に作成されるfork()ため、元のプロセスとデーモン プロセスの両方で使用できます。
  15. exit()元のプロセスを呼び出します。デーモンを呼び出したプロセスは、初期化が完了し、すべての外部通信チャネルが確立されてアクセス可能になったexit()にこれが発生することに依存できる必要があります。

次の警告に注意してください。

BSDdaemon()関数は、これらの手順のサブセットのみを実装するため、使用しないでください。

SysV システムとの互換性を提供する必要があるデーモンは、上記のスキームを実装する必要があります。ただし、デバッグを容易にし、systemd を使用してシステムへの統合を簡素化するために、この動作をオプションにしてコマンドライン引数を介して構成可能にすることをお勧めします。

POSIXdaemon()に準拠していないことに注意してください。


新しいスタイルのデーモン

新しいスタイルのデーモンの場合、次の手順が推奨されます。

  1. を受信した場合SIGTERMは、デーモンをシャットダウンして正常に終了します。
  2. が受信された場合、該当する場合SIGHUPは構成ファイルをリロードします。
  3. メイン デーモン プロセスから正しい終了コードを提供します。これは、サービス エラーや問題を検出するために init システムによって使用されます。SysV init スクリプトの LSB 推奨事項で定義されている終了コード スキームに従うことをお勧めします。
  4. 可能であれば、D-Bus IPCシステムを介してデーモンの制御インターフェースを公開し、初期化の最後のステップとしてバス名を取得します。
  5. systemd に統合するには、デーモンの起動、停止、その他のメンテナンスに関する情報を含む .service ユニットファイルを提供します。詳細systemd.service(5)については、を参照してください。
  6. 可能な限り、init システムの機能に依存して、デーモンのアクセスをファイル、サービス、およびその他のリソースに制限します。つまり、systemd の場合は、独自のリソース制限制御を実装する代わりに、 systemd のリソース制限制御に依存し、systemd の特権ドロップに依存します。デーモンなどに実装する代わりにコードを使用します。systemd.exec(5)使用可能なコントロールについては、を参照してください。
  7. D-Busを使用する場合は、D-Bus サービス アクティベーション構成ファイルを指定して、デーモンをバス アクティベート可能にします。これには複数の利点があります。デーモンをオンデマンドで遅延起動できます。それを必要とする他のデーモンと並行して開始することができます。これにより、並行化と起動速度が最大化されます。バスはアクティブ化可能なサービスの要求をキューに入れるため、バス要求を失うことなくデーモンを再起動できます。詳細については、以下を参照してください。
  8. デーモンがソケットを介して他のローカル プロセスまたはリモート クライアントにサービスを提供する場合は、以下に示すスキームに従って、ソケットをアクティブ化できるようにする必要あります。D-Bus アクティベーションと同様に、これにより、サービスのオンデマンド開始が可能になるだけでなく、サービス開始の並列化が改善されます。また、ステートレス プロトコル (syslog、DNS など) の場合、ソケットベースのアクティベーションを実装するデーモンは、単一の要求を失うことなく再起動できます。詳細については、以下を参照してください。
  9. 該当する場合、デーモンはsd_notify(3)インターフェースを介して、起動の完了またはステータスの更新について init システムに通知する必要があります。
  10. syslog()呼び出しを使用してシステム syslog サービスに直接ログを記録する代わりに、新しいスタイルのデーモンは、単に を介して標準エラーにログを記録することを選択できますfprintf()。これは、init システムによって syslog に転送されます。printk()ログ レベルが必要な場合は、Linux カーネルのレベル システムと同様のスタイルに従って、個々のログ行に "<4>" (syslog 優先度スキームのログ レベル 4 "WARNING" の場合) などの文字列をプレフィックスとして付けることでエンコードできます。詳細については、 および を参照sd-daemon(3)してくださいsystemd.exec(5)

詳細については、全体をお読みくださいman 7 daemon

于 2016-08-07T20:30:05.733 に答える
11

強制終了できないプロセスを Linux で作成することはできません。root ユーザー (uid=0) はプロセスにシグナルを送信できますが、キャッチできないシグナルが 2 つあります (SIGKILL=9、SIGSTOP=19)。また、他のシグナル (キャッチされなかった場合) によってもプロセスが終了する可能性があります。

プログラム/デーモンの名前と、プログラムを実行するためのパス (おそらく "/" または "/tmp") を指定できる、より一般的なデーモン化機能が必要な場合があります。また、stderr および stdout (および場合によっては stdin を使用した制御パス) にファイルを提供することもできます。

必要なインクルードは次のとおりです。

#include <stdio.h>    //printf(3)
#include <stdlib.h>   //exit(3)
#include <unistd.h>   //fork(3), chdir(3), sysconf(3)
#include <signal.h>   //signal(3)
#include <sys/stat.h> //umask(3)
#include <syslog.h>   //syslog(3), openlog(3), closelog(3)

そして、これはより一般的な関数です。

int
daemonize(char* name, char* path, char* outfile, char* errfile, char* infile )
{
    if(!path) { path="/"; }
    if(!name) { name="medaemon"; }
    if(!infile) { infile="/dev/null"; }
    if(!outfile) { outfile="/dev/null"; }
    if(!errfile) { errfile="/dev/null"; }
    //printf("%s %s %s %s\n",name,path,outfile,infile);
    pid_t child;
    //fork, detach from process group leader
    if( (child=fork())<0 ) { //failed fork
        fprintf(stderr,"error: failed fork\n");
        exit(EXIT_FAILURE);
    }
    if (child>0) { //parent
        exit(EXIT_SUCCESS);
    }
    if( setsid()<0 ) { //failed to become session leader
        fprintf(stderr,"error: failed setsid\n");
        exit(EXIT_FAILURE);
    }

    //catch/ignore signals
    signal(SIGCHLD,SIG_IGN);
    signal(SIGHUP,SIG_IGN);

    //fork second time
    if ( (child=fork())<0) { //failed fork
        fprintf(stderr,"error: failed fork\n");
        exit(EXIT_FAILURE);
    }
    if( child>0 ) { //parent
        exit(EXIT_SUCCESS);
    }

    //new file permissions
    umask(0);
    //change to path directory
    chdir(path);

    //Close all open file descriptors
    int fd;
    for( fd=sysconf(_SC_OPEN_MAX); fd>0; --fd )
    {
        close(fd);
    }

    //reopen stdin, stdout, stderr
    stdin=fopen(infile,"r");   //fd=0
    stdout=fopen(outfile,"w+");  //fd=1
    stderr=fopen(errfile,"w+");  //fd=2

    //open syslog
    openlog(name,LOG_PID,LOG_DAEMON);
    return(0);
}

これは、デーモンになり、ぶらぶらしてから去るサンプルプログラムです。

int
main()
{
    int res;
    int ttl=120;
    int delay=5;
    if( (res=daemonize("mydaemon","/tmp",NULL,NULL,NULL)) != 0 ) {
        fprintf(stderr,"error: daemonize failed\n");
        exit(EXIT_FAILURE);
    }
    while( ttl>0 ) {
        //daemon code here
        syslog(LOG_NOTICE,"daemon ttl %d",ttl);
        sleep(delay);
        ttl-=delay;
    }
    syslog(LOG_NOTICE,"daemon ttl expired");
    closelog();
    return(EXIT_SUCCESS);
}

SIG_IGN は、シグナルをキャッチして無視することを示していることに注意してください。シグナルの受信をログに記録し、フラグ (正常なシャットダウンを示すフラグなど) を設定できるシグナル ハンドラーを作成できます。

于 2014-01-28T23:36:02.583 に答える
9

関数を使用してみてくださいdaemon

#include <unistd.h>

int daemon(int nochdir, int noclose);

マニュアルページから:

daemon() 関数は、制御端末から自分自身を切り離し、システム デーモンとしてバックグラウンドで実行したいプログラム用です。

nochdir がゼロの場合、daemon() は呼び出しプロセスの現在の作業ディレクトリをルート ディレクトリ ("/") に変更します。それ以外の場合、現在の作業ディレクトリは変更されません。

noclose がゼロの場合、daemon() は標準入力、標準出力、および標準エラーを /dev/null にリダイレクトします。それ以外の場合、これらのファイル記述子は変更されません。

于 2015-03-12T03:49:34.873 に答える
6

第一条件「止められないデーモン…」で止められる

ありえないわ、友よ。ただし、はるかに優れたツールであるカーネル モジュールを使用して同じことを実現できます。

http://www.infoq.com/articles/inotify-linux-file-system-event-monitoring

すべてのデーモンを停止できます。他のものよりも簡単に止められるものもあります。ホールドダウン状態のパートナーとのデーモンペアでさえ、失われた場合にパートナーをリスポーンして停止することができます. もう少し頑張るしかない。

于 2013-07-30T19:20:24.347 に答える
6

アプリが次のいずれかの場合:

{
  ".sh": "bash",
  ".py": "python",
  ".rb": "ruby",
  ".coffee" : "coffee",
  ".php": "php",
  ".pl" : "perl",
  ".js" : "node"
}

NodeJSの依存関係を気にしないで、NodeJSをインストールしてから:

npm install -g pm2

pm2 start yourapp.yourext --name "fred" # where .yourext is one of the above

pm2 start yourapp.yourext -i 0 --name "fred" # run your app on all cores

pm2 list

再起動時にすべてのアプリを実行し続ける (および pm2 をデーモン化する) には:

pm2 startup

pm2 save

次のことができるようになりました。

service pm2 stop|restart|start|status

(また、アプリ ディレクトリのコード変更を簡単に監視し、コード変更が発生したときにアプリ プロセスを自動再起動できます)

于 2017-01-29T03:17:57.503 に答える
3

fork() を呼び出すと、子プロセスが作成されます。fork が成功した場合 (fork がゼロ以外の PID を返した場合)、子プロセス内からこの時点から実行が続行されます。この場合、親プロセスを正常に終了してから、子プロセスで作業を続行します。

多分これが役立つでしょう: http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html

于 2013-07-30T18:32:54.193 に答える
2

デーモンは、バックグラウンドでの単なるプロセスです。Linux で OS の起動時にプログラムを開始する場合は、開始コマンドを /etc/rc.d/rc.local (他のすべてのスクリプトの後に実行) または /etc/startup.sh に追加します。

Windows では、サービスを作成してサービスを登録し、管理 -> サービス パネルで起動時に自動的に開始するように設定します。

于 2013-07-30T18:22:57.537 に答える