Ero sivun ”Fork()” versioiden välillä

Linux.fista
Siirry navigaatioon Siirry hakuun
(→‎Taustaprosessin luominen: ISO C90 kieltää esittelyjen ja koodin sekoittamisen)
 
(8 välissä olevaa versiota 5 käyttäjän tekeminä ei näytetä)
Rivi 1: Rivi 1:
Fork on SVr4, 4.3BSD ja POSIX.1-2001 -standardienmukainen funktio [[C]]:ssä, jolla luodaan ohjelmalle kutsun suorituskohtaan lapsiprosessi vastaavassa tilassa. Funktion esittely löytyy unistd.h-otsikkotiedostosta.
'''Fork()''' on SVr4, 4.3BSD ja POSIX.1-2001 -standardien mukainen funktio [[C]]-kielessä. Sillä luodaan ohjelmalle kutsun suorituskohtaan lapsiprosessi vastaavassa tilassa. Funktion esittely löytyy <tt>unistd.h</tt>-otsikkotiedostosta.  
 
==Prototyyppi==
pid_t fork()


==Määritelmä==
pid_t fork()
===Paluuarvo===
===Paluuarvo===
Palautusarvo (tyyppiä pid_t) on -1 virheen tapahtuessa, muuten se vastaa lapsiprosessin [[pid|prosessitunnusta]]. Lapsiprosessille palautusarvo näkyy nollana (0x0).
Palautusarvo (tyyppiä <tt>pid_t</tt>) on -1 virheen tapahtuessa, muuten se vastaa lapsiprosessin [[pid|prosessitunnusta]]. Lapsiprosessille palautusarvo näkyy nollana (0x0).


==Käyttö==
==Käyttö==
=== Demostraatio ===
Fork on se tapa, jolla uusia prosesseja luodaan [[unix]]eissa. Jos lapsiprosessin pitää suorittaa toista ohjelmaa, se käyttää [[exec]]-kutsua forkin ja mahdollisten siivoustoimien jälkeen. Vaikka prosessin kopioiminen ennen kopion korvaamista toisella saattaa tuntua resurssien haaskaukselta, se ei sitä ole: lapsiprosessin muistiavaruus osoittaa samaa fyysistä muistia, kunnes lapsiprosessi kirjoittaa siihen, eikä käyttämätöntä muistia siis tarvitse kopioida ("Copy-on-Write").
 
Fork-kutsun lisäksi on olemassa [[clone]]-kutsu, jolla voi tarkemmin määritellä mitkä resurssit jäävät prosessien yhteisiksi. Tällä kutsulla luodaan [[säie|säikeet]].
 
=== Esimerkki ===
Luodaan seuraavanlainen C-ohjelma:
Luodaan seuraavanlainen C-ohjelma:
<pre>
<source lang="c">
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <unistd.h>
#include <sys/types.h>


int main() {
int main() {
        pid_t pid;
         printf("Luodaan taustaprosessi\n");
         printf("Luodaan taustaprosessi\n");
         pid_t pid = fork();
         pid = fork();
         if (pid==0)    /* Lapsiprosessille pid näkyy nollana */
         if (pid==0)    /* Lapsiprosessille pid näkyy nollana */
                 printf("Olen lapsiprosessi\n");
                 printf("Olen lapsiprosessi\n");
Rivi 24: Rivi 31:
         return 0;
         return 0;
}
}
</pre>
</source>
Tallennetaan koodi tiedostoon <tt>fork.c</tt> ja käännetään se komennolla <tt>[[gcc]] fork.c -o fork</tt>. Nyt ohjelman tuloste olisi seuraavanlainen:
Tallennetaan koodi tiedostoon <tt>fork.c</tt> ja käännetään se komennolla <tt>[[gcc]] fork.c -o fork</tt>. Nyt ohjelman tuloste olisi seuraavanlainen:
<pre>
<pre>
Rivi 37: Rivi 44:
===[[demoni|Taustaprosessin]] luominen===
===[[demoni|Taustaprosessin]] luominen===
fork-funktion ajavan ohjelman toinen osa jää taustalle ja alkuperäinen ohjelma voidaan sammuttaa. Esimerkkikoodi:
fork-funktion ajavan ohjelman toinen osa jää taustalle ja alkuperäinen ohjelma voidaan sammuttaa. Esimerkkikoodi:
<pre>
<source lang="c">
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <unistd.h>
#include <sys/types.h>


int main() {
int main() {
        pid_t pid;
         printf("Luodaan taustaprosessi\n");
         printf("Luodaan taustaprosessi\n");
         pid_t pid = fork();
         pid = fork();


         if (pid > 0) return 0;      /* Isäntäprosessi loppuu */
         if (pid > 0) return 0;      /* Isäntäprosessi loppuu */
Rivi 51: Rivi 60:
         return 0;
         return 0;
}
}
</pre>
</source>
Tallennetaan koodi tiedostoon <tt>taustaprosessi.c</tt>, käännetään se komennolla <tt>gcc taustaprosessi.c -o taustaprosessi</tt> ja ajetaan se (<tt>./taustaprosessi</tt>). Tuloste on seuraavanlainen:
Tallennetaan koodi tiedostoon <tt>taustaprosessi.c</tt>, käännetään se komennolla <tt>gcc taustaprosessi.c -o taustaprosessi</tt> ja ajetaan se (<tt>./taustaprosessi</tt>). Tuloste on seuraavanlainen:
  $ ./taustaprosessi
  $ ./taustaprosessi
