ホームページ  >  記事  >  システムチュートリアル  >  Linux のデーモン: 単純なデーモンの作成方法と使用方法

Linux のデーモン: 単純なデーモンの作成方法と使用方法

PHPz
PHPz転載
2024-02-14 22:42:33578ブラウズ

デーモン プロセスは、Linux システムの特別なプロセスです。制御端末なしでバックグラウンドで実行され、ユーザーの介入を受けません。システムまたはアプリケーション関連のタスクと機能の実行を担当します。デーモンの役割は、予期せぬ事故や異常に対処するためにシステムの安定性と効率を向上させることです。組み込み Linux デバイスでは、デーモン プロセスを使用してシステムのメイン プロセスを保護し、システムが完全にクラッシュしてユーザー エクスペリエンスが損なわれる原因となる異常終了を防ぐことができます。しかし、Linux 上のデーモン プロセスを本当に理解していますか? Linux で簡単なデーモンを作成して使用する方法をご存知ですか?この記事では、Linux でのデーモン プロセスの関連知識を詳しく紹介し、Linux でこの強力なプロセス タイプをよりよく使用し、理解できるようにします。

Linux 下的守护进程:如何编写和使用简单的守护进程

Linux デバイスにデーモン プロセスを作成して、システムのメイン プロセスを保護し、予期せぬ事故によってメイン プロセスが異常終了し、応答なしにシステムが完全にシャットダウンしてユーザー エクスペリエンスが破壊されることを防ぎます。しかし、多くの情報を検討した結果、ほとんどの人は x86 プラットフォームでデーモン プロセスを作成して実装する方法についてのみ話しており、組み込みプラットフォームでデーモン プロセスを作成して実装する方法を紹介する人は誰もいなかったことがわかりました。そこで、いくつかの探索を行い、原理からコードに至るまでのすべてを一般的に理解した後、私自身でいくつかのアイデアを思いつきました。以下に簡単な概要と構成を示します。

1. 技術原則

以下は、x86 Linux システムのデーモン プロセスの紹介と説明に関するインターネットからの抜粋です。

Daemon はバックグラウンドで実行される特別なプロセスであり、制御端末から独立しており、定期的に特定のタスクを実行したり、特定のイベントの処理を待機したりします。

デーモン プロセスは特殊な孤立プロセスです。この種のプロセスは端末から分離されています。なぜ端末から分離する必要があるのでしょうか?端末から切り離しているのは、端末が生成する情報によって処理が中断されることを防ぐためであり、実行中の情報はどの端末にも表示されません。 Linux では、各システムがユーザーと通信するためのインターフェースをターミナルと呼ぶため、このターミナルから実行を開始するすべてのプロセスはこのターミナルに接続されます。このターミナルは、これらのプロセスの制御ターミナルと呼ばれます。 , 該当するプロセスが自動的に終了します。ただし、デーモン プロセスはこの制限を突破することができます。デーモン プロセスはターミナルから切り離され、バックグラウンドで実行されます。ターミナルから切り離される目的は、実行中のプロセス中の情報がターミナルに表示されないようにするためです。どの端末からもアクセスできません。生成された端末メッセージによって中断されます。実行されると実行を開始し、システム全体がシャットダウンされるまで終了しません (もちろん、対応するデーモン プロセスを強制終了すると考えることもできます)。プロセスがユーザー、中断、その他の変更の影響を受けないようにする場合は、このプロセスをデーモン プロセスに変える必要があります。

2. 設計手順

x86 プラットフォーム上の Linux システムの場合、理論的には、上記の効果を実現するために、デーモン プロセスには厳密な実装手順が必要です。つまり、デーモン プロセスは、他のタスクの干渉や影響を受けることなくバックグラウンドで安定して実行できるように、起動の開始時にシステム関連の制限をいくつか削除する必要があります。

