Как рассчитать использование ЦП процессом по PID в Linux из C?


94

Я хочу программно [на C] вычислить процент использования ЦП для данного идентификатора процесса в Linux.

Как мы можем получить процент использования ЦП в реальном времени для данного процесса?

Чтобы прояснить ситуацию:

  • Я смогу определить использование ЦП для предоставленного идентификатора процесса или процесса.
  • Процесс не обязательно должен быть дочерним процессом.
  • Мне нужно решение на языке C.

как насчет перехвата (grep-ing) вывода top.
dusoft

Это действительно не лучший способ добиться эффективности; y
codingfreak

Вероятно, для запуска «сверху» потребуется «дорогой» системный вызов.
Лиран Ореви,

@Liran: Правильно сказано :)
vpram86

Забудьте об этом ... на C
codingfreak

Ответы:


149

Вам нужно разобрать данные из /proc/<PID>/stat. Это первые несколько полей (из Documentation/filesystems/proc.txtисходного кода ядра):

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

Вы, наверное, после utimeи / или stime. Вам также нужно будет прочитать cpuстроку /proc/stat, которая выглядит так:

cpu  192369 7119 480152 122044337 14142 9937 26747 0 0

Это показывает совокупное время ЦП, которое было использовано в различных категориях, в единицах мгновений. Вам нужно взять сумму значений в этой строке, чтобы получить time_totalмеру.

Читайте как utimeи stimeдля процесса вы заинтересованы в, и читать time_totalс /proc/stat. Затем поспите на секунду или около того и прочитайте их снова. Теперь вы можете рассчитать использование ЦП процессом за время выборки с помощью:

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

Есть смысл?


12
«Джиффи» - это единица процессорного времени. То, чему оно соответствует во времени настенных часов, зависит от архитектуры и того, как настроено ваше ядро, но важно то, что он /proc/statсообщает вам, сколько джиффов выполнил в целом ЦП, и /proc/<PID>/statсообщает вам, сколько джиффов было выполнено единый процесс.
caf,

1
@advocate: это псевдо-файл, который реализует интерфейс для получения статистики выполнения процесса из ядра.
caf

4
Людям, которые ищут дополнительную информацию о полях: man procis your friend (search for /proc/[pid]/stat)
redShadow

1
Сравнивая решение caf с решением, предоставленным zizzu (ниже), решение caf дает системное и пользовательское время отдельно, но не умножает ни одно из этих значений на количество процессоров. Разве этого не должно быть?
linuxfan

2
Видимо ОП не согласился. Ключевым моментом здесь является использование /procпсевдофайловой системы: обучение стандартным функциям доступа к файловой системе C, таким как fopen()и, не scanf()относится к делу .
caf

11

getrusage () может помочь вам определить использование текущего процесса или его дочернего процесса.

Обновление: я не могу вспомнить API. Но все детали будут в / proc / PID / stat, поэтому, если мы сможем его проанализировать, мы сможем получить процент.

РЕДАКТИРОВАТЬ: поскольку CPU% не просто вычислить, вы можете использовать здесь выборки. Считайте ctime и utime для PID в определенный момент времени и снова прочитайте те же значения через 1 секунду. Найдите разницу и разделите на сто. Вы получите использование этого процесса за последнюю секунду.

(может быть сложнее, если процессоров много)


2
Как системный вызов getrusage () помогает мне в вычислении использования процессора процессом?
codingfreak

@codingfreak. я неправильно понял вопрос. Теперь, когда вы обновили его, ясно.
vpram86

1
@Aviator CPU% = (processusertime + processkerneltime) / (CPUusertime + CPUkerneltime) Как я могу получить значения для "processusertime" и так далее. ??? Я вижу разные значения в файле "/ proc / PID / stat". Итак, какой из них соответствует какому значению?
codingfreak

@codingfreak: процессорное время сложно подсчитать. Вам нужно
прокрутить

@Aviator был бы так или иначе, чтобы сделать это ... поскольку даже такие приложения, как top, должны рассчитывать использование ЦП, чтобы отображать их в своих
выводах

7

Простой шаг за шагом для новичков вроде меня:

  1. Прочтите первую строку, /proc/statчтобы получить total_cpu_usage1.
    sscanf(line,"%*s %llu %llu %llu %llu",&user,&nice,&system,&idle);
    total_cpu_usage1 = user + nice + system + idle;
  1. Прочтите, /proc/pid/statгде pidнаходится PID процесса, который вы хотите узнать об использовании ЦП, например:
    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. Теперь просуммировать usertimeи systemtimeи получитьproc_times1
  2. Теперь подождите 1 секунду или больше
  3. Сделай это снова и получи total_cpu_usage2иproc_times2

Формула:

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

