Comment calculer l'utilisation CPU d'un processus par PID sous Linux à partir de C?


94

Je veux calculer par programme [en C] le% d'utilisation du processeur pour un ID de processus donné sous Linux.

Comment pouvons-nous obtenir le% d'utilisation du processeur en temps réel pour un processus donné?

Pour être plus clair:

  • Je devrais être en mesure de déterminer l'utilisation du processeur pour le processid ou le processus fourni.
  • Le processus n'a pas besoin d'être le processus enfant.
  • Je veux la solution en langage «C».

qu'en est-il de la capture (grep-ing) de la sortie de top.
dusoft le

Ce n'est vraiment pas la meilleure façon de faire efficace; y
codingfreak

Il faudra probablement un appel système "coûteux" pour démarrer "top".
Liran Orevi le

@Liran: Rightly said :)
vpram86

Oubliez cette façon de faire les choses .... en C
codingfreak

Réponses:


148

Vous devez analyser les données à partir de /proc/<PID>/stat. Voici les premiers champs (à partir Documentation/filesystems/proc.txtde la source de votre noyau):

Table 1-3: Contents of the stat files (as of 2.6.22-rc3)
..............................................................................
 Field          Content
  pid           process id
  tcomm         filename of the executable
  state         state (R is running, S is sleeping, D is sleeping in an
                uninterruptible wait, Z is zombie, T is traced or stopped)
  ppid          process id of the parent process
  pgrp          pgrp of the process
  sid           session id
  tty_nr        tty the process uses
  tty_pgrp      pgrp of the tty
  flags         task flags
  min_flt       number of minor faults
  cmin_flt      number of minor faults with child's
  maj_flt       number of major faults
  cmaj_flt      number of major faults with child's
  utime         user mode jiffies
  stime         kernel mode jiffies
  cutime        user mode jiffies with child's
  cstime        kernel mode jiffies with child's

Vous êtes probablement après utimeet / ou stime. Vous devrez également lire la cpuligne de /proc/stat, qui ressemble à:

cpu  192369 7119 480152 122044337 14142 9937 26747 0 0

Cela vous indique le temps processeur cumulé qui a été utilisé dans diverses catégories, en unités de jiffies. Vous devez prendre la somme des valeurs sur cette ligne pour obtenir une time_totalmesure.

Lisez à la fois utimeet stimepour le processus qui vous intéresse, et lisez à time_totalpartir de /proc/stat. Ensuite, dormez pendant une seconde environ, et relisez-les tous. Vous pouvez maintenant calculer l'utilisation du processeur du processus sur la période d'échantillonnage, avec:

user_util = 100 * (utime_after - utime_before) / (time_total_after - time_total_before);
sys_util = 100 * (stime_after - stime_before) / (time_total_after - time_total_before);

Ça a du sens?


11
Un "jiffy" est une unité de temps CPU. Ce à quoi cela correspond exactement en temps d'horloge murale dépend de l'architecture et de la configuration de votre noyau, mais l'important est de /proc/statvous dire combien de jiffies le CPU a exécuté au total, et de /proc/<PID>/statvous dire combien de jiffies ont été exécutés par un processus unique.
caf

1
@advocate: C'est un pseudo-fichier qui implémente une interface pour récupérer les statistiques d'exécution des processus depuis le noyau.
caf

4
Aux personnes qui recherchent plus d'informations sur les champs: man procest votre ami (recherche de /proc/[pid]/stat)
redShadow

1
En comparant la solution de caf avec celle fournie par zizzu (ci-dessous), la solution de caf donne les temps système et utilisateur séparément mais ne multiplie aucune de ces valeurs par le nombre de processeurs. Ne devrait-il pas faire ça?
linuxfan

2
Apparemment, l'OP n'était pas d'accord. L'idée clé ici est d'utiliser le /procpseudo-système de fichiers: l'enseignement des fonctions d'accès au système de fichiers C standard comme fopen()et scanf()est hors de propos.
caf

11

getrusage () peut vous aider à déterminer l'utilisation du processus actuel ou de son enfant

Mise à jour: je ne me souviens pas d'une API. Mais tous les détails seront dans / proc / PID / stat, donc si nous pouvons l'analyser, nous pouvons obtenir le pourcentage.

EDIT: Étant donné que le pourcentage de CPU n'est pas simple à calculer, vous pouvez utiliser ici un type d'échantillonnage. Lisez ctime et utime pour un PID à un moment donné et relisez les mêmes valeurs après 1 seconde. Trouvez la différence et divisez par cent. Vous obtiendrez l'utilisation de ce processus pendant une seconde.