Rivi 58: Rivi 67:
Ohjelma siis näyttää sammuvan heti tulostettuaan tekstirivin. Tutkitaanpa asiaa hieman tarkemmin. Aja komento
Ohjelma siis näyttää sammuvan heti tulostettuaan tekstirivin. Tutkitaanpa asiaa hieman tarkemmin. Aja komento
  [[ps]] aux | [[grep]] taustaprosessi
  [[ps]] aux | [[grep]] taustaprosessi
Jolloin huomaat, että taustaprosessi-niminen ohjelma on yhä ajossa. Myös [[top]]:in prosessilistauksessa sen pitäisi näkyä yläpäässä kuluttamassa paljon prosessoritehoa.
Jolloin huomaat, että taustaprosessi-niminen ohjelma on yhä ajossa. Myös [[top]]:in prosessilistauksessa sen pitäisi näkyä yläpäässä kuluttamassa paljon prosessoritehoa. Voit tappaa sen komennolla <tt>[[killall]] taustaprosessi</tt>.




[[Luokka:Järjestelmä]]
[[Luokka:Järjestelmä]]
[[Luokka:Prosessienhallinta]]

Nykyinen versio 15. lokakuuta 2016 kello 23.42

Fork() on SVr4, 4.3BSD ja POSIX.1-2001 -standardien mukainen funktio C-kielessä. Sillä luodaan ohjelmalle kutsun suorituskohtaan lapsiprosessi vastaavassa tilassa. Funktion esittely löytyy unistd.h-otsikkotiedostosta.

Prototyyppi[muokkaa]

pid_t fork()

Paluuarvo[muokkaa]

Palautusarvo (tyyppiä pid_t) on -1 virheen tapahtuessa, muuten se vastaa lapsiprosessin prosessitunnusta. Lapsiprosessille palautusarvo näkyy nollana (0x0).

Käyttö[muokkaa]

Fork on se tapa, jolla uusia prosesseja luodaan unixeissa. Jos lapsiprosessin pitää suorittaa toista ohjelmaa, se käyttää exec-kutsua forkin ja mahdollisten siivoustoimien jälkeen. Vaikka prosessin kopioiminen ennen kopion korvaamista toisella saattaa tuntua resurssien haaskaukselta, se ei sitä ole: lapsiprosessin muistiavaruus osoittaa samaa fyysistä muistia, kunnes lapsiprosessi kirjoittaa siihen, eikä käyttämätöntä muistia siis tarvitse kopioida ("Copy-on-Write").

Fork-kutsun lisäksi on olemassa clone-kutsu, jolla voi tarkemmin määritellä mitkä resurssit jäävät prosessien yhteisiksi. Tällä kutsulla luodaan säikeet.

Esimerkki[muokkaa]

Luodaan seuraavanlainen C-ohjelma:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main() {
        pid_t pid;
        printf("Luodaan taustaprosessi\n");
        pid = fork();
        if (pid==0)     /* Lapsiprosessille pid näkyy nollana */
                printf("Olen lapsiprosessi\n");

        else if (pid>0)    /* Forkkaus onnistui, tämä on isäntä */
                printf("Olen isäntäprosessi\n");
        printf("Terve Linux.fi\n");
        return 0;
}

Tallennetaan koodi tiedostoon fork.c ja käännetään se komennolla gcc fork.c -o fork. Nyt ohjelman tuloste olisi seuraavanlainen:

Luodaan taustaprosessi
Olen lapsiprosessi
Terve Linux.fi
Olen isäntäprosessi
Terve Linux.fi

Lisäämällä ehtolauseisiin yhden tulostusrivin sijaan vaikkapa silmukassa ajettavaa koodia (esim. while(1) printf("Lapsiprosessi");) huomataan, että prosesseja ajetaan vuorotellen.

Taustaprosessin luominen[muokkaa]

fork-funktion ajavan ohjelman toinen osa jää taustalle ja alkuperäinen ohjelma voidaan sammuttaa. Esimerkkikoodi:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main() {
        pid_t pid;
        printf("Luodaan taustaprosessi\n");
        pid = fork();

        if (pid > 0) return 0;      /* Isäntäprosessi loppuu */
        if (pid < 0) return -1;     /* Forkkaus ei onnistunut 0_o */

        while(1) { }   /* Ikuinen silmukka */
        return 0;
}

Tallennetaan koodi tiedostoon taustaprosessi.c, käännetään se komennolla gcc taustaprosessi.c -o taustaprosessi ja ajetaan se (./taustaprosessi). Tuloste on seuraavanlainen:

$ ./taustaprosessi
Luodaan taustaprosessi
$

Ohjelma siis näyttää sammuvan heti tulostettuaan tekstirivin. Tutkitaanpa asiaa hieman tarkemmin. Aja komento

ps aux | grep taustaprosessi

Jolloin huomaat, että taustaprosessi-niminen ohjelma on yhä ajossa. Myös top:in prosessilistauksessa sen pitäisi näkyä yläpäässä kuluttamassa paljon prosessoritehoa. Voit tappaa sen komennolla killall taustaprosessi.