exec (Unix)
exec è una funzione di sistema presente nella libreria standard del C del progetto GNU, la GNU C Library.
Informazioni di carattere generale
modificaExec sostituisce i segmenti codice e dati del processo correntemente in esecuzione nello stato di utente con quelli di un altro programma contenuto in un file eseguibile specificato.
Agendo solo su processi in stato di utente la exec non interviene sul segmento di sistema e sui file utilizzati dal processo che la invoca.
Durante la chiamata ad exec e al successivo caricamento nella Ram del nuovo codice compilato il processo mantiene lo stesso pid.
La funzione exec deve passare dei parametri al nuovo programma che viene eseguito. Essi vengono letti dal programma tramite il meccanismo di passaggio usuale argc, argv.
Implementazione
modificaQuesto servizio è implementato in C sotto Unix (ma anche in generale in POSIX ), tramite una famiglia di chiamate di sistema (comprese in unistd.h su unix e process.h nei sistemi dos/win32.).
Principalmente troviamo:
int execl(const char *pathname, const char *arg, ..., (char *) NULL);
int execle(const char *pathname, const char *arg, ..., (char *) NULL, char *const envp[]);
int execlp(const char *file, const char *arg, ..., (char *) NULL);
int execlpe(const char *path, const char *arg0, ..., const char *const *envp); //Solo process.h ??
int execv(const char *pathname, char *const argv[]);
int execve(const char *pathname, char *const argv[], char *const envp[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
Il primo argomento, pathname che vediamo in ciascuno degli exec*, specifica il percorso e il nome del file da eseguire come processo figlio. Gli argomenti arg0,...,argN sono una lista di puntatori agli argomenti da passare al processo figlio; argv è un array di puntatori agli argomenti. L'argomento envp è invece un array di puntatori alla configurazione dell'ambiente attuale. Le chiamate execle, execlpe, execve ed execvpe (ovvero quelle con il suffisso 'e'), alterano l'ambiente del loro processo figlio, passando una lista con le configurazioni d'ambiente per l'appunto tramite l'argomento envp (array di puntatori a caratteri).
Funzioni e nomi
modificaL'unica vera chiamata di sistema è la execve, le altre si differenziano da questa per come vengono passati i parametri.
In linea di massima si può dire che la p finale che troviamo nelle due varianti execlp ed execvp, hanno come significato che il primo parametro passato come argomento non dev'essere necessariamente il path ma può essere il nome di ciò che intendiamo eseguire.
Un esempio potrebbe essere il seguente:
execl("/bin/ls","ls", "-l", (char *)0);
e il suo complementare che contiene la p ma non il path:
execlp("ls","ls", "-l", (char *)0);
Le altre lettere che possiamo trovare oltre al suffisso "p" (e, l o v) presentano dei significati ben precisi.
Riassumendo si hanno le seguenti funzioni di base:
Funzione | Caratteristica |
---|---|
p |
Non serve specificare il path. |
v |
Gli argomenti a riga di comando saranno passati alla funzione tramite un array di puntatori. |
e |
L'insieme dei puntatori dell'environment è passato esplicitamente al processo figlio. |
l |
Gli argomenti a riga di comando sono passati individualmente alla funzione. |
Gestione degli errori
modificaNormalmente le funzioni exec* non tornano al processo chiamante, se una funzione exec torna al processo chiamate vuol dire che la chiamata non è andata a buon fine. Viene allora tornato il valore di errore -1, ed errno ci presenta uno dei seguenti valori:
Errore | Significato |
---|---|
E2BIG |
Sono stati immessi troppi argomenti. |
EACCES |
Non si può avere l'accesso al file specificato (problemi di blocco/privilegi). |
ENOENT |
Il file o il path specificato non esiste. |
ENOMEM |
Non c'è abbastanza memoria per eseguire il processo figlio |
Specifiche di Execve e delle sue varianti:
execve
modificaSintassi
modificaint execve (const char *pathname, char *const argv[], char *const envp[])
- "pathname" è il nome dell'eseguibile (completo di percorso) per il nuovo processo
- "argv[]" è il vettore degli argomenti in input a pathname (accessibile dal main come main(int argc, char *argv[], char *env[])).
- "env[]" è il vettore di ambiente in cui pathname è eseguito (accessibile dal main come main(int argc, char *argv[], char *env[])).
- Gli elementi di env hanno la forma "var=valore".
Comportamento
modificaexecve esegue le seguenti azioni
- cerca il formato di pathname
- lancia il loader per il formato di pathname
- crea una nuova mappa di memoria per il processo
- sistema in maniera opportuna argomenti e ambiente
- risistema le strutture dati di gestione della memoria del processo
- carica una piccola parte dell'eseguibile (demand paging)
- trasferisce il controllo al nuovo programma
Il processo viene poi messo in user mode: la chiamata - come tutte quelle della famiglia exec - non ritorna che in caso di errore.
Esempio d'uso
modifica/*file sorgente execve.c*/
#include <stdio.h>
#include <unistd.h>
extern char **environ;
int main() {
char *argv[] = {"nomeFile", NULL};
if(execve("nomeFile", argv, enviroment))==-1)
perror("execve fallita");
}
execl
modificaesempio
modificaPassiamo a visualizzare un esempio di utilizzo di execl:
int main(){
printf("Esecuzione di ls\n");
execl("/bin/ls", "ls", "-l", (char*)0);
printf("Si è verificato un errore nell'esecuzione del codice\n");
exit(1);
}
Osservazioni: Ci si potrebbe chiedere come mai dopo execl, ci sia una printf che segnala un errore, ed un'uscita che richiama un po' l'attenzione, ma bisogna pensare che execl elimina il programma originale sovrascrivendolo con quello passato come parametro. Quindi le istruzioni che seguono la chiamata ad execl, verranno eseguite solo se non sono state sovrascritte dal nuovo programma, cioè solo se si è verificato un errore durante l'esecuzione, e il controllo è stato ripassato al chiamante (execl).
execv
modificaesempio
modificaOra vediamo un esempio di utilizzo di execv:
int main() {
char *a[] = {"ls", "-l", (char *)0};
printf("Esecuzione di ls\n");
execv("/home/ls", a);
printf("Si è verificato un errore nell'esecuzione del codice\n");
exit(1);
}
Osservazioni: Come prima le istruzioni che seguono la chiamata ad execv, verranno eseguite solo se non sono state sovrascritte dal nuovo programma, cioè solo se si è verificato un errore durante l'esecuzione, e il controllo è stato ripassato al chiamante (execv).
execlp
modificaPrototipo: int execlp(const char *file, const char *arg, ..., (char *) NULL);
- Il primo argomento specifica il path del programma (cercato nelle cartelle della variabile di ambiente path)
- Gli argomenti successivi, specificano una lista di stringhe terminata dalla stringa NULL che costituiscono gli argomenti passati al programma
Esempio di utilizzo:
for (i=1; i<=2; i++){
fork();
execlp("echo", "myEcho", "i", (char*)0);
printf("%d\n", i); //codice che non verrà mai eseguito, a meno di errori nella execlp
}
La compilazione fornirà in output:
i i
execvp
modificaPrototipo: int execvp(const char *file, char *const argv[]);
Esempio di utilizzo:
int main(){
char *a[]={"ls", "-l", (char *)0};
printf("Esecuzione di ls\n");
execvp("ls", a);
printf("Si è verificato un errore nell'esecuzione del codice\n");
exit(1);
}
execle
modificaCollegamenti esterni
modifica- Repository sorgenti di Exec, su openbsd.su.