Chiamata di sistema Linux Exec

Linux Exec System Call



La chiamata di sistema exec viene utilizzata per eseguire un file che risiede in un processo attivo. Quando viene chiamato exec, il file eseguibile precedente viene sostituito e il nuovo file viene eseguito.

Più precisamente, possiamo dire che l'uso della chiamata di sistema exec sostituirà il vecchio file o programma dal processo con un nuovo file o programma. L'intero contenuto del processo viene sostituito con un nuovo programma.







Il segmento di dati utente che esegue la chiamata di sistema exec() viene sostituito con il file di dati il ​​cui nome è fornito nell'argomento durante la chiamata di exec().



Il nuovo programma viene caricato nello stesso spazio di processo. Il processo corrente viene appena trasformato in un nuovo processo e quindi l'ID processo PID non viene modificato, questo perché non stiamo creando un nuovo processo, stiamo semplicemente sostituendo un processo con un altro processo in exec.



Se il processo attualmente in esecuzione contiene più di un thread, tutti i thread verranno terminati e la nuova immagine del processo verrà caricata e quindi eseguita. Non esistono funzioni di distruzione che interrompono i thread del processo corrente.





Il PID del processo non viene modificato ma i dati, il codice, lo stack, l'heap, ecc. del processo vengono modificati e vengono sostituiti con quelli del processo appena caricato. Il nuovo processo viene eseguito dal punto di ingresso.

La chiamata di sistema Exec è una raccolta di funzioni e nel linguaggio di programmazione C, i nomi standard per queste funzioni sono i seguenti:



  1. eccellente
  2. execle
  3. execlp
  4. execv
  5. esecutivo
  6. execvp


Va notato qui che queste funzioni hanno la stessa base eseguire seguito da una o più lettere. Questi sono spiegati di seguito:

e: È un array di puntatori che punta a variabili di ambiente e viene passato esplicitamente al processo appena caricato.

l: l è per gli argomenti della riga di comando passati un elenco alla funzione

P: p è la variabile d'ambiente del percorso che aiuta a trovare il file passato come argomento da caricare nel processo.

v: v è per gli argomenti della riga di comando. Questi vengono passati come un array di puntatori alla funzione.

Perché si usa exec?

exec viene utilizzato quando l'utente desidera avviare un nuovo file o programma nello stesso processo.

Lavoro interno dell'exec

