Procesos en C
>
Un Proceso: es un programa en ejecucion o la instancia de un programa funcionando en un ordenador.
Los Estados de un proceso son:
Nuevo ::Cuando se crea un nuevo proceso, es admitida por el S.O y esta en espera de ser utilizado.
Ejecucion ::Cuando el proceso esta siendo utilizado.
Bloqueado/Suspendido ::Cuando el proceso NO se puede ejecutar, hasta que presente algun suceso.
Listo ::Cuando el proceso esta en espera de ser utilizado.
Terminado ::Cuando el proceso es excluido por el S.O del Grupo de Procesos.
Cada proceso que se cree, tiene su propio espacio en memoria, por lo cual hay varios puntos a considerar en la creacion de un proceso, que son:
<>
� Asignar un Unico Identificador al Nuevo Proceso ( un ID )
� Asignar Espacio para el Nuevo Proceso
� Iniciar el Bloque de Control de Procesos
� Establecer los Enlazes Apropiados
� Crear o Ampliar otras Estructuras de Datos
Al ejecutar un programa el S.O automaticamente le asigna un ID a nuestro programa ( realiza operaciones de creacion de proceso ).
De este modo obtenemos un ID, que ser� el proceso PADRE, que sera la instancia de nuestro programa. Asi despu�s desde el
codigo con solamente invocar la funcion " fork() " nosotros crearemos procesos Hijos para nuestro procesos PADRE.
Ventajas/Aportaciones de la Creacion de un Proceso
� Trabajo Interactivo y en Segundo Plano
� Procesamiento Asincrono
� Aceleracion de la Ejecucion
� Estructuracion Modular del Programa
EL Proceso ID Unix identifica el proceso mediante un entero unico denominado ID, para poder ver el ID de los procesos Padres e hijo(s)
se utilizan las funciones:
-getpid(); ------> pid_t getpid( void ); ---> vemos el ID del proceso que creamos
-getppid(); ------> pid_t getppid( void ); ---> vemos el ID del proceso Padre del Proceso que Creamos
Unix asocia cada proceso con un usuario en particular, de este modo cada usuario posee un ID, este ID de usuario lo podemos llamar mediante:
-getuid(); -----> uid_t getuid( void );
Como vimos, existe el ID del usuario que es el que ejecuta el programa, pero de igual manera tambien un ID de Usuario Efectivo, que viene
siendo el que determina los privilegios que el proceso tiene para el acceso de recursos. Para acceder a el ID de Usuario Efectivo se utiliza:
-geteuid(); ----------> uid_t geteuid( void );
Codigo: ps_001.c
Programa que imprime IDs de: proceso, padre, usuario y usuario efectivo
#include
#include
#include
#include
int main()
{
system( "clear" );
printf( "ID del Proceso: %ld", (long)getpid() );
printf( "\nID del Proceso Padre: %ld", (long)getppid() );
printf( "\nID del Usuario en el Sistema: %ld", (long)getuid() );
printf( "\nID del usuario Efectivo en el Sistema: %ld", (long)geteuid() );
printf( "\n\nPulsa para salir..." );
getchar();
exit(0);
}
Analisis de los Estados de un Proceso
Como mencionamos en el capitulo 1.1, los estados son:
� Nuevo
� Ejecucion
� Bloqueado
� Listo
� Terminado
Ahora veremos una explicacion mas practica dentro del SO.
NUEVO Cuando se crea un proceso nuevo, una vez creado el proceso, nuestro SO asigna un ID al proceso, asigna espacio, etc, etc... y lo manda
a la COLA DE PROCESOS LISTOS, osea en esa cola se colocal los procesos que estan listos o preparados para ser ejecutados.
LISTO Una ves pasado el proceso a la COLA DE PROCESOS LISTOS, el proceso pasa al estado de LISTO que es el momento en el que el proceso
es ejecutado o puesto en uso.
EJECUCION Una ves que es tomado de la COLA DE PROCESOS LISTOS, el procesos deja el estado de LISTO y pasa al estado de EJECUCION,
que es el momento en el que el proceso esta siendo utilizado dentro del CPU.
BLOQUEADO Durante la Ejecucion del proceso le surge la necesidad de esperar por algun suceso o evento, en ese momento pasa al estado de BLOQUEADO,
en el cual este estado depende de que se produzca dicho
suceso o evento para dejar este estado. Un proceso puede moverse al estado de BLOQUEADO
con solo hacer una llamda a la funcion: sleep();.
TERMINADO Cuando el proceso es finalizado por el usuario o el SO. ( el mas facil de todos o_O xD ).
Creacion de un Proceso
Unix crea un proceso atravez de la llamada ala funcion: fork();.
Un Proceso Padre::es el proceso que a sido iniciado y tiene un ID asignado por el SO.
Un Proceso Hijo::es el proceso copia del proceso padre y tiene un ID similar al Proceso Padre.
Un Proceso Hijo:: debe ser finalizado primero y despues finalizar su Proceso Padre, de este modo la Operacion del Proceso es Correcta.
Cuando esto ocurre al reves se toman los siguientes nombre a los procesos y significativos:
Un Proceso Huerfano ::es un proceso que a sido finalizado por el proceso PADRE, y es adoptado por el Demonio del Sistema que es: init, este Demonio
finalizara dicho proceso. init es identificado como Proceso Padre con ID=1.
Un Proceso Zombi ::es un proceso hijo que se ha quedado parado o bloqueado en la tabla de PROCESOS y espera la finalizacion de su proceso PADRE.
NOTA: es importante resaltar que un proceso hijo reside dentro de un Proceso Padre o Principal, y debemos tomar en cuenta que un proceso hijo
pude crear Otros Procesos Hijos, dentro del mismo Proceso.
La funcion fork(); realiza una llamada al SO donde copia la imagen en memoria que tiene el Proceso Padre. El nuevo proceso recive una COPIA
del espacio de direccion en memoria que tenia el Proceso Padre. Los procesos continuan su ejecucion en la instruccion que esta despues del fork();.
Debe quedarnos claro que desde el momento en que EJECUTAMOS nuestro programa ( el que sea ) automaticamente se crea un proceso para nuestro
programa y un ID de dicho proceso, por lo cual al momento de llamar a " getpid() " ANTES de la llamada a " fork() " podemos ver el ID de nuestro
proceso PADRE ( osea el proceso que corresponde a nuestro programa principal ).
El fork(); es de tipo: pid_t.
fork(); -----------> pid_t fork( void );
fork();::devuelve un 0 al proceso hijo y un valor diferente de 0 al proceso padre.
Codigo: ps_002.c
Programa que crea un proceso Hijo e imprime el ID del proceso Padre e Hijo creado con fork();
#include
#include
#include
#include
int main()
{
system( "clear" );
fprintf( stderr, "Inica Programa con Proceso Padre PID: %ld", (long)getpid() );
if( fork() == 0 )
{
fprintf( stderr, "\nHe Creado el Proceso Hijo PID: %ld, Mi Padre PID: %ls", (long)getpid(), (long)getppid() );
sleep(1);
}
else
{
fprintf( stderr, "\nContinua el Proceso Padre: %ld", (long)getpid() );
sleep(1);
}
fprintf( stderr, "\n\nTermino el Proceso %ld", (long)getpid() );
exit(0);
}
*---------------------------------------------------------------*
Explicacion
Al iniciar nuestro programa ( ejecutarlo ), automaticamente se crea un proceso para la instancia de nuestro programa dentro de la PC, con esto
automaticamente obtenemos un ID de Proceso ( esto sucede con cualquier programa que hemos ejecutado ), ahora como se muestra en le codigo, se
imprime Inicia el Programa con Proceso Padre PID : 7737, donde 7737 es el ID que tiene nuestro programa y lo consideramos como el PROCESO PADRE.
Ahora biene la llamada a fork(), la cual vuelve a crear otro proceso pero con un ID diferente, NOTESE que al crearse otro proceso imaginemos
que volvemos a crear otro EJECUTABLE :D. Ahora como solo se creo el Nuevo Proceso, el programa pasa a la parte de ELSE e imprime "Continua
el Proceso Padre PID 7737". Ahora nuestro proceso Hijo llega a la parte de fork() y ve que NO puede continuar a crear otro proceso, ya que
los procesos HIJOS tienen 0 y los Procesos Padre tiene un numero diferente de 1.
Por lo tanto entra el IF e imprime " He Creado el Proceso Hijo PID 7739 y Mi Padre PID 7737", ahora nuestro Proceso Padre ( programa principal )
llega a la antepenultima linea que es: "Termino el Proceso 7737", y finaliza, despues llega a la misma posicion el Proceso Hijo ( por decirlo
asi, el proceso IMAGEN o VIRTUAL ) e imprime: "Termino el Proceso 7739" y finaliza.
Algunas veces puede Iniciar primero el Proceso Padre y despues el Proceso Hijo, o al reves, pero siempre finaliza primero el proceso HIJO y
despues el Proceso Padre :D.
De la misma manera al iniciar el programa como dije anteriormente, se crea el proceso y obtenemos un ID para nuestro programa
en ejecucion, despues llega a la parte del if( fork() == 0 ) y se crea un proceso nuevo, en este caso lo podemos nombrar como: "Proceso Hijo",
de esta forma imaginemos que creamos un nuevo PROGRAMA ( osea ejecutamos en otra ventana ), y tenemos 2 procesos funcionado, el Proceso Padre
( nuestro programa principal ) y el Proceso Hijo ( el nuevo
proceso realizado por la llamada a fork(), de este modo conseguimos el
Trabajo en Segundo Plano :D. Ya que interactucamos con dos procesos a la ves :D. No es complicado, simplemente imaginemos que tenemos 2
programas, y en nuestro Proceso Padre ( programa principal ) ejecuta el fork() y crea un Proceso Nuevo (osea el Hijo) por lo tanto el fork()
nos retorna un 0, ya que creo un nuevo proceso y logicamente entra el IF e imprime: " He Creado el Proceso Hijo PID: 7739, Mi Padre PID: 7737 ",
despues entra el sleep() y espera 1 segundo, en el transcurso de ese segundo le da tiempo suficiente al padre para continuar con su codigo,
que en este caso sera el ELSE ;), y pues imprime: " Continua el Proceso Padre: 7737 ", despues el proceso padre llamada a sleep() y espera 1 segundo,
durante ese segundo de espera, el proceso Hijo Continua su ejecucion y lo primero que ve es que, finaliza el IF y lega al exit(0) por lo tanto
termina el Proceso Hijo Primero, despues termina el segundo de espera del Proceso Padre y continua su codigo y ve que termina el ELSE y continua
el codigo y se encuentra el exit(0) y finaliza.
El sleep() nos sirve para poder visualizar de una formas mas entendible el funcionamiento del programa, ya que al iniciar el programa, lo primero
que hace es mostrarte el inicio del proceso HIJO y del proceso PADRE, espera 1 segundo y realiza la finalizacion.
Codigo: ps_003.c
Programa que Ilustra un PROCESO HUERFANO, y vemos que es tomado por INIT, ver IDs :D |
#include
#include
#include
#include
int main()
{
system( "clear" );
fprintf( stderr, "Inicia el Programa con Proceso Padre: %ld", (long)getpid() );
if( fork() == 0 )
{
fprintf( stderr, "\nInicia el Hijo con PID: %ld y Padre PID: %ld", (long)getpid(), (long)getppid() );
sleep(1);
fprintf( stderr, "\nNuevamente el Hijo PID: %ld, y Padre PID: %ld", (long)getpid(), (long)getppid() );
}
else
{
fprintf( stderr, "\nContinua el Padre PID: %ld", (long)getpid() );
fprintf( stderr, "\nAhora Finaizara el Padre con PID: %ld", (long)getpid() );
exit(0);
}
exit(0);
}
Explicacion
Bueno primero imprime el ID del proceso Padre, despues llama al " fork() " y crea al HIJO, continua con IF e imprime "Inicia el Hijo con PID: 8766
y Padre PID: 8764", despues entra el sleep(), espera un segundo. En seguida entra el ELSE, e imprime "Continua el Padre PID: 8764" y finaliza
el Proceso Padre ( nuestro programa principal ), ahora regresa el Proceso Hijo y por lo tanto el Proceso Hijo es tomado por init, y e imprime:
" Nuevamente el Hijo PID: 8766 y Padr PID: 1" fuera de nuestro programa y dentro de la Shell :D.
Notese que una la cadena: " Nuevamente el Hijo PID: 8766 y Padre PID: 1 " es mostrado despues de la finalizacion del programa, dentro de
la consola Unix :D. Ya que el procedimiento ese lo realiza init :D. ;)
Codigo: ps_004.c
Programa que crea una lista de Procesos
#include
#include
#include
#include
int main()
{
int i;
system( "clear" );
for( i=0; i<4; i++ )
{
if( fork() )
break;
fprintf( stderr, "\nProceso %i con PID: %ld\tPadre PID: %ld", i, (long)getpid(), (long)getppid() );
}
fprintf( stderr, "\n\nFinalizando Proceso PID: %ld", (long)getpid() );
sleep(1);
exit(0);
}
Explicacion
Este programa crea una lista de procesos, en el que conforme se va creando procesos el proceso actual pasa a ser un Hijo y al crear otro proceso,
el proceso anterior pasa a ser Padre del Proceso Actualmente creado.
Prosiguiendo con el codigo, el programa infoca al fork() siempre y cuando sea un proceso padre... if( fork() ), de este modo creamos procesos
padres en el que un procoso esta enlazado con otro, recordemos nuevamente que un proceso padre es independiente del un proceso anterior, pero se
enlazan los procesos ya que corresponden a un mismo programa, en el cual solo como diferenciador se le asigna un numero de identificacion ( ID )
de un valor+1 del proceso anterior. ( Ejm: 5393, 5394m etc.., etc... ). De esta forma creamos una LISTA de procesos xD. La finalizacion se realiza
desde el Ultimo Proceso Creado hasta el Primer Proceso Que es La Instancia o Proceso de Nuestro Programa en Ejecucion :D.
Codigo: ps_005.c
Programa que Crea Varios Procesos Hijos dentro de un mismo Proceso
#include
#include
#include
#include
int main()
{
int i;
system( "clear" );
for( i=0; i<5; i++ )
{
if( fork() == 0 )
{
fprintf( stderr, "\nProceso %i con PID: %ld\tPadre PID: %ld", (long)getpid(), (long)getppid() );
break;
}
}
fprintf( stderr, "\n\nFinalizado Proceso con PID: %ld", (long)getpid() );
sleep(1);
exit(0);
}
Explicacion
Este Programa crea Hijos dentro de un mismo proceso. Debemos tomar en cuenta que para la creacion de Procesos Hijos, lo diferenciamos cuando
fork() devuelve 0, esto indica la creacion de un proceso hijo :D.
Siguiendo el codigo encontramos if( fork() == 0 ) siempre y cuando se cree un proceso Hijo entrara el IF e imprimira el PID del Proceso Hijo,
despues Frena el FOR e imprime Finalizado el Proceso con PID:, espera 1 segundo, tiempo justo para que el programa pueda crear otro proceso Hijos
y terminarlo conforme se pidan :D. Al final solo finaliza nuestra Istancia/Proceso Padre Principal xD.
La Llamada wait al Sistema
La llamada al sistema "wait" detiene al proceso hasta que algun proceso hijo termine
o se detenga, se debe utilizar una llamada a una de las siguientes funciones.
Codigo: ps_006.c
#include
#include
pid_t wait( int *estado );
pid_t waitpid( pid_t pid, int *estado, int opcion );
estado ::es un puntero a un valor entero.
Caracteristicas de wait:
� Wait regresa de inmediato si el proceso no contiene procesos hijos o si el hijo termina o se detiene.
� Wait regresa debido a la terminacion de un hijo.
Valores de Retorno de wait:
� Devuelve un valor positivo el cual corresponde al ID del proceso hijo. <-- cuando un hijo termina bien
� Devuelve -1 y setea " errno " con un valor en especial. <-- cuando no se finalizo mal o no existen hijos.
Valores de errno:
� ECHILD indica no existen procesos hijos a los cuales esperar
� EINTR indica que la llamada/espera fue interrumpida por un senal.
Para analizar de una mejor manera el estado de un proceso hijo correspondiente al valor devuelvo al " estado " se utiliza POSIX, en el cual especifica los siguiente macros:
� WIFEXITED( int estado )
� WEXITSTATUS( int estado )
� WIFSIGNALED( int estado )
� WTERMSIG( int estado )
� WIFSTOPPED( int estado )
� WSTOPSIG( int estado )