4.5. Proces-kontrol

Når du starter et program op, bliver det til et kørende program. Et kørende program kaldes i Unix-sammenhænge for en proces (eng. "process"). Hver proces har en unik indikator, som er et heltal. Vi kalder denne indikator for PID, hvilket kommer af det engelske "Process IDentifier".

4.5.1. ps viser processer

Hvis du vil se hvilke programmer du har kørende, kan du bruge kommandoen ps. ps er en forkortelse for "process".

[tyge@hven MitKatalog]$  ps
  PID TTY STAT TIME COMMAND
  435  2   S   0:00 /bin/login -- tyge
  436  2   S   0:00 -bash
  447  2   R   0:00 ps
[tyge@hven MitKatalog]$ 

Ovenstående dialog viser, at der er et kørende program (ps) og to sovende (/bin/login og bash). At et program er kørende, ser du ved, at der under STAT står et "R" (for "running"), mens et sovende program i status-feltet har et "S" (for "sleeping"). En sovende proces er en proces, som er blevet startet, men ikke er aktiv, og nu står og venter på at blive aktiveret. Feltet, hvor der står TTY, viser, fra hvilken terminal programmet blev startet. I Linux kan du skifte mellem flere virtuelle konsoller, og hver virtuel konsol opfattes som en terminal.

Det skal nævnes at ps kun viser processer med samme tty (samme terminal) som ps, og du kan anvende ps aux til at se alle processer.

Vil du se alt hvad der kører på maskinen og se hvilke programmer, der kalder hinanden, da kan

[tyge@hven MitKatalog]$ ps auxfww
USER PID %CPU %MEM VSZ  RSS    TTY  STAT START TIME COMMAND
.... forkortet
tyge 2930 0.0  3.4 19544 6548  ?    S    21:05  0:00 kdeinit: Running...
tyge 2960 0.1  6.0 22760 11484 ?    S    21:05  0:15  \_ kdeinit: kwin
tyge 3086 0.1  6.9 24940 13192 ?    S    21:07  0:14  \_ kdeinit: konsole -icon konsole -miniicon konsole
tyge 3088 0.0  0.8  2868 1628 pts/1 S    21:07  0:01  |   \_ -bin/tcsh
tyge 3116 1.2  4.6 11660 8972 pts/1 S    21:07  1:59  |   |   \_ emacs mandrake.sgml
tyge 4131 0.0  0.9  3224 1820 pts/1 S    23:33  0:00  |   |   \_ zsh
tyge 4142 0.0  0.7  2732 1516 pts/1 S    23:33  0:00  |   |       \_ -csh
tyge 4167 0.0  0.8  2848 1604 pts/1 S    23:33  0:00  |   |           \_ bash
tyge 4172 0.0  0.7  2728 1512 pts/1 S    23:33  0:00  |   |               \_ -sh
tyge 4197 0.0  0.7  2672 1408 pts/1 S    23:33  0:00  |   |                   \_ -csh

I eksemplet er vist en "syg" konstruktion, hvor sidste linje viser at man har startet csh op i sh, som igen er startet op i bash, som igen... Vil man vide hvilke af processerne der indeholder et søgeord – f.eks. ens eget login-navn, da kan man filtere med ps auxfww | grep SØGEORD.

4.5.2. Få et bedre overblik ved at bruge top

Som du så i foregående afsnit, kan ps bruges til at få et overblik over, hvilke processer du har kørende. Problemet er, at du kun får et statisk billede. Hvis du er interesseret i et mere dynamisk billede af din computers processer, kan du bruge programmet top. top opdaterer skærmen hvert femte sekund. Ved at trykke på "i" skifter du mellem "non-idle"-modus og almindelig modus. I "non-idle"-modus ser du kun de processer, som er aktive, mens du i almindelig modus ser alle. Du afslutter top ved at trykke på "q".

top leverer mange oplysninger, og derfor er det spændende at bruge programmet. Endvidere er det værd at læse programmets man-page.

4.5.3. At køre programmer i baggrunden