Вы можете получить количество CPU от /proc/cpuinfo.


2
Ваше решение хорошее, но чтобы получить количество процессоров, упростите его. Включите этого парня #include <unistd.h>и назовите этот методint nb = sysconf(_SC_NPROCESSORS_ONLN);
Дэвид Гайон

1
Я не понимаю, зачем умножать на (количество процессоров), предполагая, что delta (proc_times) предназначена для ядра, на котором он выполняется. Без умножения на коэффициент или количество процессоров, это должно быть правильно.
JayabalanAaron

5

Я написал две маленькие функции C на основе ответа cafs для расчета использования процессора пользователем + ядром для процесса: https://github.com/fho/code_snippets/blob/master/c/getusage.c


есть ли его скомпилированная версия, поскольку она дает мне ошибку при компиляции, а также как я могу ее использовать?
Medhat

У меня нет функции main (), поэтому это не «отдельная» программа, которую вы можете скомпилировать и выполнить. Вам нужно будет написать функцию main (), которая кое-что делает с функциями getusage.c
fho

На самом деле он не использует функции C. Просто использование произвольного языка для анализа вывода команд.
Грэм

4

Вы можете прочитать справочную страницу для proc для получения более подробной информации, но в целом вы можете прочитать / proc / [number] / stat, чтобы получить информацию о процессе. Это также используется командой ps.

Все поля и их спецификаторы формата scanf задокументированы в proc manpag e.

Вот часть информации из скопированной man-страницы (она довольно длинная):

          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.

«Использование процессора» и «текущее состояние» подобны местоположению и скорости. Если вы знаете одно, вы не можете знать другого. Использование ЦП зависит от продолжительности, поэтому вы должны сами проверить, как часто ваш процесс находится в состоянии «R».
Bombe

Хм, хороший вопрос, я всегда предполагал, что он будет там! По-видимому, вы сможете рассчитать его по этим переменным
Андре Миллер,

Если вы проверите вывод команды top, вы можете увидеть использование ЦП .... но меня не интересует вывод данных верхнего уровня для расчета использования ЦП ...
codingfreak

@codingfreak: ps auxлучше :)
vpram86

@Aviator - я уже сказал вам забыть о выводе команды оболочки, чтобы определить процент использования ЦП
codingfreak

2

Взгляните на команду "pidstat", она звучит именно так, как вам нужно.


@James - Я не могу получить доступ к команде pidstat на моем компьютере FEDORA 9.
codingfreak

@codingfreak - для этого вам необходимо установить инструмент
Sysstat

2

Это мое решение ...

/*
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
Кажется, с помощью библиотеки libgtop ..?
codingfreak

1
Мне это нравится - библиотека простая. Интересно, есть ли способ узнать, какой% от общей емкости составляет общее использование?
Cera

1

Когда вы хотите отслеживать указанный процесс, обычно это делается с помощью скриптов. Вот пример Perl. Это ставит проц так же, как и верхнюю, с привязкой к одному процессору. Тогда, когда какой-то процесс активен, работающий с 2 ​​потоками, использование процессора может быть более 100%. Особенно посмотрите, как подсчитываются ядра процессора: D, тогда позвольте мне показать свой пример:

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

Надеюсь, это поможет вам в любом мониторинге. Конечно, вы должны использовать scanf или другие функции C для преобразования любых регулярных выражений Perl, которые я использовал, в исходный код C. Конечно, 1 секунда для сна не обязательна. вы можете использовать в любое время. эффект в том, что вы получите среднюю нагрузку за указанный период времени. Когда вы будете использовать его для мониторинга, конечно, последние значения вы должны выставить снаружи. Это необходимо, потому что мониторинг обычно периодически вызывает скрипты, и скрипт должен завершить свою работу как можно скорее.



0

Думаю, стоит посмотреть исходный код команды GNU "time". time Выводит время процессора пользователя / системы вместе с реальным прошедшим временем. Он вызывает системный вызов wait3 / wait4 (если доступен), а в противном случае вызывает системный вызов times. Системный вызов wait * возвращает структурную переменную "rusage", а системный вызов раз возвращает "tms". Также вы можете взглянуть на системный вызов getrusage, который также возвращает очень интересную информацию о времени. время


GNU «время» предназначено только для дочернего процесса «времени»
Грэм

0

Вместо того, чтобы разбирать это из процедуры, можно использовать такие функции, как getrusage () или clock_gettime (), и вычислить использование процессора как соотношение или время настенных часов и время процесса / потока, используемого на процессоре.


getrusage clock_gettime ограничены не для всех процессов
Грэм

0

Использование strace обнаружило, что использование ЦП необходимо рассчитывать по периоду времени:

# 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
.
.
.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.