Jak obliczyć użycie procesora przez proces przez PID w Linuksie z C?


95

Chcę programowo [w C] obliczyć% użycia procesora dla danego identyfikatora procesu w systemie Linux.

Jak możemy uzyskać procentowe użycie procesora w czasie rzeczywistym dla danego procesu?

Aby było to bardziej jasne:

  • Powinienem być w stanie określić użycie procesora przez podany identyfikator procesu lub proces.
  • Proces nie musi być procesem potomnym.
  • Chcę rozwiązania w języku „C”.

a co z łapaniem (grep-owaniem) wyjścia top.
dusoft

To naprawdę nie jest najlepszy sposób na wydajną pracę; y
codingfreak

Prawdopodobnie będzie wymagało „drogiego” wywołania systemowego, aby rozpocząć „od góry”.
Liran Orevi

@Liran: Słusznie powiedział :)
vpram86

Zapomnij o tym sposobie robienia rzeczy ... w C
codingfreak

Odpowiedzi:


153

Musisz przeanalizować dane z /proc/<PID>/stat. Oto kilka pierwszych pól (z Documentation/filesystems/proc.txttwojego źródła jądra):

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

Prawdopodobnie szukasz utimei / lub stime. Musisz także przeczytać cpuwiersz z /proc/stat, który wygląda następująco:

cpu  192369 7119 480152 122044337 14142 9937 26747 0 0

To pokazuje skumulowany czas procesora, który był używany w różnych kategoriach, w jednostkach migotania. Aby uzyskać time_totalmiarę, musisz wziąć sumę wartości w tej linii .

Czytaj zarówno utimei stimedla procesu jesteś zainteresowany i czytać time_totalz /proc/stat. Następnie prześpij się na sekundę i przeczytaj je ponownie. Możesz teraz obliczyć użycie procesora przez proces w czasie próbkowania za pomocą:

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

Ma sens?


12
„Mrugnięcie” to jednostka czasu procesora. Dokładnie to, co odpowiada czasowi zegara ściennego, zależy od architektury i konfiguracji jądra, ale ważne jest, aby /proc/statpowiedzieć, ile migotów wykonał w sumie procesor i /proc/<PID>/statile migów zostało wykonanych przez pojedynczy proces.
kawiarnia

1
@advocate: jest to pseudo-plik, który implementuje interfejs do pobierania statystyk wykonania procesu z jądra.
kawiarnia

4
Do osób poszukujących więcej informacji na temat pól: man procczy twój przyjaciel (szukaj /proc/[pid]/stat)
redShadow

1
Porównując rozwiązanie caf z rozwiązaniem dostarczonym przez zizzu (poniżej), rozwiązanie caf podaje oddzielnie czasy systemu i użytkownika, ale nie mnoży żadnej z tych wartości przez liczbę procesorów. Czy nie powinno to robić?
linuxfan

2
Najwyraźniej PO nie zgodził się z tym. Kluczowym spostrzeżeniem jest tutaj użycie /procpseudo-systemu plików: nauczanie standardowych funkcji dostępu do systemu plików C, takich jak fopen()i scanf()jest poza tematem.
kawiarnia

11

getrusage () może pomóc w określeniu użycia bieżącego procesu lub jego elementu podrzędnego

Aktualizacja: nie pamiętam API. Ale wszystkie szczegóły będą w / proc / PID / stat, więc gdybyśmy mogli to przeanalizować, moglibyśmy uzyskać procent.

EDYCJA: Ponieważ CPU% nie jest łatwe do obliczenia, możesz użyć tutaj próbkowania. Odczytaj ctime i utime dla PID w danym momencie i przeczytaj te same wartości ponownie po 1 sek. Znajdź różnicę i podziel przez sto. Będziesz korzystać z tego procesu przez ostatnią sekundę.

(może się skomplikować, jeśli jest wiele procesorów)


2
W jaki sposób wywołanie systemowe getrusage () pomaga mi w obliczaniu użycia procesora przez proces?
codingfreak

@codingfreak. źle zrozumiałem pytanie. Teraz, po zaktualizowaniu, wyczyść.
vpram86

1
@Aviator CPU% = (processusertime + processkerneltime) / (CPUusertime + CPUkerneltime) Jak mogę uzyskać wartości dla „processusertime” i tak dalej. ??? Widzę różne wartości w pliku "/ proc / PID / stat". Więc który z nich odpowiada jakiej wartości?
codingfreak

@codingfreak: Czas procesora jest trudny do obliczenia. Musisz przejrzeć wszystkie statystyki PID (choć nie jestem pewien)
vpram86

@Aviator byłby jakiś sposób, aby to zrobić ... ponieważ nawet aplikacje takie jak top powinny obliczać użycie procesora, aby pokazać je w swoich danych wyjściowych
codingfreak

7

Łatwy krok dla początkujących takich jak ja:

  1. Przeczytaj pierwszą linię, /proc/stataby uzyskać total_cpu_usage1.
    sscanf(line,"%*s %llu %llu %llu %llu",&user,&nice,&system,&idle);
    total_cpu_usage1 = user + nice + system + idle;
  1. Przeczytaj, /proc/pid/statgdzie pidjest PID procesu, o którym chcesz wiedzieć, użycie procesora, na przykład:
    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. Teraz suma usertimea systemtimei dostaćproc_times1
  2. Teraz poczekaj 1 sekundę lub dłużej
  3. Zrób to jeszcze raz i zdobądź total_cpu_usage2iproc_times2

Formuła to:

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

Możesz uzyskać ilość procesorów z /proc/cpuinfo.