Alle de kommandoer, vi har præsenteret dig for i dette kapitel, har taget meget kort tid at udføre. Antag, at du ønsker at køre et program eller kommando, som tager en time at udføre. Hvis du nu startede programmet op på kommandolinjen, kunne du ikke udføre andre kommandoer i en time, da du ikke kunne komme til at taste nye kommandoer ind. Det er naturligvis ikke så smart, især ikke da Linux er et ægte multitasking styresystem!

Du kan løse dit ventetidsproblem ved at udføre programmet eller kommandoen i baggrunden. For at udføre et program i baggrunden sætter vi et & efter kommandoen. Et program der tager noget tid at køre, hvis du har mange filer, er updatedb. updatedb opretter den database der bruges af locate, og det tager typisk lang tid at køre denne kommando.

[root@hven root]$ updatedb &
[1] 7446
[root@hven root]$ ps
  PID TTY          TIME CMD
 7413 pts/2    00:00:00 su
 7416 pts/2    00:00:00 bash
 7446 pts/2    00:00:00 updatedb
 7447 pts/2    00:00:00 slocate
 7448 pts/2    00:00:00 ps
[root@hven root]$ cat /proc/loadavg
1.14 1.02 0.50 1/113 7479
[1]+  Done                    updatedb
[root@hven root]$ 

Programmet eller kommandoen kører nu samtidig med at du kan indtaste og udføre nye kommandoer. Grunden til vi siger, at programmet kører i baggrunden er, at du ikke sidder med det ved din konsol (som vi så kalder for forgrunden). Så mens updatedb kører i baggrunden, kan vi så kører kommandoen ps og se processen. Efter at have tastet en del andre kommandoer, her illustreret ved cat /proc/loadavg, er updatedb færdig med at køre, og kommer ud og skriver "Done". Her er det så baggrundsprocess nummer [1] der er færdig med at køre. &-kommandoen bruges på direkte fra kommandolinjen og inde i shell-scripts.

Glemte du & sidst på kommandolinjen, så kan du trykke Ctrl-z og så har du suspenderet jobbet (eng. "suspended"). Med fg (eng. "foreground") og bg (eng. "background") kan du styre hvad der er aktivt.

[root@hven root]$ updatedb
(tast Ctrl-z)
[1]+  Stopped                 updatedb
[root@hven root]$ bg
[1]+ updatedb &
[root@hven root]$ 

Har du fået sat et job igang i baggrunden, som du gerne vil stoppe, skal du først tilbage til programmet med fg. Tast dernæst Ctrl-c for at stoppe programmet.

[root@hven root]$ updatedb &
[1] 7446
[root@hven root]$ fg
updatedb
(tast Ctrl-c)
[root@hven root]$ 

Ctrl-z kan med fordel bruges mange steder. Det kunne være indefra dit post-program eller din tekst-editor. Situationen er at du sidder og er igang med at skrive et script, og så vil du lige prøve det. Efter afprøvning vil du tilbage samme sted i editoren, og stå det samme sted med markøren. Herunder er vist at editoren startes. Så tastes Ctrl-z og scriptet afprøves, hvilket giver en fejl. Til slut tilbage til editoren med kommandoen fg.

[tyge@hven ~]$  vi entest
(inde i editoren tastes Ctrl-z)
[tyge@hven ~]$  ./entest
entest: line 4: syntax error: unexpected end of file
[tyge@hven ~]$  fg

Det er muligt at have flere programmer kørende i baggrunden samtidigt. Det kan hurtigt blive lidt uoverskueligt at gøre det, men det vil i nogle tilfælde være en fordel. For overskueligehedens skyld anvendes i det følgende blot kommandoen sleep, der holder en pause på nogle sekunder for så at returnerer til prompten. Ved at starte sleep 100 og sleep 200 skulle det være lidt nemmere at følge med i hvilket program man vender tilbage til. Herunder starter vi så de to kommandoer, og vender så tilbage til

