web-dev-qa-db-ja.com

Linuxでデーモンを作成する

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;
}
90
chrisMe

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

デーモンはバックグラウンドで動作し、(通常...)TTYに属していません。そのため、stdout/stderrを思い通りに使用することはできません。通常、syslogデーモン(syslogd)は、メッセージをファイルに記録するために使用されます(デバッグ、エラーなど)。

それに加えて、プロセスをデーモン化するために必要な手順がいくつかあります


私が正しく覚えている場合、これらの手順は次のとおりです。

  • fork親プロセスをオフにし、フォークが成功した場合は終了させます。 ->親プロセスが終了したため、子プロセスはバックグラウンドで実行されます。
  • setsid-新しいセッションを作成します。呼び出しプロセスは、新しいセッションのリーダーになり、新しいプロセスグループのプロセスグループリーダーになります。これで、プロセスは制御端末(CTTY)から切り離されました。
  • キャッチシグナル-シグナルを無視および/または処理します。
  • fork again&セッションをリードするプロセスを確実に取り除くために、親プロセスを終了させます。 (セッションリーダーのみがTTYを再度取得できます。)
  • chdir-デーモンの作業ディレクトリを変更します。
  • mask-デーモンのニーズに応じてファイルモードマスクを変更します。
  • close-親プロセスから継承される可能性のある開いているファイル記述子をすべて閉じます。

出発点を与えるには:基本的な手順を示すこのスケルトンコードを見てください。

/*
 * daemonize.c
 * This example daemonizes a process, writes a few log messages,
 * sleeps 20 seconds and terminates afterwards.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <syslog.h>

static void skeleton_daemon()
{
    pid_t pid;

    /* Fork off the parent process */
    pid = fork();

    /* An error occurred */
    if (pid < 0)
        exit(EXIT_FAILURE);

    /* Success: Let the parent terminate */
    if (pid > 0)
        exit(EXIT_SUCCESS);

    /* On success: The child process becomes session leader */
    if (setsid() < 0)
        exit(EXIT_FAILURE);

    /* Catch, ignore and handle signals */
    //TODO: Implement a working signal handler */
    signal(SIGCHLD, SIG_IGN);
    signal(SIGHUP, SIG_IGN);

    /* Fork off for the second time*/
    pid = fork();

    /* An error occurred */
    if (pid < 0)
        exit(EXIT_FAILURE);

    /* Success: Let the parent terminate */
    if (pid > 0)
        exit(EXIT_SUCCESS);

    /* Set new file permissions */
    umask(0);

    /* Change the working directory to the root directory */
    /* or another appropriated directory */
    chdir("/");

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

    /* Open the log file */
    openlog ("firstdaemon", LOG_PID, LOG_DAEMON);
}
int main()
{
    skeleton_daemon();

    while (1)
    {
        //TODO: Insert daemon code here.
        syslog (LOG_NOTICE, "First daemon started.");
        sleep (20);
        break;
    }

    syslog (LOG_NOTICE, "First daemon terminated.");
    closelog();

    return EXIT_SUCCESS;
}


  • コードをコンパイルします:gcc -o firstdaemon daemonize.c
  • デーモンを開始します:./firstdaemon
  • すべてが正常に機能しているかどうかを確認します:ps -xj | grep firstdaemon

  • 出力は次のようになります。

 + ------ + ------ + ------ + ------ + ----- + ------- +- ---- + ------ + ------ + ----- + 
 | PPID | PID | PGID | SID | TTY | TPGID | STAT | UID |時間| CMD | 
 + ------ + ------ + ------ + ------ + ----- + ------- + ------ + ------ + ------ + ----- + 
 | 1 | 3387 | 3386 | 3386 | ? | -1 | S | 1000 | 0:00 | ./ | 
 + ------ + ------ + ------ + ------ + ----- + ------- + ------ + ------ + ------ + ----- + 

ここに表示されるものは:

  • デーモンには制御端末がありません(TTY =?
  • 親プロセスID(PPID)は1(initプロセス)
  • PID!= SIDは、プロセスがセッションリーダーではないことを意味します
    (2番目のfork()のため)
  • PID!=私たちのプロセスをSIDするためTTYを再び制御することはできません

syslogの読み取り:

  • Syslogファイルを見つけます。私のものはここにあります:/var/log/syslog
  • A:grep firstdaemon /var/log/syslog

  • 出力は次のようになります。

 firstdaemon [3387]:最初のデーモンが起動しました。
 firstdaemon [3387]:最初のデーモンが終了しました。


A note:実際には、シグナルハンドラを実装し、ログ(ファイル、ログレベルなど)を適切に設定することもできます。

さらに読む:

184
Pascal Werkl

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

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

  1. 従来の SysV デーモン( old-style )、
  2. systemd デーモン( new-style )。

SysVデーモン

従来の SysV デーモンに興味がある場合は、 以下の手順 を実装する必要があります。

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

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

BSD daemon() 関数は使用しないでください。これはサブセットのみを実装するためですこれらのステップの。

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

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


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

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

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

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

28
patryk.beza

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は、シグナルをキャッチして無視することを示していることに注意してください。シグナルの受信をログに記録し、フラグ(正常なシャットダウンを示すフラグなど)を設定できるシグナルハンドラを構築できます。

7
ChuckCottrill

最初の要件「停止できないデーモン...」で停止できます。

私の友人は不可能だ。ただし、はるかに優れたツールであるカーネルモジュールを使用して同じことを実現できます。

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

すべてのデーモンを停止できます。他のものより簡単に停止されるものもあります。パートナーがホールドダウンし、失われた場合にパートナーを再スポーンするデーモンペアでさえも停止できます。あなたはそれで少し一生懸命働く必要があります。

5
Edwin Buck

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

#include <unistd.h>

int daemon(int nochdir, int noclose);

manページ から:

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

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

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

5
weiyin

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

{
  ".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

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

5
danday74

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

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

2
Doug Morrow

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

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

1
Magn3s1um