私はまさにその反対が欲しいこの問題。終了した後でも再起動されるプロセスを作成する方法を知りたいです。誰かが私に実装例を与えることができますか?
例えば、次のようにタイムスタンプを継続的に記録し、カウンタをログファイルにインクリメントする簡単なプロセスがあるとします。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define FILE_PATH "/home/lincoln/date.log"
int main() {
time_t current_time;
struct tm *time_info;
int counter = 0;
while (1) {
FILE *file = fopen(FILE_PATH, "a");
if (file == NULL){
perror("Failed to open the file");
return 1;
}
// Get the current timestamp
time(¤t_time);
time_info = localtime(¤t_time);
// Format the timestamp
char timestamp[25];
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", time_info);
// Write timestamp and counter to the file
fprintf(file, "%s | Counter: %d\n", timestamp, counter++);
fflush(file);
fclose(file);
sleep(5); // Sleep for 5 seconds
}
return 0;
}
バイナリファイル(ELFファイル)を生成するには、次のコマンドを使用しました。
gcc logger_file.c -o date_test
(これは単なる例であることに注意してください。継続的に印刷する「hello world」プログラムです。継続的に実行される効果を観察したいので、ロギングを選択しました。)
~から引用された質問(私が間違っている場合は修正してください。)これは、子プロセスの親プロセスを監視してこれを行います。子プロセスが終了すると、親プロセスはそれを再開します。概念は理解していますが、実際にどのように実装されているのかわかりません。
提供されたバイナリの例を使用してこれを実装する方法を示すことができる人はいますか? (私の研究へのより多くのリンクも歓迎します。)
より多くの情報が必要な場合はお知らせください。
編集する:私の実装では、組み込みデバイスで実行する予定です。基本的に私が働く環境ではシステム初期化ブートシステムとしてビジーボックスv1.23.2armv7プロセッサで実行される3.18.44カーネルのツールセットとして使用されます(私はgcc-12-arm-linux-gnueabi-base
ツールチェーンを使用しています)。
答え1
プロセスが終了すると、自分で復活することはできません。それは死んで、実際には何もできません(それがポイントです)。
スーパーバイザにプロセスのステータスを確認し、前のプロセスが終了したときに新しいプロセスを作成するように指示できます。
ほとんどすべて(一部のニッチや小規模コンテナ指向を除く)Linuxディストリビューションはこれを簡単にします。 systemdがこれを行うことができます。何をすべきかは、systemdサービス単位ファイルを作成して設定することだけです。Restart=
財産always
、または、on-abnormal
またはon-abort
。それだけです!
その後、systemdは監督者として機能し(procfsなどを扱うために自分の監督者を作成する必要はありません)、すべてがすぐに機能します。明らかにサービスを停止して再起動しますsystemctl
。
それでは、実際にこれを行う方法を尋ねます。procfs
これは方法の1つと言及しました。 /proc/{PID}ディレクトリまたはその中のメモリマップを見てください。
しかし、規制当局は通常彼らがすることは(そしてsystemdのservice.cもこれを行います)ハンドラを登録することですSIGCHLD
:プロセスの子プロセスが終了すると、親プロセスはシグナルを受け取ります。これは、監督者が実際に監督対象プロセスを子プロセスとして開始するためにfork
機能します。
したがって、おおよそ以下が必要です。古代のOracleガイドで緩やかに適用そしてLinuxのマニュアルページは次のとおりですman waitpid
。
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
void proc_exit() {
int wstat;
pid_t pid;
while (1) {
int retval = waitpid(pid, &wstat, WNOHANG);
if (retval == 0)
return;
else if (retval == -1)
return;
else
printf("Return code: %d\n", WEXITSTATUS(wstat));
}
}
int main() {
signal(SIGCHLD, proc_exit);
switch (fork()) {
case -1:
perror("main: fork");
exit(0);
case 0:
printf("I'm alive (temporarily)\n");
exit(42);
default:
pause();
}
}
子プロセスはfork
戻り0をチェックするので、これは印刷プロセスです"I'm alive…
。ここから別の実行可能ファイル(つまり管理されているプロセス)を作成するには、を呼び出しますexecve
。
答え2
init
幸いなことに、システムに通常のデーモンがある場合にプロセスを実行できます/etc/inittab
。ここで、プロセスが終了したときに再開することを指定できます。
/etc/inittab
次の項目を追加した後:
dtst:345:respawn:/path/to/date_time
これはdtst
単なるラベルです。345
つまり、ランレベル3、4、5でアクティブになっており、respawn
プロセスが終了すると(私たちが望む場合)、プロセスが再開されます。
telinit
その後、コマンドを使用するか、ファイルを再読み込みするようにkill -HUP 1
指示します。init
systemd
代わりに、使用しているシステムで同様の操作を実行するための洗練された方法があるかもしれませんinit
。
BusyBoxはアイテムのフィールドをinit
無視します。フィールドが空でない場合、コントロール TTY が設定されます。runlevels
inittab
id
別の方法は、「nanny」シェルスクリプトを使用してこれを行うことです。
#!/bin/sh
while true; do
/path/to/date_test
done
プログラムが正常に終了し、状態が成功した場合、ループを終了するように設定できます。
while !/path/to/date_test; do : ; done
date_test
プログラムが失敗したり異常終了した場合は、do-nothing nullコマンドを実行して:
繰り返します。
ただし、このループを実行するシェル自体はシャットダウンから保護されません。これは、プログラムが不安定になり、競合が発生して再起動が必要な場合に便利です。