Considera i seguenti punti per comprendere il funzionamento di exec:

  1. L'immagine di processo attuale viene sovrascritta con una nuova immagine di processo.
  2. La nuova immagine del processo è quella passata come argomento exec
  3. Il processo attualmente in esecuzione è terminato
  4. La nuova immagine di processo ha lo stesso ID di processo, lo stesso ambiente e lo stesso descrittore di file (perché il processo non viene sostituito, l'immagine di processo viene sostituita)
  5. Le statistiche della CPU e la memoria virtuale sono interessate. La mappatura della memoria virtuale dell'immagine di processo attuale viene sostituita dalla memoria virtuale della nuova immagine di processo.

Sintassi delle funzioni familiari exec:

Di seguito sono riportate le sintassi per ciascuna funzione di exec:

int execl(const char* percorso, const char* arg, …)
int execlp(const char* file, const char* arg, …)
int execle(const char* percorso, const char* arg, …, char* const envp[])
int execv(const char* percorso, const char* argv[])
int execvp(const char* file, const char* argv[])
int execvpe(const char* file, const char* argv[], char *const envp[])

Descrizione:

Il tipo restituito da queste funzioni è Int. Quando l'immagine del processo viene sostituita correttamente, non viene restituito nulla alla funzione chiamante perché il processo che l'ha chiamata non è più in esecuzione. Ma se c'è qualche errore verrà restituito -1. Se si verifica un errore e errno è impostato.

Nella sintassi:

  1. il percorso viene utilizzato per specificare il nome completo del percorso del file che deve essere eseguito.
  1. arrabbiato è l'argomento passato. In realtà è il nome del file che verrà eseguito nel processo. La maggior parte delle volte il valore di arg e path è lo stesso.
  1. const char* arg nelle funzioni execl(), execlp() ed execle() è considerato come arg0, arg1, arg2, …, argn. È fondamentalmente un elenco di puntatori a stringhe terminate da null. Qui il primo argomento punta al nome del file che verrà eseguito come descritto al punto 2.
  1. ambiente è un array che contiene puntatori che puntano alle variabili di ambiente.
  1. file viene utilizzato per specificare il nome del percorso che identificherà il percorso del nuovo file dell'immagine di processo.
  1. Le funzioni di exec chiamano che terminano con e vengono utilizzati per modificare l'ambiente per la nuova immagine di processo. Queste funzioni passano l'elenco delle impostazioni dell'ambiente usando l'argomento ambiente . Questo argomento è un array di caratteri che punta a una stringa con terminazione null e definisce la variabile di ambiente.

Per utilizzare le funzioni della famiglia exec, è necessario includere il seguente file di intestazione nel programma C:

#includere

Esempio 1: utilizzo della chiamata di sistema exec nel programma C

Considera il seguente esempio in cui abbiamo usato la chiamata di sistema exec nella programmazione C in Linux, Ubuntu: Abbiamo due file c qui example.c e hello.c:

esempio.c

CODICE:

#includere
#includere
#includere
intprincipale(intargomento, char *argv[])
{
printf ('PID dell'esempio.c = %d ',getpid());
char *argomenti[] = {'Ciao', 'C', 'Programmazione',NULLO};
execv('./Ciao',argomenti);
printf ('Torna all'esempio.c');
Restituzione 0;
}

Ciao C

CODICE:

#includere
#includere
#includere
intprincipale(intargomento, char *argv[])
{
printf ('Siamo in Hello.c ');
printf ('PID di ciao.c = %d ',getpid());
Restituzione 0;
}

PRODUZIONE:

PID dell'esempio.c = 4733
Siamo in Hello.c
PID di ciao.c = 4733

Nell'esempio sopra abbiamo un file example.c e un file hello.c. Nel file .c di esempio prima di tutto abbiamo stampato l'ID del processo corrente (il file example.c è in esecuzione nel processo corrente). Quindi nella riga successiva abbiamo creato un array di puntatori di caratteri. L'ultimo elemento di questo array dovrebbe essere NULL come punto di terminazione.

Quindi abbiamo usato la funzione execv() che prende il nome del file e l'array di puntatori di caratteri come argomento. Va notato qui che abbiamo usato ./ con il nome di file, specifica il percorso del file. Poiché il file si trova nella cartella in cui risiede example.c, non è necessario specificare il percorso completo.

Quando viene chiamata la funzione execv(), la nostra immagine di processo verrà sostituita ora il file example.c non è nel processo ma il file hello.c è nel processo. Si può vedere che l'ID del processo è lo stesso se hello.c è l'immagine del processo o example.c è l'immagine del processo perché il processo è lo stesso e l'immagine del processo viene solo sostituita.

Quindi abbiamo un'altra cosa da notare qui che è l'istruzione printf() dopo che execv() non viene eseguito. Questo perché il controllo non viene mai restituito alla vecchia immagine di processo una volta che la nuova immagine di processo la sostituisce. Il controllo ritorna alla funzione di richiamo solo se la sostituzione dell'immagine di processo non è andata a buon fine. (Il valore restituito è -1 in questo caso).

Differenza tra le chiamate di sistema fork() ed exec():

La chiamata di sistema fork() viene utilizzata per creare una copia esatta di un processo in esecuzione e la copia creata è il processo figlio e il processo in esecuzione è il processo padre. Invece, la chiamata di sistema exec() viene utilizzata per sostituire un'immagine di processo con una nuova immagine di processo. Quindi non esiste il concetto di processi padre e figlio nella chiamata di sistema exec().

Nella chiamata di sistema fork() i processi padre e figlio vengono eseguiti contemporaneamente. Ma nella chiamata di sistema exec(), se la sostituzione dell'immagine del processo ha esito positivo, il controllo non torna al punto in cui è stata chiamata la funzione exec, ma esegue il nuovo processo. Il controllo verrà ritrasferito solo in caso di errore.

Esempio 2: combinazione di chiamate di sistema fork() ed exec()

Considera il seguente esempio in cui abbiamo usato sia le chiamate di sistema fork() che exec() nello stesso programma:

esempio.c

CODICE:

#includere
#includere
#includere
intprincipale(intargomento, char *argv[])
{
printf ('PID dell'esempio.c = %d ',getpid());
pid_t p;
P=forchetta();
Se(P== -1)
{
printf ('Si è verificato un errore durante la chiamata a fork()');
}
Se(P==0)
{
printf ('Siamo nel processo del bambino ');
printf ('Chiamata hello.c dal processo figlio ');
char *argomenti[] = {'Ciao', 'C', 'Programmazione',NULLO};
execv('./Ciao',argomenti);
}
altro
{
printf ('Siamo nel processo genitore');
}
Restituzione 0;
}

Ciao C:

CODICE:

#includere
#includere
#includere
intprincipale(intargomento, char *argv[])
{
printf ('Siamo in Hello.c ');
printf ('PID di ciao.c = %d ',getpid());
Restituzione 0;
}

PRODUZIONE:

PID dell'esempio.c = 4790
Siamo in Processo Genitori
Siamo nel processo del bambino
Chiamare hello.c dal processo figlio
Siamo in hello.c
PID di ciao.c = 4791

In questo esempio abbiamo usato la chiamata di sistema fork(). Quando viene creato il processo figlio, 0 verrà assegnato a p e quindi ci sposteremo al processo figlio. Ora verrà eseguito il blocco di istruzioni con if(p==0). Viene visualizzato un messaggio e abbiamo utilizzato la chiamata di sistema execv() e l'immagine del processo figlio corrente che è example.c verrà sostituita con hello.c. Prima della chiamata execv(), i processi figlio e padre erano gli stessi.

Si può vedere che il PID di example.c e hello.c è diverso ora. Questo perché example.c è l'immagine del processo padre e hello.c è l'immagine del processo figlio.