x86 プラットフォームでデーモンを作成する基本的なプロセスは次のとおりです:

  1. 端末の動作を制御する一部の信号をブロックします。これは、制御端末が妨害されて、デーモンが実行される前に終了したりハングしたりするのを防ぐためです。信号の使用方法の詳細については、「信号割り込みの処理」を参照してください。
  2. バックグラウンドで実行されます。これは、制御端末のハングを避けるためにデーモン プロセスをバックグラウンドに置くためです。その方法は、プロセス内で fork() を呼び出して親プロセスを終了し、子プロセスのバックグラウンドでデーモンを実行させることです。
  3. 端末、ログインセッション、プロセスグループの制御を切り離します。まず、Linux におけるプロセスと制御端末、ログイン セッションとプロセス グループの関係を紹介する必要があります。プロセスはプロセス グループに属し、プロセス グループ番号 (GID) はプロセス グループ リーダーのプロセス番号 (PID) です。 。ログイン セッションには複数のプロセス グループを含めることができます。これらのプロセス グループは制御端末を共有します。この制御端末は通常、プロセスの作成元となったシェル ログイン端末です。制御端末、ログイン セッション、プロセス グループは通常、親プロセスから継承されます。私たちの目標は、それらを排除し、それらの影響を受けないようにすることです。したがって、子に新しいセッション リーダーを処理させるには、setsid() を呼び出す必要があります。 etsid() 呼び出しが成功すると、プロセスは新しいセッション グループ リーダーおよび新しいプロセス グループ リーダーになり、元のログイン セッションおよびプロセス グループから分離されます。セッションプロセスは制御端末への排他性があるため、同時にプロセスは制御端末から切り離されます。
  4. プロセスが制御端末を再度オープンしないようにします。現在、プロセスはターミナルレス セッション リーダーになっていますが、制御ターミナルを開くために再適用できます。子プロセスを再度作成することで、プロセスがセッション リーダーでなくなることで、制御ターミナルを再度開くことを防ぐことができます。
  5. 開いているファイル記述子を閉じます。プロセスは、そのプロセスを作成した親プロセスからオープン ファイル記述子を継承します。閉じていない場合、システム リソースが無駄になり、プロセスが存在するファイル システムをアンマウントできなくなり、予期しないエラーが発生します。
  6. 現在の作業ディレクトリを変更します。プロセスがアクティブな間は、その作業ディレクトリを含むファイル システムをマウント解除することはできません。通常、作業ディレクトリをルート ディレクトリに変更する必要があります。実行する必要があるコア ダンプの場合、実行ログを書き込むプロセスにより、作業ディレクトリが /tmp などの特定のディレクトリに変更されます。
  7. ファイル作成マスクをリセットします。プロセスは、それを作成した親プロセスからファイル作成マスクを継承します。デーモンによって作成されたファイルのアクセス許可が変更される可能性があります。これを防ぐには、ファイル作成マスクをクリアする必要があります。
  8. SIGCHLD シグナルを処理します。一部のプロセス、特にサーバー プロセスでは、リクエストが到着したときにリクエストを処理するために子プロセスが生成されることがよくあります。親プロセスが子プロセスの終了を待たない場合、子プロセスはゾンビ プロセス (ゾンビ) となり、システム リソースを占有します (ゾンビ プロセスの詳細については、「ゾンビ プロセス」を参照してください)。親プロセスが子プロセスの終了を待機すると、親プロセスの負担が増加し、サーバー プロセスの同時実行パフォーマンスに影響します。 Linux では、SIGCHLD 信号の動作を SIG_IGN に設定するだけで済みます。このようにして、カーネルは子プロセスが終了するまでゾンビ プロセスを生成しません。


以下は、先輩のブログから抜粋したソース コードの完全なセットです:

リーリー

3. 実際の状況

上記のプロセス ロジックと実際のコードから、x86 プラットフォームのデーモン プロセスは実際には非常に複雑で、多くの面倒な初期化プロセスが必要であることがわかります。しかし、組み込みプラットフォームの場合、そのような複雑な処理はなく、プロセスはより単純になるようです。なぜなら、この組み込みシステムではデーモンプロセスが有効になっているからです。このデーモンプロセスを利用して別のデーモン化プロセスを起動し、そのプロセスが正常に動作しているかを定期的に監視し、異常動作を発見した場合には直ちにプロセスを再起動することが目的です。

そこで、上記のプロセスを簡略化し、次のプロセスを得ました:

  1. デーモンプロセスで監視が必要なプロセスを起動します。
  2. デーモン プロセスにスレッドを作成して、デーモン プロセスの実行ステータスを定期的に監視します。
  3. デーモンプロセスは、デーモン化されたプロセスが正常に動作しているかどうかを判断し、異常な動作を検出すると、直ちにプロセスを再起動します。

4. 実際のソースコード

以下は、この組み込みシステム プロジェクトで設計されたデーモン プロセス モジュールの完全なコードです。