2
Twoje rozwiązanie jest dobre, ale aby uzyskać liczbę procesorów, uprość je. Uwzględnij tego gościa #include <unistd.h>i nazwij tę metodęint nb = sysconf(_SC_NPROCESSORS_ONLN);
David Guyon

1
Nie rozumiem, dlaczego mnożyć przez (liczbę procesorów), zakładając, że delta (proc_times) dotyczy rdzenia, na którym został wykonany. Bez mnożenia przez współczynnik lub procesory, powinno być poprawne.
JayabalanAaron

5

Napisałem dwie małe funkcje C oparte na odpowiedzi cafs, aby obliczyć użycie procesora przez użytkownika + jądro procesora: https://github.com/fho/code_snippets/blob/master/c/getusage.c


czy istnieje skompilowana wersja tego, ponieważ daje mi błąd podczas kompilacji, a także jak mogę z niej korzystać?
Medhat

Nie mam funkcji main (), dlatego nie jest to program „samodzielny”, który można skompilować i wykonać. Musiałbyś napisać funkcję main (), która robi pewne rzeczy z funkcjami getusage.c
fho

Tak naprawdę nie używa funkcji C. Po prostu używając dowolnego języka do analizowania wyników polecenia.
Graham,

4

Możesz przeczytać stronę podręcznika dla proc, aby uzyskać więcej szczegółów, ale podsumowując, możesz przeczytać / proc / [numer] / stat, aby uzyskać informacje o procesie. Jest to również używane przez polecenie „ps”.

Wszystkie pola i ich specyfikatory formatu scanf są udokumentowane na stronie podręcznika proc .

Oto niektóre informacje ze skopiowanej strony podręcznika (jest dość długa):

          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.

„Użycie procesora” i „aktualny stan” są jak lokalizacja i prędkość. Jeśli znasz jedno, nie możesz znać drugiego. Zużycie procesora zależy od czasu trwania, więc musisz sam sprawdzić, jak często Twój proces jest w stanie „R”.
Bombe

Hmm, dobre pytanie, zawsze zakładałem, że tam będzie! Prawdopodobnie powinieneś być w stanie obliczyć to na podstawie tych zmiennych
Andre Miller,

Jeśli sprawdzisz dane wyjściowe polecenia górnego, zobaczysz użycie procesora ... ale nie interesuje mnie przeglądanie przez górne wyjście w celu obliczenia użycia procesora .....
codingfreak

@codingfreak: ps auxis better :)
vpram86

@Aviator - Już ci powiedziałem, żebyś zapomniał o grepowaniu wyjścia polecenia powłoki w celu określenia% ZUŻYCIA PROCESORA
codingfreak

2

Spójrz na polecenie „pidstat”, brzmi dokładnie tak, jak potrzebujesz.


@James - Nie mogę uzyskać dostępu do polecenia pidstat w mojej maszynie FEDORA 9.
codingfreak

@codingfreak - musisz zainstalować dla niego narzędzie Sysstat
Chintan Parikh

2

To jest moje rozwiązanie ...

/*
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
Wydaje się, że korzystasz z pomocy biblioteki libgtop ..?
codingfreak

1
Podoba mi się to - biblioteka jest prosta. Zastanawiam się, czy istnieje sposób, aby sprawdzić, jaki% całkowitej pojemności stanowi całkowite użycie?
Cera

1

Kiedy chcesz monitorować określony proces, zwykle odbywa się to za pomocą skryptów. Oto przykład Perla. W ten sposób procenty są takie same jak w przypadku góry, sprowadzając je do jednego procesora. Następnie, gdy jakiś proces jest aktywny, pracując z 2 wątkami, użycie procesora może przekraczać 100%. Specjalnie spójrz, jak liczone są rdzenie procesora: D, a następnie pokażę mój przykład:

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

Mam nadzieję, że pomoże ci to w każdym monitorowaniu. Oczywiście powinieneś użyć scanf lub innych funkcji C do konwersji wszelkich wyrażeń regularnych Perla, których używałem, na źródła C. Oczywiście 1 sekunda na spanie nie jest obowiązkowa. możesz użyć w dowolnym momencie. Efekt jest taki, że otrzymasz średnie obciążenie w określonym okresie czasu. Kiedy użyjesz go do monitorowania, oczywiście ostatnie wartości powinieneś umieścić na zewnątrz. Jest to potrzebne, ponieważ monitorowanie zwykle wywołuje skrypty okresowo, a skrypt powinien jak najszybciej zakończyć swoją pracę.



0

Myślę, że warto przyjrzeć się kodowi źródłowemu komend GNU „time”. time Wyświetla czas procesora użytkownika / systemu wraz z rzeczywistym czasem, który upłynął. Wywołuje wywołanie systemowe wait3 / wait4 (jeśli jest dostępne), w przeciwnym razie wywołuje wywołanie systemowe times. wait * wywołanie systemowe zwraca zmienną strukturalną "rusage" i razy, gdy wywołanie systemowe zwróci "tms". Możesz także rzucić okiem na wywołanie systemowe getrusage, które również zwraca bardzo interesujące informacje o czasie. czas


„Czas” GNU jest tylko dla procesu potomnego „czasu”
Graham

0

Zamiast analizować to z proc, można użyć funkcji takich jak getrusage () lub clock_gettime () i obliczyć użycie procesora jako stosunek lub czas wallclock i czas procesu / wątku używany na procesorze.


getrusage clock_gettime są ograniczone, nie dla wszystkich procesów
Graham

0

Use strace stwierdził, że użycie procesora musi być obliczone według przedziału czasu:

# 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
.
.
.
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.