(peut devenir plus complexe s'il y a de nombreux processeurs)


2
Comment l'appel système getrusage () m'aide-t-il à calculer l'utilisation du processeur d'un processus?
codingfreak le

@codingfreak. J'ai mal compris la question. Maintenant, après l'avoir mis à jour, effacez.
vpram86 le

1
@Aviator CPU% = (processusertime + processkerneltime) / (CPUusertime + CPUkerneltime) Comment puis-je obtenir les valeurs pour "processusertime" et ainsi de suite. ??? Je vois différentes valeurs dans le fichier "/ proc / PID / stat". Alors, lequel correspond à quelle valeur ??
codingfreak le

@codingfreak: le temps CPU est difficile à calculer. Vous devez parcourir toutes les statistiques PID je suppose (mais pas sûr)
vpram86

@Aviator il y aurait une manière ou une autre de le faire ... puisque même des applications comme top devraient calculer l'utilisation du processeur à afficher dans leur sortie
codingfreak

6

Étape facile à suivre pour les débutants comme moi:

  1. Lisez la première ligne de /proc/statpour obtenir total_cpu_usage1.
    sscanf(line,"%*s %llu %llu %llu %llu",&user,&nice,&system,&idle);
    total_cpu_usage1 = user + nice + system + idle;
  1. Lisez /proc/pid/statoù se pidtrouve le PID du processus dont vous souhaitez connaître l'utilisation du processeur, comme ceci:
    sscanf(line,
    "%*d %*s %*c %*d" //pid,command,state,ppid

    "%*d %*d %*d %*d %*u %*lu %*lu %*lu %*lu"

    "%lu %lu" //usertime,systemtime

    "%*ld %*ld %*ld %*ld %*ld %*ld %*llu"

    "%*lu", //virtual memory size in bytes
    ....)
  1. Maintenant somme usertimeet systemtimeet obtenirproc_times1
  2. Maintenant, attendez 1 seconde ou plus
  3. Faites-le à nouveau, et obtenez total_cpu_usage2etproc_times2

La formule est:

(number of processors) * (proc_times2 - proc_times1) * 100 / (float) (total_cpu_usage2 - total_cpu_usage1)

Vous pouvez obtenir la quantité de CPU à partir de /proc/cpuinfo.


2
Votre solution est bonne mais pour obtenir le nombre de processeurs, simplifiez-la. Inclure ce gars #include <unistd.h>et appeler cette méthodeint nb = sysconf(_SC_NPROCESSORS_ONLN);
David Guyon

1
Je ne comprends pas pourquoi multiplier par le (nombre de processeurs), en supposant que delta (proc_times) est pour le noyau sur lequel il s'exécute. Sans multiplier par le facteur ou les processeurs, cela devrait être correct.
JayabalanAaron

5

J'ai écrit deux petites fonctions C basées sur la réponse de cafs pour calculer l'utilisation du processeur utilisateur + noyau d'un processus: https://github.com/fho/code_snippets/blob/master/c/getusage.c


y a-t-il une version compilée de celui-ci car cela me donne une erreur lors de la compilation et comment puis-je l'utiliser?
Medhat

Je n'ai pas de fonction main () donc ce n'est pas un programme "autonome" que vous pouvez compiler et exécuter. Vous auriez à écrire une fonction main () qui fait certaines choses avec les fonctions de getusage.c
fho

Il n'utilise pas vraiment les fonctions C. Utiliser simplement un langage arbitraire pour analyser la sortie de commande.
Graham

4

Vous pouvez lire la page de manuel de proc pour plus de détails, mais en résumé, vous pouvez lire / proc / [nombre] / stat pour obtenir des informations sur un processus. Ceci est également utilisé par la commande «ps».

Tous les champs et leurs spécificateurs de format scanf sont documentés dans le manpag e proc .

Voici quelques-unes des informations de la page de manuel copiée (elle est assez longue):

          pid %d The process ID.

          comm %s
                 The  filename of the executable, in parentheses.  This is
                 visible whether or not the executable is swapped out.

          state %c
                 One character from the string "RSDZTW" where  R  is  runâ
                 ning,  S is sleeping in an interruptible wait, D is waitâ
                 ing in uninterruptible disk sleep,  Z  is  zombie,  T  is
                 traced or stopped (on a signal), and W is paging.

          ppid %d
                 The PID of the parent.

          pgrp %d
                 The process group ID of the process.

          session %d
                 The session ID of the process.

          tty_nr %d
                 The tty the process uses.

          tpgid %d
                 The  process group ID of the process which currently owns
                 the tty that the process is connected to.

«Utilisation du processeur» et «état actuel» sont comme l'emplacement et la vitesse. Si vous en connaissez un, vous ne pouvez pas connaître l'autre. L'utilisation du processeur dépend d'une durée, vous devez donc vérifier vous-même la fréquence à laquelle votre processus est dans l'état «R».
Bombe le

Hmm, bonne question, j'ai toujours supposé que ce serait là! Vraisemblablement, vous devriez être en mesure de le calculer à partir de ces variables
Andre Miller

Si vous vérifiez la sortie de la commande top, vous pouvez voir l'utilisation du processeur ... mais je ne suis pas intéressé par greping via la sortie supérieure pour calculer l'utilisation du processeur .....
codingfreak

@codingfreak: ps auxc'est mieux :)
vpram86

@Aviator - Je vous ai déjà dit d'oublier la grepping de la sortie d'une commande shell pour déterminer l'USAGE CPU%
codingfreak

2

Jetez un oeil à la commande "pidstat", sonne exactement ce dont vous avez besoin.


@James - Je ne parviens pas à accéder à la commande pidstat sur ma machine FEDORA 9.
codingfreak le

@codingfreak - vous devez installer l' outil Sysstat pour elle
Chintan Parikh

2

C'est ma solution ...

/*
this program is looking for CPU,Memory,Procs also u can look glibtop header there was a lot of usefull function have fun..
systeminfo.c
*/
#include <stdio.h>
#include <glibtop.h>
#include <glibtop/cpu.h>
#include <glibtop/mem.h>
#include <glibtop/proclist.h>



int main(){

glibtop_init();

glibtop_cpu cpu;
glibtop_mem memory;
glibtop_proclist proclist;

glibtop_get_cpu (&cpu);
glibtop_get_mem(&memory);


printf("CPU TYPE INFORMATIONS \n\n"
"Cpu Total : %ld \n"
"Cpu User : %ld \n"
"Cpu Nice : %ld \n"
"Cpu Sys : %ld \n"
"Cpu Idle : %ld \n"
"Cpu Frequences : %ld \n",
(unsigned long)cpu.total,
(unsigned long)cpu.user,
(unsigned long)cpu.nice,
(unsigned long)cpu.sys,
(unsigned long)cpu.idle,
(unsigned long)cpu.frequency);

printf("\nMEMORY USING\n\n"
"Memory Total : %ld MB\n"
"Memory Used : %ld MB\n"
"Memory Free : %ld MB\n"
"Memory Buffered : %ld MB\n"
"Memory Cached : %ld MB\n"
"Memory user : %ld MB\n"
"Memory Locked : %ld MB\n",
(unsigned long)memory.total/(1024*1024),
(unsigned long)memory.used/(1024*1024),
(unsigned long)memory.free/(1024*1024),
(unsigned long)memory.shared/(1024*1024),
(unsigned long)memory.buffer/(1024*1024),
(unsigned long)memory.cached/(1024*1024),
(unsigned long)memory.user/(1024*1024),
(unsigned long)memory.locked/(1024*1024));

int which,arg;
glibtop_get_proclist(&proclist,which,arg);
printf("%ld\n%ld\n%ld\n",
(unsigned long)proclist.number,
(unsigned long)proclist.total,
(unsigned long)proclist.size);
return 0;
}

makefile is
CC=gcc
CFLAGS=-Wall -g
CLIBS=-lgtop-2.0 -lgtop_sysdeps-2.0 -lgtop_common-2.0

cpuinfo:cpu.c
$(CC) $(CFLAGS) systeminfo.c -o systeminfo $(CLIBS)
clean:
rm -f systeminfo

1
Semble utiliser l'aide de la bibliothèque libgtop ..?
codingfreak

1
J'aime ça - la bibliothèque est simple. Je me demande s'il existe un moyen de voir quel est le% de la capacité totale de l'utilisation totale?
Cera

1

Lorsque vous souhaitez surveiller le processus spécifié, cela se fait généralement par script. Voici un exemple perl. Cela a mis les pourcentages de la même manière que top, le scalling à un CPU. Ensuite, lorsqu'un processus est actif fonctionnant avec 2 threads, l'utilisation du processeur peut être supérieure à 100%. Regardez en particulier comment les cœurs de processeur sont comptés: D puis laissez-moi vous montrer mon exemple:

#!/usr/bin/perl

my $pid=1234; #insert here monitored process PID

#returns current process time counters or single undef if unavailable
#returns:  1. process counter  , 2. system counter , 3. total system cpu cores
sub GetCurrentLoads {
    my $pid=shift;
    my $fh;
    my $line;
    open $fh,'<',"/proc/$pid/stat" or return undef;
    $line=<$fh>;
    close $fh;
    return undef unless $line=~/^\d+ \([^)]+\) \S \d+ \d+ \d+ \d+ -?\d+ \d+ \d+ \d+ \d+ \d+ (\d+) (\d+)/;
    my $TimeApp=$1+$2;
    my $TimeSystem=0;
    my $CpuCount=0;
    open $fh,'<',"/proc/stat" or return undef;
    while (defined($line=<$fh>)) {
        if ($line=~/^cpu\s/) {
            foreach my $nr ($line=~/\d+/g) { $TimeSystem+=$nr; };
            next;
        };
        $CpuCount++ if $line=~/^cpu\d/;
    }
    close $fh;
    return undef if $TimeSystem==0;
    return $TimeApp,$TimeSystem,$CpuCount;
}

my ($currApp,$currSys,$lastApp,$lastSys,$cores);
while () {
    ($currApp,$currSys,$cores)=GetCurrentLoads($pid);
    printf "Load is: %5.1f\%\n",($currApp-$lastApp)/($currSys-$lastSys)*$cores*100 if defined $currApp and defined $lastApp and defined $currSys and defined $lastSys;
    ($lastApp,$lastSys)=($currApp,$currSys);
    sleep 1;
}

J'espère que cela vous aidera dans tout suivi. Bien sûr, vous devriez utiliser scanf ou d'autres fonctions C pour convertir toutes les expressions rationnelles perl que j'ai utilisées en source C. Bien sûr, 1 seconde pour dormir n'est pas obligatoire. vous pouvez utiliser à tout moment. l'effet est que vous obtiendrez une charge moyenne sur une période de temps spécifiée. Quand vous l'utiliserez pour la surveillance, bien sûr les dernières valeurs que vous devriez mettre à l'extérieur. C'est nécessaire, car la surveillance appelle généralement des scripts périodiquement et le script doit terminer son travail dès que possible.


0

Installez psacctou créez un acctpackage. Utilisez ensuite la sacommande pour afficher le temps CPU utilisé pour diverses commandes. sa page de manuel

Un joli howto du site nixCraft.


0

Je pense qu'il vaut la peine de regarder le code source de la commande GNU "time". time Il affiche le temps CPU de l'utilisateur / système ainsi que le temps réel écoulé. Il appelle l'appel système wait3 / wait4 (si disponible) et sinon, il appelle l'appel système times. wait * l'appel système renvoie une variable struct "rusage" et fois l'appel système renvoie "tms". Vous pouvez également jeter un œil à l'appel système getrusage qui renvoie également des informations de synchronisation très intéressantes. temps


GNU "time" est pour le processus enfant de "time" uniquement
Graham

0

Au lieu d'analyser cela à partir de proc, on peut utiliser des fonctions comme getrusage () ou clock_gettime () et calculer l'utilisation du processeur en tant que ratio ou heure et heure du processus / thread utilisé sur le processeur.


getrusage clock_gettime sont limités, pas pour tous les processus
Graham

0

Utiliser strace a révélé que l'utilisation du processeur doit être calculée par période:

# top -b -n 1 -p 3889
top - 16:46:37 up  1:04,  3 users,  load average: 0.00, 0.01, 0.02
Tasks:   1 total,   0 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  5594496 total,  5158284 free,   232132 used,   204080 buff/cache
KiB Swap:  3309564 total,  3309564 free,        0 used.  5113756 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 3889 root      20   0  162016   2220   1544 S   0.0  0.0   0:05.77 top
# strace top -b -n 1 -p 3889
.
.
.
stat("/proc/3889", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/3889/stat", O_RDONLY)       = 7
read(7, "3889 (top) S 3854 3889 3854 3481"..., 1024) = 342
.
.
.

nanosleep({0, 150000000}, NULL)         = 0
.
.
.
stat("/proc/3889", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/3889/stat", O_RDONLY)       = 7
read(7, "3889 (top) S 3854 3889 3854 3481"..., 1024) = 342
.
.
.
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.