- p°edchozφ Φlßnek - nßsledujφcφ Φlßnek - obsah - ·vodnφ strßnka -

LinuxovΘ noviny Duben 1998

Programujeme v C s libproc

Karel «ßk, 10. dubna 1998

V nßsledujφcφm Φlßnku se pokusφm vßs seznßmit s jednou velmi jednoduchou knihovnou pro vytvß°enφ program∙ pracujφcφch s daty o procesech a systΘmu. ╚asto je sly╣et hlasy, kterΘ tvrdφ, ╛e cΘΦko je slo╛itΘ a v jin²ch programovacφch jazycφch lze programovat daleko rychleji atd. Osobn∞ jsem toho nßzoru, ╛e obtφ╛nost, s jakou lze naprogramovat to Φi ono, je vφce ne╛ vlastnφm jazykem dßna existencφ knihoven, kterΘ °e╣φ obtφ╛nΘ nebo (na naprogramovßnφ) zdlouhavΘ ·koly za programßtora. Jednou takovou (°e╣φcφ) knihovnou je i libproc.

O libproc se starß v souΦasnΘ dob∞ Helmut Geye Helmut.Geyer@iwr.uni-heidelberg.de. Knihovna je ve v∞t╣in∞ distribucφ ve verzi 1.2.6. Dßle je dostupnß i experimentßlnφ verze tΘto knihovny a to verze 1.12.2. Osobn∞ se mi lφbφ vφce 1.12.2. Ta obsahuje daleko vφce maker a i po strßnce mno╛stvφ dat o jednotliv²ch procesech je daleko obsa╛n∞j╣φ. DoporuΦuji dßt si velk² pozor na rozdφlnost t∞chto knihoven p°i psanφ program∙. Stejn²mi verzemi jako knihovny jsou znaΦeny i programy ps, top, uptime, pstree, free a to proto, ╛e v╣e je distribuovßno v balφku procps. Zajφmavostφ m∙╛e b²t, ╛e v Debianu v unstable distribution je verze 1.2.6 a ve stable distribution je verze 1.12.2. V²vojß°skΘ knihovny jsou v balφku libproc-dev odpovφdajφcφ verze.

Ale zp∞t k vlastnφmu programovßnφ. ZaΦneme tφm jednodu╣╣φm. Pokusφme se ud∞lat progrßmek, kter² nßm vypφ╣e na standardnφ v²stup aktußlnφ uptime a obsazenφ pam∞ti a swapu na╣eho poΦφtaΦe. Pokud bychom tento ·kol cht∞li splnit bez libproc znamenalo by to p°eΦφst soubory /proc/uptime a /proc/meminfo a obojφ analyzovat (a to v p°φpad∞ uptimu nenφ zas takovß banalita). ╪e╣enφ pomocφ libproc je uvedeno v p°φkladu Pou╛itφ knihovny libproc. Knihovna umo╛≥uje u uptimu p°φmo tisk uptime stringu na standardnφ v²stup a to pomocφ print_uptime(void), takto je ostatn∞ ud∞lßn i program uptime(1). S ·daji o pam∞ti je to trochu slo╛it∞j╣φ. Zde je pomocφ funkce meminfo vrßceno pole typu unsigned (**mem). To obsahuje dv∞ °ßdky: meminfo_main a meminfo_swap. A ka╛dß °ßdka n∞kolik sloupc∙: meminfo_free, meminfo_total atd. Kombinacφ sloupc∙ a °ßdek se lze dobrat jednotliv²ch ·daj∙. Ty jsou v na╣em p°φpad∞ je╣t∞ p°ed vypsßnφm p°evedeny z b na kB (bitovß operace >> 10). A to je v╣e na cca 30 °ßdcφch. Snad je╣t∞ poznßmka pro ty co by nev∞d∞li jak kompilovat:

gcc -Wall -O2 prog.c -o prog -lproc 

/* ----- p°φklad 1. ----- */
#include <stdio.h>
#include <proc/sysinfo.h>       /* meminfo()      */
#include <proc/whattime.h>      /* print_uptime() */

#define RE_ERROR        1
#define RE_OK           0

   int main()
   {
      unsigned **mem;   

      puts("\nUPTIME:");                           
      print_uptime();   

      if (!(mem = meminfo())) 
         exit(RE_ERROR);

      puts("\nSYSTEM MEMORY (kB):");                           
      printf("\tTotal  : %u\n",  (mem[meminfo_main][meminfo_total]   >> 10));
      printf("\tFree   : %u\n",  (mem[meminfo_main][meminfo_free]    >> 10));
      printf("\tUsed   : %u\n",  (mem[meminfo_main][meminfo_used]    >> 10));
      printf("\tShared : %u\n",  (mem[meminfo_main][meminfo_shared]  >> 10));
      printf("\tBuffers: %u\n",  (mem[meminfo_main][meminfo_buffers] >> 10));
      printf("\tCached : %u\n",  (mem[meminfo_main][meminfo_cached]  >> 10));
      puts("\nSYSTEM SWAP (kB):");                           
      printf("\tTotal  : %u\n",  (mem[meminfo_swap][meminfo_total]   >> 10));
      printf("\tFree   : %u\n",  (mem[meminfo_swap][meminfo_free]    >> 10));   
      printf("\tUsed   : %u\n\n",(mem[meminfo_swap][meminfo_used]    >> 10));

      exit(RE_OK);
   }

