funcion - linux fork c
¿Diferencia entre “sistema” y “exec” en Linux? (12)
En general, el "sistema" es muy ineficiente y no debe usarlo a menos que tenga un código pequeño. Si necesita ejecutar varios programas en su proceso, sería mejor que use fork & exec aunque lo haga más complicado. Aquí hay una lista de diferencias entre ellos:
1- El comando "sistema" crea una copia del shell para ejecutar su programa. Cada vez que llamas a un sistema, creas una copia de shell. Por lo tanto, no lo use cuando tenga muchos programas para ejecutar dentro de su proceso.
2- Específicamente, si desea ejecutar funciones del sistema como "mv", "mkdir", sería mejor usar rutinas como mkdir (), unlink () o remove () en lugar de ejecutarlas a través de "system (" rm .... ") o sistema (" mkdir .... ")".
3- Dado que el sistema llama a shell para ejecutar el programa deseado, es posible que tenga algunos problemas de permiso de usuario. Por ejemplo, alguien puede descifrar su código y ejecutar otra cosa en lugar del programa que desea ejecutar mediante el comando del sistema.
Para obtener más información, puede leer el capítulo 11 de este libro: "Programación de sistemas UNIX" por David Curry.
¿Cuál es la diferencia entre el system
y los comandos de la familia exec
? ¿Especialmente quiero saber cuál de ellos crea un proceso secundario para trabajar?
Hay algunas diferencias significativas entre exec(2)
y el system(3)
que deben tenerse en cuenta. system()
regresa a la persona que llama, mientras que exec()
reemplaza el código existente con la nueva imagen. Esto se ha explicado anteriormente.
Sin embargo, la diferencia no tan sutil se produce cuando desea ejecutar un procedimiento y luego regresar a su código existente, recibiendo el código de retorno del procedimiento invocado. system()
proporciona un código de retorno, pero el código de retorno solo se puede usar para detectar una condición de error y no se puede usar para recuperar un código de retorno.
Una posible secuencia apropiada de llamadas al sistema es:
#include <unistd.h>
#include <sys/wait.h>
#define NUMARGS 2
int main (int argc, char *argv[])
{
pid_t child_pid, wait_pid;
int * child_status;
char * exec_path = "/path/to/executable";
char * child_args[NUMARGS] = {0,0};
child_pid = fork();
if (0 == child_pid)
{ // In child process
...
int child_ret_code = execv(exec_path, child_args); //or whichever flavor of exec() that floats your boat
... // if child_ret_code = -1, process execv() error return
}
else if (-1 == child_pid)
{
... //process error return from fork
}
else if (0 < child_pid)
{ // Parent process
wait_pid = wait(child_status);
if (-1 == wait_pid)
{
... //Process error return from wait()
}
else
{ // Good fork/exec/wait
if (WIFEXITED(child_status)) // Child exited normally and hopefully returned exit code
{
int child_ret_code = WEXITSTATUS(child_status);
... // Continue on as you would after call to system(3)
// except now you have the return code you needed
}
}
}
}
Hay otras sutilezas en esta secuencia que pueden determinarse mediante una lectura cuidadosa de las páginas man relevantes, pero este código funcionará bien en ausencia de señales, procesos secundarios múltiples, etc. Además, las declaraciones en línea pueden limitar el alcance de la secuencia. variables, pero se incluyen para permitir que este código se use como una plantilla que funcione (puede usar un estilo de codificación diferente :-).
La función exec reemplaza la imagen del proceso en ejecución cuando se realiza correctamente, no se crea un hijo (a menos que lo haga usted mismo previamente con fork()
). La función system () realiza un proceso secundario y se devuelve cuando el comando suministrado termina de ejecutarse o se produce un error.
La respuesta de JonSpencer está bien, excepto que child_status debe ser un int (no hay un puntero a int) y se debe pasar para esperar la función por referencia.
Entonces, el código sería básicamente el mismo, solo cambiando esas dos cosas:
#include <unistd.h>
#include <sys/wait.h>
#define NUMARGS 2
int main (int argc, char *argv[])
{
pid_t child_pid, wait_pid;
int child_status;
char * exec_path = "/path/to/executable";
char * child_args[NUMARGS] = {0,0};
child_pid = fork();
if (0 == child_pid)
{ // In child process
...
int child_ret_code = execv(exec_path, child_args); //or whichever flavor of exec() that floats your boat
... // if child_ret_code = -1, process execv() error return
}
else if (-1 == child_pid)
{
... //process error return from fork
}
else if (0 < child_pid)
{ // Parent process
wait_pid = wait(&child_status);
if (-1 == wait_pid)
{
... //Process error return from wait()
}
else
{ // Good fork/exec/wait
if (WIFEXITED(child_status)) // Child exited normally and hopefully returned exit code
{
int child_ret_code = WEXITSTATUS(child_status);
... // Continue on as you would after call to system(3)
// except now you have the return code you needed
}
}
}
}
(Señale que todavía no tengo suficiente reputación para comentar la publicación de Jon, así que la edité. Algunas personas rechazaron la edición y me pidieron que respondiera la pregunta en lugar de editarla, pero creo que en este caso es mucho más simple y práctico. y claro, editar un código existente simplemente corrigiendo un pequeño error que escribir una copia completa / pegar / modificar la respuesta.) De todos modos, gracias a JonSpencer por su respuesta, ¡fue muy útil para mí!
System () creará un proceso secundario e invocará otro sub shell, mientras que exec () no creará un proceso secundario. El Ejemplo dado borrará la diferencia.
algún código ...
exec (''ls -l'')
echo "1 2 3" // Esto no se ejecutará en bash (ya que el comando exec usa el mismo shell)
algún código ...
system (ls -l) echo "1 2 3" // Esto se ejecutará después de finalizar el proceso secundario del sistema, ya que son diferentes del PID principal.
exec () reemplaza el proceso en ejecución actual con la imagen de proceso de la función que se está ejecutando ... solo se pueden invocar archivos ejecutables utilizando esto.
system () desactiva implícitamente un nuevo proceso para atender la solicitud y devuelve el valor que obtuvo a través del proceso secundario que inicialmente se bifurcó. Utiliza el shell predeterminado del sistema para llevar a cabo la operación.
system () invoca el programa deseado o el comando incorporado usando un shell, esta es una manera ineficiente porque un shell se inicia antes de que se inicie el programa.
En el caso de la familia exec de llamadas al sistema, se está creando una imagen completamente nueva, es decir, reemplazan el proceso actual con un proceso nuevo especificado por la ruta o el archivo o cualquier argumento que mencione.
Lo que hay que tener en cuenta es que, cuando se utiliza la familia de llamadas de sistema exec, el programa original ya no se ejecutará después de que se inicie el nuevo.
system () invocará el shell de comandos predeterminado de su sistema, que ejecutará la cadena de comando pasada como un argumento, que puede o no crear otros procesos, que dependerán del comando y del sistema. De cualquier manera, al menos se creará un proceso de shell de comandos.
Con system () puede invocar cualquier comando, mientras que con exec (), solo puede invocar un archivo ejecutable. Los scripts de shell y los archivos de proceso por lotes deben ser ejecutados por el comando shell.
Básicamente son completamente diferentes utilizados para diferentes propósitos. Además, exec () reemplaza el proceso de llamada y no regresa. Una comparación más útil sería entre system () y spawn (). Si bien el sistema puede ser más fácil de invocar, devuelve un valor que le dice si se invocó el comando shell y no le dice nada sobre el éxito del comando en sí. Con spawn () puedes obtener el código de salida del proceso; Por convenio, se utiliza un valor distinto de cero para indicar condiciones de error. Al igual que exec (), spawn () debe invocar un ejecutable, no un script de shell o un comando incorporado.
system()
ejecutará el comando suministrado en un proceso secundario que genera. exec()
reemplazará el proceso actual con la invocación del nuevo ejecutable que especifique. Si desea generar un proceso hijo utilizando exec
, deberá fork()
su proceso de antemano.
system()
llama a sh
para manejar su línea de comando, para que pueda obtener expansión de comodines, etc. exec()
y sus amigos reemplazan la imagen del proceso actual con una nueva imagen de proceso.
Con el system()
, su programa continúa ejecutándose y vuelve a aparecer algo sobre el comando externo al que llamó. Con exec()
, su proceso se borra.
En general, creo que se podría pensar en system()
como una interfaz de nivel superior. Usted podría duplicar su funcionalidad utilizando una combinación de fork()
, exec()
y wait()
.
Para responder a su pregunta final, system()
hace que se cree un proceso hijo, y la familia exec()
no lo hace. Necesitarías usar fork()
para eso.
Para crear un proceso:
-
fork(2)
, un sistema llamado directamente al kernel
Para ejecutar un programa, reemplazando la imagen actual:
-
execve(2)
, una llamada del sistema directamente al kernel, generalmente llamadaexec
Esperar a que finalice un proceso hijo:
-
wait(2)
, una llamada al sistema directamente al kernel
Para ejecutar un programa en un shell en un proceso hijo y esperar a que finalice:
-
system(3)
, una función de biblioteca
Para obtener las páginas de manual de todo lo anterior:
$ man 2 fork execve wait
$ man 3 system
int system(const char *cmdstring);
Ej: system("date > file");
En general, el sistema se implementa llamando a fork, exec y waitpid , hay tres tipos de valores de retorno.
- Si falla la bifurcación o waitpid devuelve un error distinto de EINTR, el sistema devuelve –1 con errno configurado para indicar el error.
- Si el exec falla, lo que implica que el shell no se puede ejecutar, el valor de retorno es como si el shell hubiera ejecutado exit (127).
- De lo contrario, las tres funciones (fork, exec y waitpid) tienen éxito, y el valor de retorno del sistema es el estado de terminación del shell, en el formato especificado para waitpid.
La función de bifurcación es crear un nuevo proceso (el secundario) que luego hace que se ejecute otro programa llamando a una de las funciones exec . Cuando un proceso llama a una de las funciones exec, ese proceso es reemplazado completamente por el nuevo programa, y el nuevo programa comienza a ejecutarse en su función principal. El ID de proceso no cambia a través de un exec, porque no se crea un nuevo proceso; exec simplemente reemplaza el proceso actual (sus segmentos de texto, datos, montón y pila) con un nuevo programa desde el disco.
Hay seis funciones ejecutivas diferentes ,
int execl(const char *pathname, const char *arg0, ... /* (char *)0 */ );
int execv(const char *pathname, char *const argv []);
int execle(const char *pathname, const char *arg0, .../* (char *)0, char *const envp[] */ );
int execve(const char *pathname, char *const argv[], char *const envp []);
int execlp(const char *filename, const char *arg0,... /* (char *)0 */ );
int execvp(const char *filename, char *const argv []);