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;
}
Linuxでは、停止できず、ファイルシステムの変更を監視するデーモンを追加します。変更が検出された場合、開始されたコンソールへのパスと改行を書き込む必要があります。
デーモンはバックグラウンドで動作し、(通常...)TTYに属していません。そのため、stdout/stderrを思い通りに使用することはできません。通常、syslogデーモン(syslogd)は、メッセージをファイルに記録するために使用されます(デバッグ、エラーなど)。
それに加えて、プロセスをデーモン化するために必要な手順がいくつかあります。
私が正しく覚えている場合、これらの手順は次のとおりです。
出発点を与えるには:基本的な手順を示すこのスケルトンコードを見てください。
/*
* 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 | ./ | + ------ + ------ + ------ + ------ + ----- + ------- + ------ + ------ + ------ + ----- +
ここに表示されるものは:
syslogの読み取り:
/var/log/syslog
A:grep firstdaemon /var/log/syslog
出力は次のようになります。
firstdaemon [3387]:最初のデーモンが起動しました。 firstdaemon [3387]:最初のデーモンが終了しました。
A note:実際には、シグナルハンドラを実装し、ログ(ファイル、ログレベルなど)を適切に設定することもできます。
さらに読む:
man 7 daemon
デーモンの作成方法を詳細に説明しています。私の答えはこのマニュアルからの抜粋です。
少なくとも2種類のデーモンがあります。
従来の SysV デーモンに興味がある場合は、 以下の手順 を実装する必要があります。
- 標準input、output、およびerror(を除くすべての開いているファイル記述子を閉じます(すなわち、最初の3つのファイル記述子0、1、2)。これにより、誤って渡されたファイル記述子がデーモンプロセス内に留まることがなくなります。 Linuxでは、これは、
/proc/self/fd
を反復処理することにより、ファイル記述子3からgetrlimit()
によって返される値へのフォールバックを使用して、RLIMIT_NOFILE
に対して最適に実装されます。- リセット すべてのシグナルハンドラをデフォルトに戻します。これは、
_NSIG
の制限まで利用可能な信号を繰り返し処理し、それらをSIG_DFL
にリセットすることにより最適に実行されます。sigprocmask()
を使用して信号マスクをリセットします。- 環境ブロックをサニタイズし、デーモンのランタイムに悪影響を与える可能性のある環境変数を削除またはリセットします。
fork()
を呼び出して、バックグラウンドプロセスを作成します。- 子では、
setsid()
を呼び出して任意の端末からデタッチし、独立した session を作成します。- 子で、
fork()
を再度呼び出して、デーモンが端末を再度再取得できないようにします。- 最初の子で
exit()
を呼び出すと、2番目の子(実際のデーモンプロセス)のみが残ります。これにより、すべてのデーモンがそうであるように、デーモンプロセスがinit/PID 1に再ペアレント化されます。- デーモンプロセスで、
/dev/null
を標準input、output、およびエラー。- デーモンプロセスで、
umask
を0にリセットして、ファイルモードがopen()
、mkdir()
に渡されるなどして、アクセスモードを直接制御します。作成されたファイルとディレクトリ。- デーモンプロセスで、 change ルートディレクトリ(
/
)への現在のディレクトリ。これは、デーモンがマウントポイントのアンマウントを意図せずにブロックすることを避けるためです。- デーモンプロセスで、デーモン PID (
getpid()
によって返される)をPIDファイルに書き込みます。たとえば、/run/foobar.pid
(架空のデーモン "foobar"の場合)デーモンを複数回起動できないこと。これは、PIDファイルに以前に保存されたPIDが存在しないか、外部プロセスに属していないことが同時に確認された場合にのみPIDファイルが更新されるように、競合のない方法で実装する必要があります。- デーモンプロセスで、可能であれば適用可能な特権を削除します。
- デーモンプロセスから、初期化が完了したことを開始した元のプロセスに通知します。これは、名前のないパイプまたは最初の
fork()
の前に作成され、元のプロセスとデーモンプロセスの両方で使用可能な同様の通信チャネルを介して実装できます。- 元のプロセスで
exit()
を呼び出します。デーモンを呼び出したプロセスは、このexit()
が発生することを信頼できる必要がありますafter初期化が完了し、すべての外部通信チャネル確立され、アクセス可能です。
この警告に注意してください。
BSD
daemon()
関数は使用しないでください。これはサブセットのみを実装するためですこれらのステップの。SysVシステムとの互換性を提供する必要があるデーモンは、上記のスキームを実装する必要があります。ただし、デバッグを容易にし、systemdを使用するシステムへの統合を簡素化するために、この動作をオプションにし、コマンドライン引数で構成できるようにすることをお勧めします。
daemon()
は POSIX に準拠していないことに注意してください。
新しいスタイルのデーモンの場合、 以下の手順 が推奨されます。
SIGTERM
を受け取った場合は、デーモンをシャットダウンして、正常に終了します。SIGHUP
を受け取った場合、これが該当する場合、構成ファイルを再ロードします。- これは、initシステムがサービスのエラーと問題を検出するために使用されるため、メインデーモンプロセスから正しい終了コードを提供します。 SysV initスクリプトに関するLSBの推奨事項 で定義されている終了コードスキームに従うことをお勧めします。
- 可能かつ適用可能な場合は、 D-Bus IPC システムを介してデーモンの制御インターフェースを公開し、初期化の最後のステップとしてバス名を取得します。
- Systemdに統合するには、デーモンの起動、停止、その他の保守に関する情報を含む 。servicenit ファイルを提供します。詳細については
systemd.service(5)
をご覧ください。- 可能な限り、initシステムの機能に依存して、ファイル、サービス、およびその他のリソースへのデーモンのアクセスを制限します。つまり、systemdの場合、systemdの resource limit control を実装する代わりに独自に、デーモンに実装するのではなく、systemdの privilege drop コードに依存します。利用可能なコントロールについては
systemd.exec(5)
をご覧ください。- D-Bus が使用されている場合は、D-Busサービスのアクティベーション 設定ファイル を指定して、デーモンをバス起動可能にします。これには複数の利点があります:デーモンはオンデマンドで遅延起動される場合があります。それを必要とする他のデーモンと並行して起動できます。これにより、並列化と ブートアップ速度 が最大化されます。バスは起動可能なサービスの要求をキューに入れるため、バス要求を失うことなく、障害時にデーモンを再起動できます。詳細については below をご覧ください。
- デーモンがソケットを介して他のローカルプロセスまたはリモートクライアントにサービスを提供する場合、指摘されたスキームに従って socket-activatable にする必要があります below 。 D-Busアクティベーションと同様に、これにより、サービスのオンデマンド起動が可能になり、サービス起動の並列化が改善されます。また、ステートレスプロトコル(syslog、DNSなど)の場合、ソケットベースのアクティベーションを実装するデーモンは、単一の要求を失うことなく再起動できます。詳細については below をご覧ください。
- 該当する場合、デーモンは
sd_notify(3)
インターフェイスを介して、起動完了またはステータスの更新についてinitシステムに通知する必要があります。syslog()
を使用してシステムsyslogサービスに直接ログを記録する代わりに、新しいスタイルのデーモンはfprintf()
を介して標準エラーに単純に記録することを選択できます。 initシステムによるsyslog。ログレベルが必要な場合は、Linuxカーネルのprintk()
レベルシステム。詳細については、sd-daemon(3)
およびsystemd.exec(5)
を参照してください。
詳細については、 man 7 daemon
全体をお読みください。
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は、シグナルをキャッチして無視することを示していることに注意してください。シグナルの受信をログに記録し、フラグ(正常なシャットダウンを示すフラグなど)を設定できるシグナルハンドラを構築できます。
最初の要件「停止できないデーモン...」で停止できます。
私の友人は不可能だ。ただし、はるかに優れたツールであるカーネルモジュールを使用して同じことを実現できます。
http://www.infoq.com/articles/inotify-linux-file-system-event-monitoring
すべてのデーモンを停止できます。他のものより簡単に停止されるものもあります。パートナーがホールドダウンし、失われた場合にパートナーを再スポーンするデーモンペアでさえも停止できます。あなたはそれで少し一生懸命働く必要があります。
daemon
関数を使用してみてください。
#include <unistd.h>
int daemon(int nochdir, int noclose);
manページ から:
Daemon()関数は、制御端末から自分自身を切り離し、システムデーモンとしてバックグラウンドで実行するプログラム用です。
Nochdirがゼロの場合、daemon()は呼び出しプロセスの現在の作業ディレクトリをルートディレクトリ( "/")に変更します。それ以外の場合、現在の作業ディレクトリは変更されません。
Nocloseがゼロの場合、daemon()は標準入力、標準出力、および標準エラーを/ dev/nullにリダイレクトします。それ以外の場合、これらのファイル記述子は変更されません。
アプリが次のいずれかである場合:
{
".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
(アプリディレクトリ内のコードの変更を簡単に監視し、コードの変更が発生したときにアプリプロセスを自動的に再起動することもできます)
Fork()を呼び出すことで、子プロセスを作成しました。 forkが成功した場合(forkがゼロ以外のPIDを返した場合)、子プロセス内からこの時点から実行が継続されます。この場合、親プロセスを正常に終了してから、子プロセスで作業を続行します。
多分これが役立つでしょう: http://www.netzmafia.de/skripten/unix/linux-daemon-howto.html
デーモンは、バックグラウンドでの単なるプロセスです。 LinuxでOSの起動時にプログラムを起動する場合は、/ etc/rc.d/rc.local(他のすべてのスクリプトの後に実行)または/etc/startup.shに起動コマンドを追加します
Windowsでは、サービスを作成し、サービスを登録してから、管理->サービスパネルでブート時に自動的に開始するように設定します。