[tyge@hven ~]$ sleep 100 &
[1] 7533
[tyge@hven ~]$ sleep 200 &
[2] 7534
[tyge@hven ~]$ fg
sleep 200

Ovenstående er helt som ønsket, hvis det altså er process 2 man vil tilbage til. Hvis man i stedet vil tilbage til process 1, skal kommandoen %1 bruges, hvilket angiver baggrundsprocess nummer 1.

[tyge@hven ~]$  sleep 100 &
[1] 7533
[tyge@hven ~]$  sleep 200 &
[2] 7534
[tyge@hven ~]$  %1
sleep 100

Og selvfølgelig kan man starte flere programmer samtidigt på kommandolinjen og lægge dem alle i baggrunden, så de stadig kører samtidigt.

[tyge@hven ~]$  sleep 100 & sleep 200 &
[1] 7550
[2] 7551
[tyge@hven ~]$ 

Er du startet op i grafisk tilstand og gerne vil starte et grafisk program, men ikke lige vil flytte hånden over til musen og bruge menuerne, så er det:

[tyge@hven ~]$  gimp &

4.5.4. Kør flere programmer efter hinanden

Ofte har man brug for at køre flere programmer efter hinanden flere gange på kommandolinjen. Nu kan man jo blot taste pil op et par gange, og køre kommandoen igen. Som eksempel er det følgende to kommandoer vi vil kører gentagende gange.

[tyge@hven ~]$  date>foo
[tyge@hven ~]$  md5sum foo
7643ce159d2b9269e21cdd1fb88f79ba  foo
[tyge@hven ~]$ 

Ovenstående kunne løses ved at lave et script med disse to linjer, men det er lidt meget at gøre ud af det, når scriptet ikke senere skal bruges til noget. I stedet kan man sætte et ; (semikolon) imellem, og derved først køre det ene program, efterfulgt af det andet.

[tyge@hven ~]$  date>foo ; md5sum foo
6d1a33063a8eba547c278a9776b7b59d  foo
[tyge@hven ~]$ 

I ovenstående eksempel sker der nok ikke nogen fejl – tror vi. Nu kunne man være så uheldig at filen foo var skrivebeskytte, hvilket gør at der ikke bliver skrevet nye data til filen. Dette beyder så at det ikke giver mening efterfølgende at køre md5sum. Først et eksempel på hvor galt det kan gå.

[tyge@hven ~]$  date>foo ; md5sum foo
bash: foo: Permission denied
6d1a33063a8eba547c278a9776b7b59d  foo
[tyge@hven ~]$  date>foo ; md5sum foo
bash: foo: Permission denied
6d1a33063a8eba547c278a9776b7b59d  foo
[tyge@hven ~]$ 

Problemet er at kommandoen md5sum bliver kørt, til trods for at det gik galt med den forgående kommando. Som vist bliver der hele tiden udregnet den samme md5-sum, hvilket ikke var meningen. Dette kan løses ved i stedet at bruge kommandoen &&. Derved bliver den næste kommando kun kørt, hvis den første gik godt.

[tyge@hven ~]$  date>foo && md5sum foo
bash: foo: Permission denied
[tyge@hven ~]$ 

Et mere praktisk eksempel på ovenstående vil være at kompilere en Linux-kerne. Først make dep, så make, så osv. osv. Men de efterfølgende kommandoer må kun køres hvis den forgående gik godt.

[tyge@hven ~]$  cd /usr/src/linux
[tyge@hven linux]$  make dep && make && make install

Eksemplet med make vil man både bruge på kommandolinjen og i shell-scripts. Et andet eksempel som typisk kun ville blive brugt i shell-scripts, kunne være først at teste om en fil eksiterer, og i herefter gøre noget med den.

#! /bin/sh
[ -e foo ] && md5sum foo

Mange syntes ikke om den måde at skrive det på, og det er nok også nemmere at læse det, hvis der i stedet står:

#! /bin/sh
if [ -e foo ]; then
        md5sum foo
fi

