Creazione e risoluzione del processo

Fino ad ora sappiamo che ogni volta che eseguiamo un programma, viene creato un processo che verrebbe terminato dopo il completamento dell'esecuzione. E se avessimo bisogno di creare un processo all'interno del programma e potessimo programmare un'attività diversa per esso. Può essere raggiunto? Sì, ovviamente attraverso la creazione del processo. Ovviamente, al termine del lavoro, verrà terminato automaticamente oppure sarà possibile interromperlo se necessario.

La creazione del processo si ottiene tramite fork() system call. Il processo appena creato è chiamato processo figlio e il processo che lo ha avviato (o il processo quando viene avviata l'esecuzione) è chiamato processo genitore. Dopo la chiamata di sistema fork (), ora abbiamo due processi: genitore e figlio. Come differenziarli? Molto semplice, è attraverso i loro valori di ritorno.

Dopo la creazione del processo figlio, vediamo i dettagli della chiamata di sistema fork ().

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

pid_t fork(void);

Crea il processo figlio. Dopo questa chiamata, ci sono due processi, quello esistente è chiamato processo genitore e quello appena creato è chiamato processo figlio.

La chiamata di sistema fork () restituisce uno dei tre valori:

  • Valore negativo per indicare un errore, ovvero non riuscita nella creazione del processo figlio.

  • Restituisce uno zero per il processo figlio.

  • Restituisce un valore positivo per il processo genitore. Questo valore è l'ID del processo figlio appena creato.

Consideriamo un semplice programma.

File name: basicfork.c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main() {
   fork();
   printf("Called fork() system call\n");
   return 0;
}

Fasi di esecuzione

Compilazione

gcc basicfork.c -o basicfork

Esecuzione / output

Called fork() system call
Called fork() system call

Note- Di solito dopo la chiamata a fork (), il processo figlio e il processo genitore eseguono attività diverse. Se la stessa attività deve essere eseguita, per ogni chiamata fork () verrà eseguita 2 volte n volte, doven è il numero di volte in cui viene invocato fork ().

Nel caso precedente, fork () viene chiamato una volta, quindi l'output viene stampato due volte (2 power 1). Se fork () viene chiamato, diciamo 3 volte, l'output verrebbe stampato 8 volte (2 power 3). Se viene chiamato 5 volte, viene stampato 32 volte e così via.

Dopo aver visto fork () creare il processo figlio, è tempo di vedere i dettagli dei processi genitore e figlio.

Nome file: pids_after_fork.c

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

int main() {
   pid_t pid, mypid, myppid;
   pid = getpid();
   printf("Before fork: Process id is %d\n", pid);
   pid = fork();

   if (pid < 0) {
      perror("fork() failure\n");
      return 1;
   }

   // Child process
   if (pid == 0) {
      printf("This is child process\n");
      mypid = getpid();
      myppid = getppid();
      printf("Process id is %d and PPID is %d\n", mypid, myppid);
   } else { // Parent process 
      sleep(2);
      printf("This is parent process\n");
      mypid = getpid();
      myppid = getppid();
      printf("Process id is %d and PPID is %d\n", mypid, myppid);
      printf("Newly created process id or child pid is %d\n", pid);
   }
   return 0;
}

Fasi di compilazione ed esecuzione

Before fork: Process id is 166629
This is child process
Process id is 166630 and PPID is 166629
Before fork: Process id is 166629
This is parent process
Process id is 166629 and PPID is 166628
Newly created process id or child pid is 166630

Un processo può terminare in uno dei due modi:

  • In modo anomalo, si verifica alla consegna di determinati segnali, ad esempio segnale di terminazione.

  • Normalmente, utilizzando la chiamata di sistema _exit () (o la chiamata di sistema _Exit ()) o la funzione di libreria exit ().

La differenza tra _exit () e exit () è principalmente l'attività di pulizia. Ilexit() fa un po 'di pulizia prima di restituire il controllo al kernel, mentre il _exit() (o _Exit ()) restituirà immediatamente il controllo al kernel.

Considera il seguente programma di esempio con exit ().

Nome file: atexit_sample.c

#include <stdio.h>
#include <stdlib.h>

void exitfunc() {
   printf("Called cleanup function - exitfunc()\n");
   return;
}

int main() {
   atexit(exitfunc);
   printf("Hello, World!\n");
   exit (0);
}

Fasi di compilazione ed esecuzione

Hello, World!
Called cleanup function - exitfunc()

Considera il seguente programma di esempio con _exit ().

Nome file: at_exit_sample.c

#include <stdio.h>
#include <unistd.h>

void exitfunc() {
   printf("Called cleanup function - exitfunc()\n");
   return;
}

int main() {
   atexit(exitfunc);
   printf("Hello, World!\n");
   _exit (0);
}

Fasi di compilazione ed esecuzione

Hello, World!