Maison  >  Article  >  Tutoriel système  >  Démons sous Linux : comment écrire et utiliser des démons simples

Démons sous Linux : comment écrire et utiliser des démons simples

PHPz
PHPzavant
2024-02-14 22:42:33533parcourir

Le processus démon est un processus spécial dans le système Linux. Il s'exécute en arrière-plan sans terminal de contrôle et n'est pas soumis à l'interférence de l'utilisateur. Il est responsable de l'exécution de certaines tâches et fonctions liées au système ou aux applications. Le rôle du démon est d'améliorer la stabilité et l'efficacité du système pour faire face à certains accidents ou anomalies inattendus. Dans les appareils Linux embarqués, les processus démons peuvent être utilisés pour protéger le processus principal du système et l'empêcher de se terminer anormalement, provoquant un crash complet du système et endommageant l'expérience utilisateur. Mais comprenez-vous vraiment le processus démon sous Linux ? Savez-vous comment écrire et utiliser un démon simple sous Linux ? Cet article vous présentera en détail les connaissances pertinentes sur les processus démons sous Linux, vous permettant de mieux utiliser et comprendre ce puissant type de processus sous Linux.

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

Créez un processus démon dans un appareil Linux pour protéger le processus principal du système et éviter que certains accidents inattendus ne provoquent une fin anormale du processus principal, provoquant l'arrêt complet du système sans aucune réponse, détruisant l'expérience utilisateur. Cependant, après avoir examiné de nombreuses informations, j'ai constaté que la plupart des gens parlaient uniquement de la façon de créer et d'implémenter des processus démons sur la plate-forme x86, et que personne n'expliquait comment créer et implémenter des processus démons sur les plates-formes embarquées. Ainsi, après quelques explorations et une compréhension générale de tout, des principes au code, j'ai moi-même trouvé quelques idées. Vous trouverez ci-dessous un bref résumé et une organisation.

1.Principes techniques

Ce qui suit est un extrait d'Internet sur l'introduction et la description du processus démon dans le système Linux x86.

Le démon est un processus spécial qui s'exécute en arrière-plan. Il est indépendant du terminal de contrôle et exécute périodiquement certaines tâches ou attend le traitement de certains événements.

Le processus démon est un processus orphelin spécial. Ce type de processus quitte le terminal. Pourquoi doit-il quitter le terminal ? La raison pour laquelle il est séparé du terminal est d'éviter que le processus ne soit interrompu par des informations générées par n'importe quel terminal, et ses informations pendant l'exécution ne seront affichées sur aucun terminal. Puisque sous Linux, l'interface par laquelle chaque système communique avec les utilisateurs est appelée un terminal, chaque processus qui démarre à partir de ce terminal sera attaché à ce terminal. Ce terminal est appelé le terminal de contrôle de ces processus lorsque le terminal de contrôle est fermé. , Les processus correspondants seront automatiquement fermés. Cependant, le processus démon peut dépasser cette limitation. Il est séparé du terminal et s'exécute en arrière-plan. Le but d'être séparé du terminal est d'empêcher que les informations pendant le processus en cours d'exécution ne soient affichées dans n'importe quel terminal et que le processus le fasse. ne sera accessible par aucun terminal. Interrompu par le message de terminal généré. Il commence à s'exécuter lorsqu'il est exécuté et ne se termine que lorsque tout le système est arrêté (cela peut bien sûr être considéré comme tuant le processus démon correspondant). Si vous souhaitez qu'un processus ne soit pas affecté par l'utilisateur, une interruption ou d'autres modifications, vous devez alors transformer ce processus en processus démon.

2. Étapes de conception

Pour les systèmes Linux sur la plate-forme x86, théoriquement, afin d'obtenir les effets ci-dessus, le processus démon comporte un ensemble strict d'étapes de mise en œuvre. En d’autres termes, le processus démon doit supprimer certaines restrictions liées au système au début du démarrage, afin qu’il puisse s’exécuter de manière stable en arrière-plan sans être interféré ni affecté par d’autres tâches.