V²pis Φ. 2: Pou╛itφ knihovny libproc

Te∩ o n∞co t∞╛╣φ progrßmek, kter² zjistφ jak² proces pou╛φvß nejvφce rezistentnφ pam∞ti (tedy tΘ, kterß nenφ swapovßna) a jak² proces nejvφce zat∞╛uje procesor. ╪e╣enφ je uvedeno na v²pise Pou╛itφ knihovny libproc podruhΘ. Na poΦßtku je nutnΘ nastavit verzi Linuxu (set_linux_version()), proto╛e pokud to neud∞lßte, m∙╛ete se doΦkat i pon∞kud zkreslen²ch ·daj∙ (m∞ se to stalo nap°. u ttyc). Pak je nutnΘ nastavit ΦasovΘ ·daje, kterΘ budeme pot°ebovat pro v²poΦet zatφ╛enφ procesoru. Zde se pozastavφm - funkce get_pcpu() je p∙vodnφ funkce, kterou pou╛φvß program ps(1) po v²poΦet %CPU (proto ty podivnΘ prom∞nnΘ...). Tato funkce umo╛≥uje bu∩ poΦφtat zatφ╛enφ CPU pro vlastnφ proces nebo i vΦetn∞ potomk∙ (cumulative). Proto i mo╛n² parametr -c u na╣eho programu. A pak u╛ nßsleduje vlastnφ naΦtenφ dat o procesech. Libproc umo╛≥uje naΦφst informace bu∩ o jednom procesu (readproc()) nebo pro vφce proces∙ (readproctab()). Informace o procesech jsou t∞mito funkcemi vrßceny v pointeru na strukturu typu proc_t, ta je definovßna a okomentovßna v hlaviΦkovΘ souboru readproc.h. Struktura obsahuje asi 60 r∙zn²ch ·daj∙ o danΘm procesu a to od UID (verze 1.12.2 rozli╣uje n∞kolik typ∙ UID, GID, USER, GROUP) a╛ po takovΘ v∞ci jako detailnφ rozli╣enφ kolik pam∞ti je pou╛ito knihovnami procesu, kolik je sdφleno, jak² je pam∞╗ov² limit procesu a mnoho a mnoho dal╣φho. NahlΘdnou do readproc.h nebo alespo≥ do manußlu k /proc doporuΦuji i (ne)programßtor∙m, budete p°φjemn∞ p°ekvapeni kolik mnoho se lze o procesech dozv∞d∞t (zde mß jist² komerΦn∞ "·sp∞╣n²" operaΦnφ systΘm je╣t∞ co dohßn∞t). Pokud zφskßvßme data pomocφ readproctab() je nßm vrßceno pole pointer∙ na struktury, kde jedna °ßdka pole je jedna struktura typu proc_t a reprezentuje prßv∞ jeden proces - poslednφ °ßdka pole je pointer NULL. Funkce readproc() a readproctab() umo╛≥ujφ velice efektivn∞ definovat co o procesu chceme naΦφst a je mo╛nΘ i definovat filtr, podle kterΘho jsou vybrßny jen na╣φ podmφnce vyhovujφcφ procesy. (Toto je hlavn∞ u verze 1.12.2.) Vφce u╛ makra PROC_N╠CO v souboru readproc.h. Funkce readproctab() je vlastn∞ celΘ kouzlo na╣eho programu a asi i nejd∙le╛it∞j╣φ Φßstφ libproc. Nynφ u╛ je jen na╣φ starostφ co s daty, kterΘ nßm tato funkce vrßtila. (A v╣e se v podstat∞ odbylo na jednom °ßdku - a pak ╛e je cΘΦko slo╛itΘ).

/* ----- p°φklad 2. ----- */
#include <stdio.h>
#include <string.h>		/* strcmp()            */
#include <proc/readproc.h>	/* readproctab         */
#include <proc/version.h>	/* set_linux_version() */
#include <proc/sysinfo.h>	/* uptime()            */
#include <sys/param.h>	/* HZ                  */
#include <time.h>		/* time()              */

#define RE_ERROR	1
#define RE_OK		0

   int	GL_current_time, CL_Sum;
   long	GL_time_now;