/******************************************************************************************

******** ** 函数名称: lockfile ** 功能描述: 对文件加锁/解锁 ** 输入参数: lock: 1表示进行加锁处理,

0表示进行解锁处理 ** 输出参数: 无 ** 返回参

数: 无 *************************************************************************************

*************/ int tryto_lockfile(int fd, int lock) { struct flock fl; fl.l_type = (lock =

= 1) ? F_WRLCK : F_UNLCK; fl.l_start = 0; fl.l_whence = SEEK_SET; fl.l_len = 0; return (f

cntl(fd, F_SETLK, &fl)); } /***************************************************************

*********************************** ** 函数名称: get_proc_running_state ** 功能描述: 获取进程

运行状态 ** 输入参数: 无 ** 输出参数: 无 ** 返回参数: 返回-1表示路径错误 ** 返回参数: 返回0表示进程

从未运行过,返回1表示进程曾经运行过但是现在停止运行了,返回2表示进程正在运行

中 ****************************************************************************************

**********/ static int get_proc_running_state(const char* filename) { int fd; if (filename

 == NULL) { /* 文件名为

空 */ return -1; } fd = open(filename, O_RDWR, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); i

f (fd 0) { /* 文件不存在,表示进程从未运行

过 */ return 0; } if (tryto_lockfile(fd, 1) == -1) { /* 文件加锁失败,表示进程在运行

中 */ close(fd); return 2; } else { /* 文件加锁成功,表示进程已经消

失 */ tryto_lockfile(fd, 0); /* 此处要注意记得解锁和关闭文

件 */ close(fd); return 1; } } /***********************************************************

*************************************** ** 函数名称: proc_watch ** 功能描述: 检测进程是否有在运

行,没有运行则重新启动之 ** 输入参数: procname: 进程名 ** 输出参数: 无 ** 返回参数: 返回-1表示进程从

未运行过;返回0表示进程当前运行正常; ** 返回参数: 返回其他非零值表示进程不存在且已被重新启动,返回的值

是新的pid值 ***************************************************************************

***********************/ int proc_watch(const char *procname) { int result, state; char fi

lename[100]; result = 0; sprintf(filename, "/var/run/%s.pid", procname); state = get_proc_

running_state(filename); switch (state) { case 0: result = -1; break; case 1: result = sta

rt_proc_by_name(procname); break; case 2: result = 0; break; default: break; } return resu

lt; } /************************************************************************************

************** ** 函数名称: start_proc ** 功能描述: 启动进程开始运行 ** 输入参数: 无 ** 输出参

数: 无 ** 返回参数: 进程的ID号,若启动失败则返回

0 *****************************************************************************************

*********/ int start_proc_by_name(const char* procname) { pid_t pid, child_pid; char filen

ame[100]; sprintf(filename, "%s%s", PROC_FILE_PATH, procname); child_pid = 0; if (access(f

ilename, X_OK | F_OK) != 0) { /* 如果文件存在,并且可执行 */ return 0; } pid = fork(); /* 首

先要fork一个进程出来 */ if (pid 0) { /* 创建进程失

败 */ return 0; } else if (pid == 0) { /* 创建进程成功,此处是子进程的代

码 */ if (execl(filename, procname, (char *)NULL) != -1) { return 1; } else { return 0; } 

} else { /* 创建进程成功,此处是父进程代


******************************************************************* ** 函数名

称: thread_client_hdl ** 功能描述: client进程监视线程 ** 输入参数: 无 ** 输出参数: 无 ** 返回参

数: 无 *************************************************************************************

*************/ static void *thread_client_hdl(void *pdata) { int result; pdata = pdata; sl

eep(10); /* 第一次要进行延

时 */ for (;;) { printf("time to check thread_client...\n"); result = proc_watch(PROC_NAME



_CLIENT); if (result == -1) { printf("thread_client never exist...\n"); } else if (result 

== 0) { printf("thread_client running ok...\n"); } else { printf("thread_client has gone! 

but restarted...\n"); } sleep(10); } return NULL; } /*************************************

************************************************************* ** 函数名称: main ** 功能描

述: 入口主函数 ** 输入参数: 无 ** 输出参数: 无 ** 返回参

数: 无 *************************************************************************************

*************/ int main(int argc, char *argv[]) { int client_para; char *p, *process_name;

 pthread_t thread_client; process_name = argv[0]; /* 获取进程名

称 */ p = process_name + strlen(process_name); while (*p != '/' && p != process_name) { p-

-; } if (*p == '/') { process_name = p + 1; } printf("\"%s\" starting...\n", process_name)

; client_para = 0x01; if (pthread_create(&thread_client, NULL, thread_client_hdl, &client_

para) != 0) { printf("create thread_client failed!\n"); return 1; } if (start_proc_by_name

(PROC_NAME_CLIENT) == 0) { printf("start thread_client failed!\n"); return 1; } for (;;) {

 sleep(60); printf("i am still alive...\n"); } return 0; }

通过本文,你应该对 Linux 下的守护进程有了一个基本的了解,知道了它的定义、特点和用途。你也应该明白了如何在 Linux 下编写和使用简单的守护进程,以及使用守护进程时需要注意的一些问题和技巧。我们建议你在使用 Linux 系统时,使用守护进程来提高系统的稳定性和效率。同时,我们也提醒你在使用守护进程时要注意一些潜在的问题和挑战,如信号处理、日志记录、资源管理等。希望本文能够帮助你更好地使用 Linux 系统,让你在 Linux 下掌握守护进程的编写和使用。

以上がLinux のデーモン: 単純なデーモンの作成方法と使用方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はlxlinux.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。