h2. Prosessinhallinta
Prosessinhallinnan systeemikutsut:
* uuden prosessin luonti fork()
* prosessin koodinvaihto exec()
* prosessin lopettaminen exit()
* prosessin päättymisen odotus wait(), waitpid()
----
h3. Prosession luonti
Prosessi luodaan funktiolla *pid_t \*fork(void);*, joka palauttaa kutsuvalle prosessille lapsiprosessin numeron ja lapsiprosessille 0. fork()-kutsun tuloksena syntyy uusi prosessi, joka on 'klooni' äitiprosessista.
* yhteinen koodi
* samat muuttujien arvot
* samat prosessinkuvaajan perustiedot
* yhteiset avoimet tiedostot
Kummallakin on kuitenkin:
* oma data-alue
* oma prosessinkuvaaja
fork()-kutsun jälkeen ei voi olla varma siitä kumpi prosessi (äiti vai lapsi) jatkaa aiemmin suoritustaan. Jos järjestys tärkeää, on ohjelmoitava itse synkronointi ( [Esim 1|http://www.cs.helsinki.fi/u/jplindst/c/prosess1.c].#include < sys/types.h >
#include < unistd.h >
int glob = 6; /* external variable in initialized data */
char buf[] = "a write to stdout\n";
int
main(void)
{
int var; /* automatic variable on the stack */
pid_t pid;
var = 88;
if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
perror("write error");
printf("before fork\n"); /* we don't flush stdout */
if ( (pid = fork()) < 0)
perror("fork error");
else if (pid == 0) { /* child */
glob++; /* modify variables */
var++;
} else
sleep(2); /* parent */
printf("pid=%d,glob=%d,var=%d\n",getpid(), glob, var);
exit(0);
}
Lapsiprosessi luodaan, kun:
* halutaan suorittaa äiti\- ja lapsiprosessissa erillinen osa samassa tiedostossa olevasta koodista. Esim. verkkosovelluksissa on tavallista, että palvelija luo lapsiprosessin antamaan palvelua ja jää itse odottamaan uusia palvelupyyntöjä.
* halutaan suorittaa kokonaan toinen ohjelma. Tällöin fork()-kutsun jälkeen on lapsiprosessissa myös exec()-kutsu, eli se vaihtaa suoritettavaa koodia. Esim. komentotulkit käyttävät tätä menetelmää.
Lapsi perii äidiltä kaikki avoimet tiedostokuvaajat. Sekä äiti että lapsi käyttävät yhteistä avoimet tiedostot taulun alkiota niillä on yhteinen luku / kirjoituspositio.
----
h3. 4.2 Prosession päättyminen
Prosessin suoritus päättyy normaalisti, kun suoritetaan:
* funktiossa main() funktio return(status)
* funktio exit(status) tai
* funktio \_exit(status)
exit() kutsuu funktiolla atexit() rekisteröityjä funktioita ja purkaa stdio:n puskurit (vrt. funktio flush()). \_exit() hoitelee Unix-spesifiset lopputoimet Prosessin suoritus voi päättyä myös 'epänormaalisti', kun kutsutaan funktiota abort() tai prosessi saa signaalin, johon se ei varautunut tai ei voi varautua. Epänormaaleissa päättymisissä generoi ydin paluuarvon.
Unix-spesifisiin lopputoimiin kuuluu tiedostojen sulkeminen, muistitilan vapauttaminen sekä äitiprosessin signalointi, mutta prosessinkuvaaja jää vielä olemaan ("zombie"). Koska paluuarvon välittäminen äitiprosessille ja laskutustietojen kokoaminen on vielä kesken.
Jos äitiprosessi on päättynyt ennen lapsiprosessia, merkitsee ydin zombien äidiksi prosessin 1 (init). Se kokoaa laskutustiedot ja vapauttaa prosessinkuvaajan.
Kun prosessi päättyy, saa äiti aina signaalin SIGCHLD. Oletusarvo on, että äiti ei välitä tästä signaalista. Äiti voi pysähtyä odottamaan lapsen päättymistä funktioon wait() tai waitpid():#include < sys/types.h >
#include < sys/wait.h >
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
Palauttaa: päättyneen prosessin pid,
parametrissa statloc on päättyneen lapsen status
Jos lapsiprosessi on jo päättynyt, pääsee äitiprosessi heti jatkamaan. Funktiolla waitpid() voi määrätä odotettavaksi jonkin tietyn prosessin päättymistä, kun taas funktiolla wait() odotetaan minkä tahansa lapsen päättymistä.
* pid == \-1 odota mitä tahansa lapsiprosessia
* pid > 0 odota lasta, jolla ko. pid
* pid == 0 odota mitä tahansa äidin kanssa samaan prosessiryhmän kuuluvaa lasta
* pid < \-1 odota prosessiryhmään \|pid\| kuuluvaa prosessia
----
h3. 4.3 Prosessin koodin vaihto
Prosessi vaihtaa suoritettavaa koodia funktiolla exec(). Siitä on kuusi erilaista muotoa, jotka eroavat komentoriviargumenttien ja ympäristömuuttujien välityksessä. Koodia etsitään annetun polkunimen perusteella funktioissa execl(), execv(), execle(), execve() tai tiedostonimen perusteella ympäristömuuttujassa PATH luetelluista hakemistoista fuktioissa execlp() tai execvp();.
Koodille voi välittää komentoriviargumentteja joko listana (execl()) tai vektorina (execv()). Koodille voi välittää edellisten lisäksi myös haluamansa ympäristömuuttujat aina vektorina execle() tai execve() tai äidin ympäristömuuttujat periytyvät lapselle sellaisenaan environ-muuttujasta.#include < unistd.h >
int execl(const char *pathname, const char *arg0, ... /* NULL */);
int execv(const char *pathname, char *const argv[]);
int execle(const char *pathname, const char *arg0, ... /* NULL, char *const envp[] */);
int execve(const char *pathname, char *const argv[], char *const envp[]);
int execlp(const char *filename, const char *arg0, ... /* NULL */)
int execvp(const char *filename, char *const argv[]);
#include < sys/types.h >
#include < sys/wait.h >
#include < unistd.h >
char *env_init[] = { "USER=unknown", "PATH=/tmp", NULL };
int
main(void)
{
pid_t pid;
if ( (pid = fork()) < 0)
perror("fork error");
else if (pid == 0) { /* specify pathname, specify environment */
if (execle("/bin",
"echo", "myarg1", "MY ARG2", (char *) 0,
env_init) < 0)
perror("execle error");
}
if (waitpid(pid, NULL, 0) < 0)
perror("wait error");
if ( (pid = fork()) < 0)
perror("fork error");
else if (pid == 0) { /* specify filename, inherit environment */
if (execlp("echo",
"echo", "only 1 arg", (char *) 0) < 0)
perror("execlp error");
}
exit(0);
} |