Kommandoen [ er blot et symlink til test. Læs om den under man test.

4.5.5. Start en sub-shell på kommandolinjen

Når man starter en ny kommandofortolker, får man nye omgivelser (eng. environment) som programmet køres i. Dette ses når man har et kommandofortolkerprogram hvor systemvariable ændres eller tilføjes, eller der skiftes til et andet katalog. Når programmet er færdigt med at køre, er alt tilbage ved det gamle. Til tider kan det være nemmere ikke at skulle oprette et program først, men blot skrive det hele direkte på kommandolinjen. Først et lille simpelt eksempel som et program, der først skifter til kataloget /tmp og dernæst lister filerne. Tricket er, at når programmet er færdigt med at køre, står vi igen i samme katalog som vi kaldte programmet fra.

#! /bin/sh
cd /tmp ; ls

For at gøre det samme på kommandolinjen skal der blot paranteser omkring:

[tyge@hven ~]$ (cd /tmp ; ls)

Det eneste "ødelæggende" der skete ved ovenstående, var at der blev skiftet til et andet katalog, men det kunne også være en variabel der blev ændret:

[tyge@hven ~]$ (cd /tmp ; PATH=. ; min_test)

Et mere praktisk eksempel der er besværligt at løse uden brug af "()", kan illustreres ved noget hvor der skal holdes en pause. Som vist før kan man blot udskifte det som står imellem paranteserne med et script, hvilket i det følgende ikke er praktisk.

Vil man gerne vide lidt om hvordan en web-server er sat op, kan dette gøres med telnet og brug af HTTP-protokollen. Først åbnes en forbindelse til port 80. Når man får en forbindelse, indtastes HEAD-kommandoen og så venter man indtil der kommer et svar. Manuelt gøres det således:

[tyge@hven ~]$ telnet www.sslug.dk 80
Trying 130.228.2.150...
Connected to ns.sslug.dk (130.228.2.150).
Escape character is '^]'.
HEAD / HTTP/1.0

HTTP/1.1 200 OK
Date: Wed, 24 Jul 2002 07:21:16 GMT
Server: Apache/1.3.26 (Unix) (Red-Hat/Linux) OpenSSL/0.9.5a PHP/3.0.18
Last-Modified: Wed, 24 Jul 2002 04:45:00 GMT
Connection: close
Content-Type: text/html; charset=iso-8859-1

Connection closed by foreign host.

Bemærk at efter HEAD skal der tastes ENTER to gange. Her kunne man være fristet til at kanalisere HEAD-kommandoen direkte til telnet, men det går ikke da forbindelsen bliver lukket inden der kommer ouput retur fra web-serveren. En lille pause på et par sekunder med sleep løser problemet, men så skal kommandoen indkapsles med (). Det færdige eksempel er så:

[tyge@hven ~]$ (echo -e "HEAD / HTTP/1.0\n"; sleep 2) | telnet www.sslug.dk 80

Som før nævnt, kan det der står i parantes skrives i et script:

#! /bin/sh
echo -e "HEAD / HTTP/1.0\n"
sleep 2

4.5.6. Proces substituering

Man kommer til tider ud for at man bliver nødt til at oprette midlertidige filer, der senere skal bruges af et andet program. Nogle programmer kan kun tage filer som input, og hvis output kommer fra en proces, skal der gøres noget andet. Disse midlertidige filer skal til slut slettes, hvilket ofte ikke er noget problem. Med kommandoen <() kan man undgå dette midlertidige trin.

Programmer såsom tar, diff og comm skal bruge filer som input på kommandolinjen. Er input til disse programmer så noget der kommer fra en proces, vil man typisk gemme dette i en midlertidig fil. Her er et eksempel på hvordan det kunne se ud med et shell-script:

#! /bin/sh
# Find filer der skal tages backup af:
find | egrep "\.c$" > temp
# 'tar' filerne nævnt i 'temp'
tar cvf backup.tar -T temp
# Ryd op
rm -f temp

Ovenstående program er måske overskueligt, men man skal stadig huske at fjerne midlertidige filer, og man skal finde på et godt midlertidigt navn til sin temp-fil. I stedet for at oprette en midlertidig fil, kan man tage output fra find og lave dette om til noget der for tar ligner en fil. Med <() (processubstituering) bliver det så:

[tyge@hven ~]$ tar cvf backup.tar -T <(find | egrep "\.c$")

tar opfatter <() som en fil, og behandler den på denne måde. Lad os lige prøve at se om ikke systemet selv kan forklare hvad en <() er. Hvis nu <() er en fil, så ville man kunne skrive filnavnet ud på skærmen.

[tyge@hven ~]$ echo <(true)
/dev/fd/63

Her ses at der bare oprettes noget der ligner en midlertidig fil et eller andet sted, som bash selv holder styr på. Hvis man så har to gange <() på kommandolinjen, kan bash stadig håndtere det:

[tyge@hven ~]$ echo <(true) <(true)
/dev/fd/63 /dev/fd/62

<() ser ud som en fil, men er rent faktisk en kanal, hvilket er mere effektivt end at bruge en fil. Følgende kommandoer giver lidt yderligere information:

[tyge@hven ~]$ ls -l <(true)
lr-x------    1 tyge  tyge      64 Jul 25 20:15 /dev/fd/63 -> pipe:[100342]
[tyge@hven ~]$ stat <(true)
  File: "/dev/fd/63" -> "pipe:[100408]"
  Size: 64              Blocks: 0          IO Block: 0      Symbolic Link
Device: 3h/3d   Inode: 419070015   Links: 1
Access: (0500/lr-x------)  Uid: (  506/    tyge)   Gid: (  512/    tyge)
Access: Fri Jul 25 20:16:21 2002
Modify: Fri Jul 25 20:16:21 2002
Change: Fri Jul 25 20:16:21 2002

Ovenstående eksempel med tar kan selvfølgelig løses på mange andre måder, hvor <() ikke er nødvendigt. Til gengæld skulle det lille eksempel være nemmere at forstå. Et mere drilsk eksempel kan laves med comm, der er et program til at sammenligne filer. Ved almindelig brug anvendes det således:

[tyge@hven ~]$ comm fil-1 fil-2

Nu kan man være så uheldig at de filer der skal undersøges er uddata fra for eksempel grep. Uden brug af <() kunne et shell-script se således ud:

#! /bin/sh
grep foo test1 > fil-1
grep foo test2 > fil-2
comm fil-1 fil-2
rm -f fil-1 fil-2

Omskrevet med <() bliver det blot en enkelt linje:

[tyge@hven ~]$ comm <(grep foo test1) <(grep foo test2)

For mere information om processubstituering (eng. "process substitution") se http://www.tldp.org/LDP/abs/html/process-sub.html

4.5.7. Dræb en proces

Nu kan det ske, at du har fået startet et program, som du bliver træt af. Du vil altså gerne afbryde det, inden det er kørt færdigt. Unix-verdenen er barsk, for man taler ikke om at afbryde en proces, men om at slå den ihjel (eng. "kill"). Når du vil slå en proces ihjel, kan du bruge kommandoen kill. Som argument til kill giver du PID. Nedenfor er vist et eksempel.

[tyge@hven MitKatalog]$ ps
  PID TTY STAT TIME COMMAND
  435  2   S   0:00 /bin/login -- tyge
  436  2   S   0:00 -bash
  447  2   R   0:00 ps
  585  2   R   2:34 ls
[tyge@hven MitKatalog]$ kill 585
[tyge@hven MitKatalog]$ ps
  PID TTY STAT TIME COMMAND
  435  2   S   0:00 /bin/login -- tyge
  436  2   S   0:00 -bash
  763  2   R   0:00 ps

Det skal også nævnes, at enkelte gange kan en proces være kørt helt i skoven, og så må du tage kraftigere skyts i brug. I stedet for kill PROCESNUMMER kan du bruge kill -9 PROCESNUMMER. Forskellen er at kill som standard sender en besked til programmet om at det skal stoppe, mens man med kill -9 beder linuxkernen om at tage sig af at stoppe programmet.