Como calcular o uso da CPU de um processo por PID no Linux a partir de C?


94

Desejo calcular programaticamente [em C] a porcentagem de uso da CPU para um determinado ID de processo no Linux.

Como podemos obter a% de uso da CPU em tempo real para um determinado processo?

Para deixar mais claro:

  • Devo ser capaz de determinar o uso da CPU para o processid ou processo fornecido.
  • O processo não precisa ser filho.
  • Quero a solução em linguagem 'C'.

que tal capturar (grep-ing) a saída do topo.
dusoft

Essa não é realmente a melhor maneira de ser eficiente; y
codingfreak

Provavelmente exigirá uma chamada de sistema "cara" para iniciar o 'topo'.
Liran Orevi,

@Liran: Disse com razão :)
vpram86

Esqueça essa maneira de fazer as coisas .... em C
codingfreak

Respostas:


148

Você precisa analisar os dados de /proc/<PID>/stat. Estes são os primeiros campos (da Documentation/filesystems/proc.txtfonte do 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

Você provavelmente está atrás utimee / ou stime. Você também precisará ler a cpulinha de /proc/stat, que se parece com:

cpu  192369 7119 480152 122044337 14142 9937 26747 0 0

Isso informa o tempo de CPU cumulativo que foi usado em várias categorias, em unidades de jiffies. Você precisa obter a soma dos valores nesta linha para obter uma time_totalmedida.

Leia ambos utimee stimepara o processo que você está interessado, e ler time_totala partir /proc/stat. Em seguida, durma por um segundo ou mais e leia todos novamente. Agora você pode calcular o uso da CPU do processo ao longo do tempo de amostragem, com:

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

Faz sentido?


11
Um "jiffy" é uma unidade de tempo de CPU. Exatamente a que corresponde no tempo de espera depende da arquitetura e de como seu kernel está configurado, mas o importante é que /proc/statinforma quantos jiffies a CPU executou no total e /proc/<PID>/statquantos jiffies foram executados por um único processo.
café de

1
@advocate: É um pseudo-arquivo que implementa uma interface para recuperar estatísticas de execução de processos do kernel.
caf

4
Para pessoas que buscam mais informações sobre os campos: man procis your friend (pesquisar por /proc/[pid]/stat)
redShadow

1
Comparando a solução de caf com a fornecida por zizzu (abaixo), a solução de caf fornece os tempos do sistema e do usuário separadamente, mas não multiplica nenhum desses valores pelo número de CPUs. Não deveria estar fazendo isso?
linuxfan

2
Aparentemente, o OP discordou. O principal insight aqui é usar o /procpseudo-sistema de arquivos: o ensino das funções de acesso do sistema de arquivos C padrão é semelhante fopen()e não scanf()vem ao caso.
caf

11

getrusage () pode ajudá-lo a determinar o uso do processo atual ou de seu filho

Atualização: não me lembro de uma API. Mas todos os detalhes estarão em / proc / PID / stat, então se pudermos analisá-los, podemos obter a porcentagem.

EDIT: Uma vez que CPU% não é fácil de calcular, você poderia usar o tipo de amostragem aqui. Leia ctime e utime para um PID em um ponto no tempo e leia os mesmos valores novamente após 1 segundo. Encontre a diferença e divida por cem. Você obterá a utilização desse processo por mais de um segundo.

(pode ficar mais complexo se houver muitos processadores)


2
Como a chamada de sistema getrusage () me ajuda no cálculo do uso da CPU de um processo?
codingfreak

@codingfreak. eu entendi mal a pergunta. Agora, depois de atualizar, limpe.
vpram86 de

1
@Aviator CPU% = (processusertime + processkerneltime) / (CPUusertime + CPUkerneltime) Como posso obter os valores para "processusertime" e assim por diante. ??? Vejo valores diferentes no arquivo "/ proc / PID / stat". Então, qual corresponde a qual valor ??
codingfreak

@codingfreak: o tempo de CPU é difícil de calcular. Você precisa percorrer todas as estatísticas PID, eu acho (embora não tenha certeza)
vpram86

@Aviator, haveria uma maneira ou outra de fazer isso ... já que até mesmo aplicativos como o top deveriam calcular o uso da CPU para mostrar em sua saída
codingfreak

6

Passo a passo fácil para iniciantes como eu:

  1. Leia a primeira linha de /proc/stat para obter total_cpu_usage1.
    sscanf(line,"%*s %llu %llu %llu %llu",&user,&nice,&system,&idle);
    total_cpu_usage1 = user + nice + system + idle;
  1. Ler /proc/pid/stat onde pidestá o PID do processo que deseja saber o uso da CPU, assim:
    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. Agora somar usertimee systemtimee getproc_times1
  2. Agora espere 1 segundo ou mais
  3. Faça de novo, e obtenha total_cpu_usage2eproc_times2

A fórmula é:

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

Você pode obter a quantidade de CPU de /proc/cpuinfo.


2
Sua solução é boa, mas para obter o número de CPUs, simplifique. Inclua esse cara #include <unistd.h>e chame este métodoint nb = sysconf(_SC_NPROCESSORS_ONLN);
David Guyon

1
Não entendo por que multiplicar pelo (número de processadores), assumindo que delta (proc_times) é para o núcleo em que foi executado. Sem multiplicar pelo fator ou CPUs, deve estar correto.
JayabalanAaron

5

Eu escrevi duas pequenas funções C com base na resposta de cafs para calcular o uso de CPU + kernel de um processo: https://github.com/fho/code_snippets/blob/master/c/getusage.c


existe uma versão compilada dele, pois me dá erro durante a compilação e também como posso usá-lo?
Medhat

Eu não tenho uma função main (), portanto, não é um programa "independente" do que você pode compilar e executar. Você teria que escrever uma função main () que faz algumas coisas com as funções de getusage.c
fho

Na verdade, não está usando funções C. Apenas usando uma linguagem arbitrária para analisar a saída do comando.
Graham

4

Você pode ler o manual do proc para obter mais detalhes, mas em resumo, você pode ler / proc / [número] / stat para obter as informações sobre um processo. Isso também é usado pelo comando 'ps'.

Todos os campos e seus especificadores de formato scanf são documentados no proc manpag e.

Aqui estão algumas das informações da página de manual copiada (é bem longa):

          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.

“Uso da CPU” e “estado atual” são como localização e velocidade. Se você conhece um, não pode conhecer o outro. O uso da CPU depende de uma duração, então você deve verificar a frequência com que seu processo está no estado “R”.
Bombe,

Hmm, boa pergunta, sempre presumi que estaria lá! Presumivelmente, você deve ser capaz de calculá-lo a partir dessas variáveis
Andre Miller,

Se você verificar a saída do comando top, poderá ver o uso da CPU ... mas não estou interessado em fazer o greping pela saída superior para calcular o uso da CPU ...
codingfreak

@codingfreak: ps auxis better :)
vpram86

