web-dev-qa-db-fra.com

Déterminer par programme si un programme est en cours d'exécution

En C, comment puis-je savoir par programme si un processus est déjà en cours d'exécution sur Linux/Ubuntu pour éviter qu'il démarre deux fois? Je cherche quelque chose de similaire à pidof.

28
Frank Vilea

Vous pouvez parcourir les entrées pid dans /proc et recherchez votre processus dans le fichier cmdline ou effectuez une readlink sur le lien exe (ce qui suit utilise la première méthode).

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>

pid_t proc_find(const char* name) 
{
    DIR* dir;
    struct dirent* ent;
    char* endptr;
    char buf[512];

    if (!(dir = opendir("/proc"))) {
        perror("can't open /proc");
        return -1;
    }

    while((ent = readdir(dir)) != NULL) {
        /* if endptr is not a null character, the directory is not
         * entirely numeric, so ignore it */
        long lpid = strtol(ent->d_name, &endptr, 10);
        if (*endptr != '\0') {
            continue;
        }

        /* try to open the cmdline file */
        snprintf(buf, sizeof(buf), "/proc/%ld/cmdline", lpid);
        FILE* fp = fopen(buf, "r");

        if (fp) {
            if (fgets(buf, sizeof(buf), fp) != NULL) {
                /* check the first token in the file, the program name */
                char* first = strtok(buf, " ");
                if (!strcmp(first, name)) {
                    fclose(fp);
                    closedir(dir);
                    return (pid_t)lpid;
                }
            }
            fclose(fp);
        }

    }

    closedir(dir);
    return -1;
}


int main(int argc, char* argv[]) 
{
    if (argc == 1) {
        fprintf("usage: %s name1 name2 ...\n", argv[0]);
        return 1;
    }

    int i;
    for(int i = 1; i < argc; ++i) {
        pid_t pid = proc_find(argv[i]);
        if (pid == -1) {
            printf("%s: not found\n", argv[i]);
        } else {
            printf("%s: %d\n", argv[i], pid);
        }
    }

    return 0;
}
29
John Ledbetter

C'est le même que le code affiché par John Ledbetter. Il est bon de se référer au fichier nommé stat dans le répertoire/proc/pid/que cmdline car le premier donne les états et le nom du processus. Le fichier cmdline donne des arguments complets avec lesquels le processus est démarré. Cela échoue donc dans certains cas. Quoi qu'il en soit, l'idée donnée par John est bonne. Ici, j'ai posté le code modifié de John. Je cherchais le code en c sous Linux pour vérifier que dhcp fonctionne ou non. Avec ce code, je peux le faire. J'espère que cela peut être utile pour quelqu'un comme moi.

#include <sys/types.h>
#include <dirent.h>
#include<unistd.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

pid_t proc_find(const char* name) 
{
    DIR* dir;
    struct dirent* ent;
    char buf[512];

    long  pid;
    char pname[100] = {0,};
    char state;
    FILE *fp=NULL; 

    if (!(dir = opendir("/proc"))) {
        perror("can't open /proc");
        return -1;
    }

    while((ent = readdir(dir)) != NULL) {
        long lpid = atol(ent->d_name);
        if(lpid < 0)
            continue;
        snprintf(buf, sizeof(buf), "/proc/%ld/stat", lpid);
        fp = fopen(buf, "r");

        if (fp) {
            if ( (fscanf(fp, "%ld (%[^)]) %c", &pid, pname, &state)) != 3 ){
                printf("fscanf failed \n");
                fclose(fp);
                closedir(dir);
                return -1; 
            }
            if (!strcmp(pname, name)) {
                fclose(fp);
                closedir(dir);
                return (pid_t)lpid;
            }
            fclose(fp);
        }
    }


closedir(dir);
return -1;
}


int main(int argc, char* argv[]) 
{
    int i;
    if (argc == 1) {
        printf("usage: %s name1 name2 ...\n", argv[0]);
        return 1;
    }

    for( i = 1; i < argc; ++i) {
        pid_t pid = proc_find(argv[i]);
        if (pid == -1) {
            printf("%s: not found\n", argv[i]);
        } else {
            printf("%s: %d\n", argv[i], pid);
        }
    }

    return 0;
}
14
yuvaeasy

Il existe des moyens d'éviter l'utilisation de /proc (Et il pourrait y avoir de bonnes raisons de le faire, par exemple /proc Pourrait ne pas être installé du tout, et/ou il pourrait avoir été lié à quelque chose de trompeur, ou ce pid a été caché dans /proc). Certes, la méthode ci-dessous ne semble pas si bonne, je souhaite qu'il y ait une API appropriée pour cela!

Quoi qu'il en soit, la section 1.9 d'un 1997 FAQ de programmation Unix dit:

Utilisez kill() avec 0 pour le numéro de signal. Il y a quatre résultats possibles de cet appel:

  • kill() renvoie 0

    Cela implique qu'un processus existe avec le PID donné et que le système vous permettra de lui envoyer des signaux. Cela dépend du système si le processus peut être un zombie.

  • kill() renvoie -1, errno == ESRCH

    Soit aucun processus n'existe avec le PID donné, soit des améliorations de sécurité font que le système nie son existence. (Sur certains systèmes, le processus pourrait être un zombie.)

  • kill() renvoie -1, errno == EPERM

    Le système ne vous permettrait pas de tuer le processus spécifié. Cela signifie que le processus existe (encore une fois, il pourrait s'agir d'un zombie) ou que des améliorations draconiennes de la sécurité sont présentes (par exemple, votre processus n'est pas autorisé à envoyer des signaux à quiconque ).

  • kill() renvoie -1, avec une autre valeur de errno

    Tu as des problèmes!

La technique la plus utilisée consiste à supposer que le succès ou l'échec avec EPERM implique que le processus existe et que toute autre erreur implique qu'il n'en existe pas.

14
RCL

pidof fonctionne en marchant sur le /proc système de fichiers . En C, vous pouvez faire quelque chose de similaire en énumérant /proc; ouverture /proc/X/cmdline pour chaque X où X est une liste d'un ou plusieurs nombres décimaux. Je ne sais pas si vous avez des exigences de portabilité, mais gardez cela à l'esprit si vous comptez sur la disponibilité de /proc.

Ce problème est plus généralement résolu sur les systèmes de type UNIX en encapsulant le démarrage du programme et en maintenant un fichier PID. Voir /etc/init.d/* pour des exemples classiques de cette approche. Vous devrez veiller à ce que le code qui lit les écritures du fichier PID le fasse de manière sûre (atomiquement). Si votre système d'exploitation cible a une init plus performante (telle que systemd ), vous pourrez peut-être sous-traiter ce travail à cela.

3
jmtd