Dagens program Operativsystemer Prosesser og systemkall i UNIX Hente prosessens nummer Starte prosesser Vente på prosesser Utføre programmer Nyttige forklaringer Sikker opprettelse av nye prosesser Hva er en kommandotolker? Ark 1 av 18 Forelesning 28.3.2001
Hva er en prosess i UNIX? En prosess er et program under utførelse. Prosessen består av Hurtiglager for kode, data og stakk: Stakk Tomt Data Kode Registerinnhold. Systeminformasjon, blant annet et prosessnummer (en såkalt PID). Forelesning 28.3.2001 Ark 2 av 18
Hvordan kommunisere med operativsystemet? Brukerprogrammer har behov for å kommunisere med operativsystemet, blant annet for å få utført I/U. Forskjellige operativsystemer benytter ulike teknikker til dette: Hopp til spesielle adresser (CP/M). Spesielle instruksjoner (som SysCall). Ulovlige instruksjoner. Systemkall (UNIX). Et systemkall er et kall på en C-rutine som ligger i operativsystemet. Forelesning 28.3.2001 Ark 3 av 18
Et enkelt systemkall Filen pid.c #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(void) { printf("prosessen har PID=%d\n", (int)getpid()); return 0; maskin navn> pid Prosessen har PID=20796 maskin navn> pid Prosessen har PID=20797 getpid gir oss altså prosess-id (PID) til den kallende prosessen. Forelesning 28.3.2001 Ark 4 av 18
Hvordan opprette en ny prosess? Den absolutt eneste måten å opprette nye prosesser på i UNIX er å benytte systemkallet fork. Fork gjør følgende: 1. En ny prosess opprettes. 2. Den nye prosessen er en nøyaktig og fullstendig kopi av foreldre-prosessen. Det eneste som er forskjellig, er at de har forskjellig PID. 3. Begge prosessene fortsetter eksekveringen etter fork-kallet. Forelesning 28.3.2001 Ark 5 av 18
Filen fork1.c #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(void) { printf("prosessen har PID=%d\n", (int)getpid()); fork(); printf("nå er PID=%d\n", (int)getpid()); return 0; maskin navn> fork1 Prosessen har PID=20803 Nå er PID=20804 Nå er PID=20803 Forelesning 28.3.2001 Ark 6 av 18
Foreldre og barn Den prosessen som opprinnelig utførte fork-kallet, kalles foreldreprosessen, ogde som opprettes, kalles barneprosesser. For å kunne skille de to prosessene, benyttes følgende løsning for returverdien fra fork: Barneprosessen får 0. Foreldreprosessen får barnets PID. Filen fork2.c #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(void) { pid_t pid; pid = fork(); if (pid == 0) { printf("dette er barnet\n"); else{ printf("barnet har PID=%d\n", (int)pid); return 0; maskin navn> fork2 Dette er barnet Barnet har PID=20811 Forelesning 28.3.2001 Ark 7 av 18
Hvordan avslutte prosesser? En prosess kan avslutte normalt på tre forskjellige måter: Prosessen terminerer (med ukjent status) når programmet avsluttes. Hvis hovedprogrammet (main) avsluttes med en return-setning, vil dette være prosessens statusverdi. Systemkallet exit vil terminere prosessen, samt angi statusverdi. Statusverdi 0 angir «status OK»; andre verdier angir en feilsituasjon. Forelesning 28.3.2001 Ark 8 av 18
Å vente på andre prosesser Systemkallet wait benyttes til å vente på at prosessens barn skal bli ferdige. Kallet #include <sys/types.h> #include <sys/wait.h> pid_t n; int status;. n = wait(&status); vil gjøre følgende: Hvis det ikke finnes flere barne-prosesser, returnerer wait øyeblikkelig med n = 1. Når det finnes barn som er ferdige, returnerer wait med følgende parametre: n er PID til barnet. status (nest nederste byte) inneholder barnets statusverdi. status (nederste byte) inneholder data om hvorledes barnet ble avsluttet; 0 angir nomal stopp. Hvis det bare finnes levende barn, blir prosessen som kaller wait blokkert til ett av barna er ferdig. Forelesning 28.3.2001 Ark 9 av 18
Filen fork3.c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> int main(void) { pid_t pid, n; int status; pid = fork(); if (pid) { /* Samme som (pid!= 0) */ printf("opphavet har PID=%d, ", (int)getpid()); printf("barnet har PID=%d\n", (int)pid); printf("nå venter opphavet...\n"); n = wait(&status); printf("n=%d, status=0x%x\n", (int)n, status); return 0; else{ printf("barnet har PID=%d\n", (int)getpid()); printf("barnet får status=99\n"); exit(99); maskin navn> fork3 Barnet har PID=20818 Barnet får status=99 Opphavet har PID=20817, barnet har PID=20818 Nå venter opphavet... n=20818, status=0x6300 Forelesning 28.3.2001 Ark 10 av 18
Hvordan utføre programmer? Systemkallet execve benyttes til å utføre programmer. Kallet execve("/usr/bin/pwd", param, env); vil gjøre følgende: Alt innholdet av prosessen som utfører kallet, vil bli erstattet av innholdet av filen /usr/bin/pwd. Parametre og omgivelsen overføres. Den nye prosessen kjøres. Det er altså ingen retur fra en execve med mindre noe går galt! (Det finnes mange varianter av execve; ofte benyttes navnet exec om alle sammen. Forskjellen mellom de forskjellige dreier seg for det meste om hvorledes man kan sløyfe parametre eller gi disse på en alternativ måte. I dette kurset vil vi kun benytte execve.) Forelesning 28.3.2001 Ark 11 av 18
Filen pwdx.c #include <stdio.h> #include <sys/types.h> #include <unistd.h> char *empty[1] = {NULL; int main(void) { execve("/usr/bin/pwd", empty, empty); perror("pwdx"); exit(1); maskin navn> pwdx /ifi/ganglot/a09/kritisk/in147/v01/forelesninger/kode Forelesning 28.3.2001 Ark 12 av 18
Parametre til execve Andre og tredje parameter til execve angir parametre til kommandoen og omgivelsen kommandoen skal utføres i. Begge parametre er adresser til vektorer av pekere til tekster, og begge vektorene må avsluttes med en NULL-peker. Filen lsx.c #include <stdio.h> #include <sys/types.h> #include <unistd.h> char *par[] = { "ls", "-acf", NULL ; char *env[] = { NULL ; int main(void) { execve("/bin/ls", par, env); perror("lsx"); exit(1); maskin navn> lsx./../ lsx* lsx.c test-a@ Omgivelsen er de variable som overføres mellom forskjellige utgaver av kommandotolkeren. I (ba)sh angis disse variablene som export, i (t)csh med direktivet setenv. Forelesning 28.3.2001 Ark 13 av 18
Program-parametre Parametrene kan komme som parametre til hovedprogrammet (main) og omgivelsesvariablene som den globale pekeren environ evt. som en tredje parameter til main slik vi har brukt det. Filen pars.c #include <stdio.h> extern char **environ; int main(int argc, char *argv[]) { char **ep; int i; for (i = 0; i < argc ; i++) printf("argv[%d]= %s \n", i, argv[i]); ep = environ; while (*ep) { printf("%s\n", *ep); ++ep; return 0; Forelesning 28.3.2001 Ark 14 av 18
maskin navn> pars -la 122 /bin/cp argv[0]= pars argv[1]= -la argv[2]= 122 argv[3]= /bin/cp PWD=/ifi/ganglot/a09/kritisk/in147/V01/Forelesninger/Kode JDBC_HOME=/hom/kritisk/jConnect/JConnect4.0 TZ=MET WINDOWID=71303182 PAGER=less. Antallet parametre er gitt i argc, mens listen av omgivelsesvariable avsluttes med en NULL-peker. Forelesning 28.3.2001 Ark 15 av 18
Hvordan kvele et UNIX-system? Selv om UNIX-implementasjoner er godt sikret, finnes det kode som kveler de aller fleste: while(1) {... fork();... hvor det ikke er tilstrekkelig mange kall på wait. Som en løsning er det blitt skrevet en egen fork: safefork, som kun tillater seks barneprosesser opprettet pr. prosess; forøvrig oppfører den seg akkurat som fork. Koden ligger på ~in147/kode/safefork.c. Alle IN147-studenter anmodes intenst om å bruke safefork i stedet for fork. Kopiér filen til området der programmet ditt ligger. Forelesning 28.3.2001 Ark 16 av 18
Kommandotolkeren En kommandotolker (ofte kalt «shell» på engelsk) er et program som leser brukerens kommandolinjer og kaller de riktige programmene. De vanligste kommandotolkerne heter sh (Bourne shell), csh (C shell) og bash («Bourne-again shell»). Snart kommer ifish (Ifi shell). En kommandotolker (som (ba)sh) gjør (sterkt forenklet) følgende når den skal utføre vanlige kommandoer: while (1) { skriv klarsignal ( prompt ) les brukerens kommandolinje splitt linjen i kommando og parametre if ( tolkerkommando som exit eller cd ) { utfør kommandoen else{ if (safefork() == 0) { finn programfilen til kommandoen execve( programfil, parametre, omgivelse ); else{ vent til barnet er ferdig wait(&status); Forelesning 28.3.2001 Ark 17 av 18
Hvordan finne programmene? Når brukeren gir en kommando som tail, hvilken programfil menes da? Det finnes (foreløbig) 24 åpne filer som heter tail på systemet. Omgivelsesvariabelen PATH forteller hvilke filområder som kommandotolkeren skal lete i: maskin navn> printenv PATH /ifi/mjollnir/objectstore/ostore/bin:/ifi/mjollnir/objectstore/osji/bin:/local/vlsi/bin/sol: /ifi/ganglot/a09/kritisk/bin:/local/x11r5/bin:/local/x11r5/bin/pbm:/local/java/jdk1.2/bin: /local/bin/texmf:/local/bin:/local/ssh/bin:/local/bin/msdos:/opt/sunwspro/bin:/usr/openwin/bin: /usr/dt/bin:/usr/ccs/bin:/usr/ucb:/usr/bin:/local/sybase11/sol/bin:/local/gnu/bin:/local/qt/bin:.:/local/etc:/etc:/local/drift/bin:/local/hacks/bin/sol:/local/hacks/bin:/local/kt-lab/bin: /ifi/levding/mysql/mysql/bin:/local/vbroker/bin:/local/oracle/8.0.5/bin De aktuelle områdene er angitt med kolon som skilletegn. Hvis man lurer på hvilken programfil som velges, kan man skrive maskin navn> which tail /usr/ucb/tail Forelesning 28.3.2001 Ark 18 av 18