@Aviator - Eu já disse a você para esquecer de usar o grep na saída de um comando shell para determinar a% de uso da CPU
codingfreak

2

Dê uma olhada no comando "pidstat", soa exatamente como você precisa.


@James - Não consigo acessar o comando pidstat em minha máquina FEDORA 9.
codingfreak

@codingfreak - você precisa instalar a ferramenta Sysstat para ele
Chintan Parikh,

2

Esta é a minha solução ...

/*
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 está usando a ajuda da biblioteca libgtop ..?
codingfreak

1
Eu gosto disso - a biblioteca é simples. Eu me pergunto se existe uma maneira de ver qual% da capacidade total é o uso total?
Cera

1

Quando você deseja monitorar o processo especificado, geralmente isso é feito por script. Aqui está um exemplo de perl. Isso coloca as porcentagens da mesma forma que no topo, transferindo-as para uma CPU. Então, quando algum processo está ativo trabalhando com 2 threads, o uso da CPU pode ser superior a 100%. Observe especialmente como os núcleos da CPU são contados: D, então, deixe-me mostrar meu exemplo:

#!/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 ajude em algum monitoramento. É claro que você deve usar scanf ou outras funções C para converter qualquer regexpes perl que usei em código-fonte C. Claro que 1 segundo para dormir não é obrigatório. você pode usar a qualquer momento. efeito é, você obterá carga média no período de tempo especificado. Quando você for usá-lo para monitorar, é claro que os últimos valores que você deve colocar de fora. É necessário, porque o monitoramento geralmente chama scripts periodicamente, e o script deve terminar seu trabalho o mais rápido possível.



0

Acho que vale a pena olhar o código-fonte do comando GNU "time". time Produz o tempo da CPU do usuário / sistema junto com o tempo real decorrido. Ele chama a chamada do sistema wait3 / wait4 (se disponível) e, caso contrário, ele chama a chamada do sistema vezes. wait * chamada do sistema retorna uma variável de estrutura "rusage" e vezes que a chamada do sistema retorna "tms". Além disso, você pode dar uma olhada na chamada do sistema getrusage, que também retorna informações de tempo muito interessantes. Tempo


GNU "tempo" é apenas para o processo filho de "tempo"
Graham

0

Em vez de analisar isso a partir de proc, pode-se usar funções como getrusage () ou clock_gettime () e calcular o uso da cpu como uma razão ou tempo de parede e tempo do processo / thread usado na cpu.


getrusage clock_gettime são limitados, não para todos os processos
Graham

0

Use strace encontrado que o uso da CPU precisa ser calculado por um período de tempo:

# 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
.
.
.
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.