Voici le processus de base d'écriture d'un démon sur la plateforme x86 :

  1. Bloquez certains signaux qui contrôlent les opérations du terminal. Cela permet d'éviter que le terminal de contrôle ne soit perturbé et ne se ferme ou ne se bloque avant que le démon ne soit exécuté. Pour une utilisation plus détaillée des signaux, veuillez consulter « Gestion des interruptions de signal ».
  2. Fonctionne en arrière-plan. Il s'agit de mettre le processus démon en arrière-plan pour éviter de bloquer le terminal de contrôle. La méthode consiste à appeler fork() dans le processus pour terminer le processus parent et laisser le démon s'exécuter en arrière-plan dans le processus enfant.
  3. Détachez le contrôle des terminaux, des sessions de connexion et des groupes de processus. Il faut d'abord introduire la relation entre les processus et les terminaux de contrôle, les sessions de connexion et les groupes de processus sous Linux : un processus appartient à un groupe de processus, et le numéro de groupe de processus (GID) est le numéro de processus (PID) du chef du groupe de processus. . Une session de connexion peut contenir plusieurs groupes de processus. Ces groupes de processus partagent un terminal de contrôle. Ce terminal de contrôle est généralement le terminal de connexion shell à partir duquel le processus a été créé. Les terminaux de contrôle, les sessions de connexion et les groupes de processus sont généralement hérités du processus parent. Notre objectif est de nous en débarrasser et de ne pas en être affecté. Par conséquent, setsid() doit être appelé pour que l'enfant soit le nouveau leader de la session. Une fois l'appel setsid() réussi, le processus devient le nouveau chef de groupe de session et le nouveau chef de groupe de processus, et est séparé de la session de connexion et du groupe de processus d'origine. En raison de l'exclusivité du processus de session sur le terminal de contrôle, le processus est en même temps détaché du terminal de contrôle.
  4. Empêche le processus de rouvrir le terminal de contrôle. Désormais, le processus est devenu le leader de session sans terminal, mais il peut se réappliquer pour ouvrir un terminal de contrôle. Vous pouvez empêcher un processus de rouvrir le terminal de contrôle en le faisant n'être plus le leader de la session, en créant à nouveau un processus enfant.
  5. Fermez un descripteur de fichier ouvert. Un processus hérite des descripteurs de fichiers ouverts du processus parent qui l'a créé. S'il n'est pas fermé, les ressources système seront gaspillées, le système de fichiers sur lequel se trouve le processus ne pourra pas être démonté et des erreurs imprévisibles se produiront.
  6. Changez le répertoire de travail actuel. Lorsqu'un processus est actif, le système de fichiers contenant son répertoire de travail ne peut pas être démonté. Généralement, vous devez remplacer le répertoire de travail par le répertoire racine. Pour les vidages mémoire qui doivent être effectués, le processus qui écrit le journal d'exécution modifie le répertoire de travail vers un répertoire spécifique tel que /tmp.
  7. Réinitialiser le masque de création de fichier. Un processus hérite du masque de création de fichier du processus parent qui l'a créé. Il peut modifier les autorisations d'accès aux fichiers créés par le démon. Pour éviter cela, le masque de création de fichier doit être effacé.
  8. Gérez le signal SIGCHLD. Pour certains processus, en particulier les processus serveur, des processus enfants sont souvent générés pour gérer les requêtes lorsque celles-ci arrivent. Si le processus parent n'attend pas la fin du processus enfant, le processus enfant deviendra un processus zombie (zombie) et occupera des ressources système (pour plus de détails sur les processus zombies, veuillez consulter « Processus zombie »). Si le processus parent attend la fin du processus enfant, cela augmentera la charge du processus parent et affectera les performances de concurrence du processus serveur. Sous Linux, vous pouvez simplement définir le fonctionnement du signal SIGCHLD sur SIG_IGN. De cette façon, le noyau ne générera pas de processus zombie jusqu'à la fin du processus enfant.


Ce qui suit est un ensemble complet de codes sources tirés du blog d'un senior :

#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
int init_daemon(void) { 
    int pid; int i; // 1)屏蔽一些控制终端操作的信

号  signal(SIGTTOU,SIG_IGN); signal(SIGTTIN,SIG_IGN); signal(SIGTSTP,SIG_IGN); signal(SIGHU

P ,SIG_IGN); // 2)在后台运行  if( pid=fork() ){// 父进程  exit(0);//结束父进程,子进程继

续  }else if(pid 的宏定义  // NOFILE 为文件描述符最大个数,不同系统有不同限

制  for(i=0; i

3. Situation actuelle

Il ressort de la logique de processus ci-dessus et du code réel que le processus démon de la plate-forme x86 est en fait assez complexe et nécessite un certain nombre de processus d'initialisation fastidieux. Cependant, pour les plateformes embarquées, le processus semble plus simple, sans traitement aussi compliqué. Parce que le processus démon est activé dans ce système embarqué. Le but est simplement d'utiliser ce processus démon pour démarrer un autre processus démonisé, puis de surveiller régulièrement si le processus fonctionne toujours normalement. Une fois qu'il s'avère qu'il fonctionne anormalement, redémarrez simplement le processus immédiatement.

J'ai donc simplifié le processus ci-dessus et j'ai obtenu le processus suivant :

  1. Démarrez le processus qui doit être surveillé dans le processus démon.
  2. Créez un fil de discussion dans le processus démon pour surveiller régulièrement l'état d'exécution du processus démon
  3. Le processus démon détermine si le processus démonisé fonctionne toujours normalement. Une fois qu'il constate qu'il fonctionne anormalement, il redémarre immédiatement le processus.

4. Code source réel

Ce qui suit est le code complet du module de processus démon conçu dans ce projet de système embarqué.

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

******** ** 函数名称: 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 下掌握守护进程的编写和使用。

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer