¿Cómo calcular el uso de CPU de un proceso por PID en Linux desde C?


94

Quiero calcular programáticamente [en C] el% de uso de CPU para un ID de proceso dado en Linux.

¿Cómo podemos obtener el% de uso de CPU en tiempo real para un proceso determinado?

Para dejarlo más claro:

  • Debería poder determinar el uso de la CPU para el proceso o id de proceso proporcionado.
  • El proceso no tiene por qué ser el proceso hijo.
  • Quiero la solución en lenguaje 'C'.

¿qué pasa con la captura (grep-ing) de salida de top.
dusoft

Esa no es realmente la mejor manera de hacerlo eficiente; y
codingfreak

Probablemente requerirá una llamada al sistema "costosa" para comenzar 'arriba'.
Liran Orevi

@Liran: Con razón :)
vpram86

Olvídate de esta forma de hacer las cosas ... en C
codingfreak

Respuestas:


148

Necesita analizar los datos de /proc/<PID>/stat. Estos son los primeros campos (de Documentation/filesystems/proc.txtla fuente del kernel):

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

Probablemente estés buscando utimey / o stime. También necesitará leer la cpulínea de /proc/stat, que se ve así:

cpu  192369 7119 480152 122044337 14142 9937 26747 0 0

Esto le indica el tiempo de CPU acumulado que se ha utilizado en varias categorías, en unidades de jiffies. Debe tomar la suma de los valores en esta línea para obtener una time_totalmedida.

Leer tanto utimey stimepara el proceso que le interesa, y leer time_totala partir /proc/stat. Luego duerme un segundo más o menos y vuelve a leerlos todos. Ahora puede calcular el uso de CPU del proceso durante el tiempo de muestreo, con:

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);

¿Tener sentido?


11
Un "jiffy" es una unidad de tiempo de CPU. Exactamente a lo que corresponde en el tiempo del reloj de pared depende de la arquitectura y de cómo esté configurado su kernel, pero lo importante es que /proc/statle dice cuántos jiffies ha ejecutado la CPU en total, y /proc/<PID>/statle dice cuántos jiffies ha ejecutado un proceso único.
Café

1
@advocate: Es un pseudoarchivo que implementa una interfaz para recuperar estadísticas de ejecución de procesos del kernel.
caf

4
Para las personas que buscan más información sobre los campos: man procis your friend (search for /proc/[pid]/stat)
redShadow

1
Al comparar la solución de caf con la proporcionada por zizzu (abajo), la solución de caf da los tiempos del sistema y del usuario por separado, pero no multiplica ninguno de estos valores por el número de CPU. ¿No debería estar haciendo eso?
linuxfan

2
Al parecer, el OP no estuvo de acuerdo. La idea clave aquí es utilizar el /procpseudo sistema de archivos: la enseñanza de las funciones de acceso al sistema de archivos C estándar como fopen()y no scanf()viene al caso.
caf

11

getrusage () puede ayudarlo a determinar el uso del proceso actual o su hijo

Actualización: no recuerdo una API. Pero todos los detalles estarán en / proc / PID / stat, así que si pudiéramos analizarlo, obtendríamos el porcentaje.

EDITAR: Dado que el porcentaje de CPU no es sencillo de calcular, puede usar el tipo de muestreo aquí. Lea ctime y utime para un PID en un momento determinado y vuelva a leer los mismos valores después de 1 segundo. Encuentra la diferencia y divide por cien. Obtendrá la utilización de ese proceso durante más de un segundo.

(puede volverse más complejo si hay muchos procesadores)


2
¿Cómo me ayuda la llamada al sistema getrusage () a calcular el uso de CPU de un proceso?
codingfreak

@codingfreak. Entendí mal la pregunta. Ahora, después de actualizarlo, claro.
vpram86

1
@Aviator CPU% = (processusertime + processkerneltime) / (CPUusertime + CPUkerneltime) ¿Cómo puedo obtener los valores de "processusertime" y así sucesivamente? ??? Veo diferentes valores en el archivo "/ proc / PID / stat". Entonces, ¿cuál corresponde a qué valor?
codingfreak

@codingfreak: el tiempo de CPU es difícil de calcular. U necesita recorrer todas las estadísticas PID, supongo (aunque no estoy seguro)
vpram86

@Aviator, habría una forma u otra de hacerlo ... ya que incluso aplicaciones como top deberían calcular el uso de la CPU para mostrar en su salida
codingfreak

6

Paso a paso fácil para principiantes como yo:

  1. Lea la primera línea de /proc/statpara obtener total_cpu_usage1.
    sscanf(line,"%*s %llu %llu %llu %llu",&user,&nice,&system,&idle);
    total_cpu_usage1 = user + nice + system + idle;
  1. Lea /proc/pid/statdónde pidestá el PID del proceso en el que desea conocer el uso de la CPU, así:
    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. Ahora resumir usertimey systemtimee getproc_times1
  2. Ahora espera 1 segundo o más
  3. Hazlo de nuevo y obtén total_cpu_usage2yproc_times2

La formula es:

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

Puede obtener la cantidad de CPU de /proc/cpuinfo.


2
Su solución es buena, pero para obtener la cantidad de CPU, hágalo más simple. Incluir a este tipo #include <unistd.h>y llamar a este métodoint nb = sysconf(_SC_NPROCESSORS_ONLN);
David Guyon

1
No entiendo por qué multiplicar por el (número de procesadores), asumiendo que delta (proc_times) es para el núcleo en el que se ejecutó. Sin multiplicar por el factor o las CPU, debería ser correcto.
JayabalanAaron

5

Escribí dos pequeñas funciones de C basadas en la respuesta de cafs para calcular el uso de la CPU de usuario + kernel de un proceso: https://github.com/fho/code_snippets/blob/master/c/getusage.c


¿Hay una versión compilada de la misma, ya que me da errores durante la compilación y también cómo puedo usarla?
Medhat

No tengo una función main (), por lo tanto, no es un programa "independiente" que pueda compilar y ejecutar. Tendría que escribir una función main () que haga algunas cosas con las funciones de getusage.c
fho

Realmente no está usando funciones C. Simplemente usando un lenguaje arbitrario para analizar la salida del comando.
Graham

4

Puede leer la página de manual de proc para obtener más detalles, pero en resumen puede leer / proc / [número] / stat para obtener la información sobre un proceso. Esto también lo usa el comando 'ps'.

Todos los campos y sus especificadores de formato scanf están documentados en proc manpag e.

A continuación, se muestra parte de la información de la página de manual copiada (es bastante larga):

          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.

El "uso de la CPU" y el "estado actual" son como la ubicación y la velocidad. Si conoces a uno, no puedes conocer al otro. El uso de la CPU depende de la duración, por lo que debe verificar la frecuencia con la que su proceso está en el estado "R".
Bombe

Hmm, buena pregunta, ¡siempre asumí que estaría ahí! Presumiblemente, debería poder calcularlo a partir de estas variables
Andre Miller

Si verifica la salida del comando superior, puede ver el uso de la CPU ... pero no estoy interesado en hacer greping a través de la salida superior para calcular el uso de la CPU ...
codingfreak

@codingfreak: ps auxes mejor :)
vpram86

@Aviator: ya te he dicho que te olvides de hacer grepping en la salida de un comando de shell para determinar el% de USO de la CPU
codingfreak

2

Eche un vistazo al comando "pidstat", parece exactamente lo que necesita.


@James: no puedo acceder al comando pidstat en mi máquina FEDORA 9.
codingfreak

@codingfreak - necesita instalar la herramienta Sysstat para ello
Chintan Parikh

2

Esta es mi solución ...

/*
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
Parece que utiliza la ayuda de la biblioteca libgtop ..?
codingfreak

1
Me gusta esto: la biblioteca es sencilla. Me pregunto si hay una manera de ver qué porcentaje de la capacidad total es el uso total.
Cera

1

Cuando desee supervisar un proceso específico, normalmente se realiza mediante secuencias de comandos. Aquí está un ejemplo de Perl. Esto coloca los porcentajes de la misma manera que top, escalando a una CPU. Luego, cuando algún proceso está activo trabajando con 2 subprocesos, el uso de la CPU puede ser superior al 100%. Mire especialmente cómo se cuentan los núcleos de la CPU: D luego déjeme mostrar mi ejemplo:

#!/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;
}

Espero que te ayude en cualquier seguimiento. Por supuesto, debería usar scanf u otras funciones de C para convertir cualquier expresión regular de Perl que haya usado en fuente C. Por supuesto, 1 segundo para dormir no es obligatorio. se puede utilizar en cualquier momento. El efecto es que obtendrá una carga promedio en un período de tiempo especificado. Cuando lo usará para monitorear, por supuesto, los últimos valores deben poner afuera. Es necesario, porque la supervisión suele llamar a los scripts periódicamente, y el script debería terminar su trabajo lo antes posible.



0

Creo que vale la pena mirar el código fuente del comando "time" de GNU. tiempo Muestra el tiempo de la CPU del sistema / usuario junto con el tiempo real transcurrido. Llama a la llamada al sistema wait3 / wait4 (si está disponible) y, de lo contrario, llama a la llamada al sistema de tiempos. esperar * la llamada al sistema devuelve una variable de estructura "rusage" y las veces que la llamada al sistema devuelve "tms". Además, puede echar un vistazo a la llamada al sistema getrusage que también devuelve información de tiempo muy interesante. hora


El "tiempo" de GNU es solo para el proceso secundario de "tiempo"
Graham

0

En lugar de analizar esto desde proc, se pueden usar funciones como getrusage () o clock_gettime () y calcular el uso de la CPU como una relación o el tiempo del reloj de pared y el tiempo del proceso / subproceso utilizado en la CPU.


getrusage clock_gettime son limitados, no para todos los procesos
Graham

0

Use strace encontró que el uso de la CPU debe calcularse por un período de tiempo:

# 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
.
.
.
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.