Points clés pour apprendre à contrôler les processus sous Linux

1. Création de processus

1.1 fonction fourchette

#include <unistd.h>
pid_t fork(void);
返回值:自进程中返回0,父进程返回子进程id,出错返回-1

Le processus appelle fork. Lorsque le contrôle est transféré au code fork dans le noyau, le noyau effectuera les opérations suivantes 

  • Allouer de nouveaux blocs de mémoire et structures de données du noyau au processus enfant
  • Copiez une partie du contenu de la structure de données du processus parent vers le processus enfant
  • Ajouter un processus enfant à la liste des processus système
  • fork revient et démarre la planification du planificateur

Avant le fork, le processus parent s'exécute indépendamment. Après le fork, les flux d'exécution parent et fils s'exécutent séparément. Notez qu'après fork, qui s'exécute en premier est entièrement déterminé par le planificateur.

1.2 Copie sur écriture

Habituellement, le code du père et du fils est partagé. Lorsque le père et le fils n'écrivent pas, les données sont également partagées. Lorsque l'une ou l'autre des parties tente d'écrire, elles en ont chacune une copie sous forme de copie sur écriture. . Voir la figure ci-dessous pour plus de détails :

1.3 Utilisation courante de fork

  1. Un processus parent souhaite se dupliquer afin que les processus parent et enfant exécutent différents segments de code en même temps. Par exemple, un processus parent attend une demande client et génère un processus enfant pour gérer la demande.
  2. Un processus veut exécuter un programme différent. Par exemple, une fois que le processus enfant revient de fork, il appelle la fonction exec. 

1.4 Raisons de l'échec de l'appel de fork

  • Il y a trop de processus dans le système
  • Le nombre de processus pour l'utilisateur réel dépasse la limite

2. Fin du processus

2.1 Scénario de sortie de processus

  • Sortie normale, sortie anormale
  • La valeur de retour d'un processus n'a de sens que lorsqu'il se termine normalement.
  • Si le processus se termine anormalement, la valeur de retour n'a aucun sens.

2.2 Méthodes courantes de sortie des processus

Sortir normalement

  • 1. Retour du principal
  • 2. Sortie d'appel
  • 3. _sortie

2.3 Sortie anormale

  • Reçu un signal anormal tel que tuer
  • Une erreur se produit lors de l'exécution du code, le système d'exploitation envoie un signal et le processus est ensuite notifié

fonction exit et fonction _exit

#include <unistd.h>
void _exit(int status);
void exit(int status);

  • Paramètre : statut définit l'état de fin du processus. Le processus parent obtient cette valeur par attente.
  • Bien que status soit un entier, seuls les 8 bits inférieurs peuvent être utilisés par le processus parent. Ainsi, lorsque _exit(-1), exécutez $? sur le terminal et constatez que la valeur de retour est 255

exit appellera également _exit à la fin, mais avant d'appeler _exit, un autre travail est effectué :

1. Exécutez la fonction de nettoyage définie par l'utilisateur via atexit ou on_exit.

2. Fermez tous les flux ouverts et toutes les données mises en cache sont écrites.

3. Appelez _exit

2.4 retour sortie

return est un moyen plus courant de quitter un processus. L'exécution de return n équivaut à l'exécution de exit(n) , car la fonction d'exécution appelant main utilisera la valeur de retour de main comme paramètre de exit .

3. Processus en attente

3.1 Signification :

  • Si le processus enfant se termine et que le processus parent est ignoré, cela peut provoquer un problème de « processus zombie », qui à son tour peut provoquer une fuite de mémoire.
  • Nous devons savoir dans quelle mesure les tâches assignées par le processus parent au processus enfant sont accomplies. Par exemple, si le processus enfant est terminé, le résultat est correct ou incorrect, ou s'il se termine normalement.
  • Le processus parent recycle les ressources du processus enfant et obtient les informations de sortie du processus enfant via l'attente du processus.

3.2 Méthode

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int*status);
返回值:
 成功返回被等待进程pid,失败返回-1。
参数:
 输出型参数,获取子进程退出状态,不关心则可以设置成为NULL
pid_ t waitpid(pid_t pid, int *status, int options);
返回值:
 当正常返回的时候waitpid返回收集到的子进程的进程ID;
 如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0;
 如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在;
参数:
 pid:
 Pid=-1,等待任一个子进程。与wait等效。
 Pid>0.等待其进程ID与pid相等的子进程。
 status:
 WIFEXITED(status): 若为正常终止子进程返回的状态,则为真。(查看进程是否是正常退出)
 WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码。(查看进程的退出码)
 options:
 WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待。若正常结束,则返回该子进
程的ID。
  • Si le processus enfant s'est terminé, lors de l'appel de wait/waitpid, wait/waitpid reviendra immédiatement, libérera des ressources et obtiendra la sortie du processus enfant.
  • informations.
  • Si wait/waitpid est appelé à tout moment et que le processus enfant existe et s'exécute normalement, le processus peut être bloqué.
  • Si le processus enfant n'existe pas, une erreur est immédiatement renvoyée.

3.3 Obtenir l'état du processus enfant

wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
如果传递NULL,表示不关心子进程的退出状态信息。
否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
status不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究status低16比特
位):