/* This function is from ps(1) - ps.c 
 * Copyright (c) 1992 Branko Lankester
 * Changes Copyright (C) 1993, 1994 Michael K. Johnson,
 *   and   Copyright (C) 1995, 1996 Charles Blake,
 *   and   Copyright (C) 1996       Helmut Geyer 
 */
   void get_pcpu(proc_t *p) {
      int total_time, seconds;
      time_t start;
      unsigned int pcpu=0;
   
      seconds = (((GL_current_time * HZ) - p->start_time) / HZ);
      start = GL_time_now - seconds;
      total_time = (p->utime + p->stime  + 
                   (CL_Sum ?   p->cutime + p->cstime : 0));
      pcpu = seconds ? (total_time * 10 * 100/HZ) / seconds : 0;
      if (pcpu > 999) pcpu = 999;
      p->pcpu = pcpu;
   }

   int main(int argc, char **argv)
   {
      proc_t	**pt;	
      int	proc_num=0, mem_max=0, cpu_max=0;
   
      if (argc > 1)
         if (!(strcmp(argv[1], "-c")))
            CL_Sum = 1;
   
      set_linux_version();
      if (!(GL_current_time = uptime(0,0))) 
         exit(RE_ERROR);
      GL_time_now = time(0L);
      pt = readproctab(PROC_FILLTTY | PROC_FILLUSR | PROC_FILLMEM);
   
      while(pt[proc_num] != NULL) {
      /* --- mem --- */
         if (pt[proc_num]->resident > pt[mem_max]->resident)
            mem_max = proc_num;
      /* --- cpu --- */ 
         get_pcpu(pt[proc_num]);    	 	
         if (pt[proc_num]->pcpu > pt[cpu_max]->pcpu)
            cpu_max = proc_num; 	
         ++proc_num;
      }      
      printf("\nPROCESS  : %d\n", proc_num);
      puts("PROCES WITH MAX. RESIDENT MEMORY:");
      printf("\tName     : %s\n",         pt[mem_max]->cmd);
      printf("\tPID      : %d\n",         pt[mem_max]->pid);
      printf("\tSTATE    : %c\n",         pt[mem_max]->state);
      printf("\tUSER     : %s\n",         pt[mem_max]->user); 
      printf("\tTTY      : %s\n",         pt[mem_max]->ttyc);
      printf("\tRESIDENT : %ld (pages)\n", pt[mem_max]->resident);  
      puts("PROCES WITH MAX. %CPU:");
      printf("\tName     : %s\n",         pt[cpu_max]->cmd);
      printf("\tPID      : %d\n",         pt[cpu_max]->pid);
      printf("\tSTATE    : %c\n",         pt[cpu_max]->state); 
      printf("\tUSER     : %s\n",         pt[cpu_max]->user); 
      printf("\tTTY      : %s\n",         pt[cpu_max]->ttyc);
      printf("\t%%CPU     : %.1f", (float) pt[cpu_max]->pcpu/10);    
      if (CL_Sum) 
         puts(" (cumulative)\n");
      else
         puts("\n");
   
      exit(RE_OK);
   }

V²pis Φ. 3: Pou╛itφ knihovny libproc podruhΘ

Je╣t∞ malΘ upozorn∞nφ pro ty co by rßdi n∞co podnikali s libproc. U polφ jako nap°. **cmdline je dobrΘ p°ed zapoΦetφm prßce testovat jestli v∙bec n∞co obsahuje. A testovat takΘ dΘlku polo╛ek v n∞m ulo╛en²ch. Ona p°φkazovß °ßdka n∞kter²ch proces∙ by mohla vß╣ program odeslat kamsi do core...

Pochopiteln∞ v²╣e uvedenΘ je jen Φßstφ toho co dovede libproc. DostupnΘ jsou i funkce p°evßd∞jφcφ Φφsla za°φzenφ do string∙ (devname.h), specißlnφ outputy (output.h), zachßzenφ s "psdatabßzφ" (psdata.h), jmΘna signßl∙ (signames.h), vytvß°enφ stromu proces∙ dle vztahu rodiΦ - dφt∞ (tree.h) atd.

Aby nikdo nemusel opisovat nebo jinak slo╛it∞ zφskßvat uvedenΘ p°φklady (pokud by si je cht∞l vyzkou╣et), m∙╛e je nalΘzt na FTP serverech Linuxov²ch novin nebo na adrese http://home.zf.jcu.cz/~zakkr/LN/.

V╣em zßjemc∙m lze jen doporuΦit nainstalovat balφk libproc-dev a zaΦφt samostudium s knihovnou, kterß dokß╛e usnadnit programßtorsk² ╛ivot v╣em, kte°φ ji dokß╛φ efektivn∞ vyu╛φt. *


- p°edchozφ Φlßnek - nßsledujφcφ Φlßnek - obsah - ·vodnφ strßnka -