Une fois le statut déréférencé, il s’agit d’une variable int occupant quatre octets d’espace mémoire. Mais ses quatre octets ne stockent pas tous la valeur de retour de sortie du processus. Comme le montre la figure : Nous numérotons chaque octet de l'adresse haute à l'adresse basse : 1, 2, 3, 4 octets.

Il n'y a que deux scénarios pour qu'un processus se termine

Sortie normale et sortie anormale

3.3.1 Arithmétique des bits

Par conséquent, avant d'obtenir la valeur de retour, utilisez d'abord les sept bits inférieurs pour déterminer si le processus s'est terminé normalement.

  • Déterminer s'il s'est terminé normalement : Si status & 0x7f == 0, vous pouvez juger si un processus s'est terminé normalement, sinon il s'est terminé anormalement.
  • Explication : Étant donné que le code de sortie anormal occupe les 7 bits inférieurs de l'octet n°4, nous pouvons obtenir le code de sortie anormal par AND (*status) et le nombre hexadécimal (0x7f ) .

 

  • Après avoir jugé que la valeur de retour est significative , retirez les 8 bits de poids fort des 16 bits de poids faible.
  • Méthode de récupération : (statut >> 8) et le résultat de 0xff
  • Explication : je souhaite supprimer les 8 bits de poids fort des 16 bits de poids faible. Je dois d'abord décaler l'état vers la droite de 8 bits, puis effectuer un AND au niveau du bit avec 1111 1111 pour obtenir le résultat.
     

Étant donné que la valeur de retour n'utilise qu'un seul octet pour être enregistrée, la valeur de retour du processus ne doit pas être supérieure à 255.

3.3.2 Fonctions macros

WIFEXITED(status)  est équivalent à  ((*status)&(0x7f))==0  . Lorsque le code de sortie anormal est 0, la fonction macro renvoie true, indiquant que la valeur de retour de sortie du processus est significative.

WEXITSTATUS(status)  est équivalent à  ((*status >> 8)&(0xff)) , vous pouvez donc obtenir directement la valeur de retour de sortie du processus.

Méthode de conversion binaire décimal en hexadécimal

Apprenez à apprendre rapidement à convertir entre binaire, décimal et hexadécimal - Zhihu (zhihu.com)

 

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
    pid_t pid;

    pid = fork();
    if (pid < 0)
    {
        printf("%s fork error\n", __FUNCTION__);
        return 1;
    }
    else if (pid == 0)
    { // child
        printf("child is run, pid is : %d\n", getpid());
        sleep(5);
        exit(1);
    }
    else
    {
        int status = 0;
        pid_t ret = 0;
        do
        {
            ret = waitpid(-1, &status, WNOHANG); // 非阻塞式等待
            if (ret == 0)
            {
                printf("child is running, i have some task\n");
            }
            sleep(1);
        } while (ret == 0);

        if (WIFEXITED(status) && ret == pid)
        {
            printf("wait child 5s success, child return code is :%d.\n", WEXITSTATUS(status));
        }
        else
        {
            printf("wait child failed, return.\n");
            return 1;
        }
    }
    return 0;
}

4. Programme de remplacement des processus

4.1 Principe de remplacement

Après avoir utilisé fork pour créer un processus enfant,il exécute le même programme que le processus parent (mais peut exécuter différentes branches de code).Le processus enfant appelle souvent une fonction exec pour exécuter un autre programme. Lorsqu'un processus appelle une fonction exec, le code de l'espace utilisateur et les données du processus sont complètement remplacés par le nouveau programme et l'exécution démarre à partir de la routine de démarrage du nouveau programme. L'appel de exec ne crée pas de nouveau processus, donc l'ID du processus ne change pas avant et après l'appel de exec.

Il existe en fait six fonctions de remplacement commençant par exec, collectivement appelées fonctions exec :

#include <unistd.h>`
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);

J'utilise int execve(const char *path, char *const argv[], char *const envp[]);

  • Si ces fonctions sont appelées avec succès, un nouveau programme sera chargé et exécuté à partir du code de démarrage sans retour.
  • Renvoie -1 s'il y a une erreur dans l'appel
  • Ainsi, la fonction exec n'a qu'une valeur de retour d'erreur mais aucune valeur de retour de réussite.

4.2 Compréhension du nom

l(list) : 表示参数采用列表
v(vector) : 参数用数组
p(path) : 有p自动搜索环境变量PATH
e(env) : 表示自己维护环境变量
#include <unistd.h>
int main()
{
 char *const argv[] = {"ps", "-ef", NULL};
 char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};
 execl("/bin/ps", "ps", "-ef", NULL);
 // 带p的,可以使用环境变量PATH,无需写全路径
 execlp("ps", "ps", "-ef", NULL);
 // 带e的,需要自己组装环境变量
 execle("ps", "ps", "-ef", NULL, envp);
 execv("/bin/ps", argv);
 
 // 带p的,可以使用环境变量PATH,无需写全路径
 execvp("ps", argv);
 // 带e的,需要自己组装环境变量
 execve("/bin/ps", argv, envp);
 exit(0);
}

En fait, seul execve est un véritable appel système, et les cinq autres fonctions finissent par appeler execve, donc execve se trouve dans la section 2 du manuel de l'homme, et les autres fonctions se trouvent dans la section 3 du manuel de l'homme.

5. Créez une coque simple

Acho que você gosta

Origin blog.csdn.net/m0_74234485/article/details/132572045
Recomendado
Clasificación