Вы находитесь на странице: 1из 13

U.N.S.

A Sistemas Operativos (Laboratorio) Página: 13

Programación de
Procesos en C para
Linux

I
OBJETIVOS
 Conocer y aplicar los conceptos sobre Procesos en LINUX
 Creación y ejecución de Procesos
 Finalización de procesos
 Señales
 Compilación y ejecución de programas en LINUX
 Realizar ejercicios.
II
TEMAS
 El árbol de procesos
 El proceso init
 ps, pstree
 Identificadores (PID, PPID, UID, EUID, GID y EGID)
 Cómo crear un proceso
 La llamadas al sistema fork()
 La llamadas al sistema exec()
 Obtención de Identificadores (getpid(), getppid())
 exit,wait, waitpid
 Señales
 GCC (Compilación)
 ./ (Ejecución)
III

MARCO TEORICO
El árbol de procesos
Al igual que con los archivos, todos los procesos que corren en un sistema Linux
están organizados en forma de árbol, y cada proceso tiene un número (su PID,
Process ID, «identificador de procesos»), junto con el número de su proceso padre
(PPID, Parent Process ID, «identificador del proceso padre»).

Ing. Eveling Gloria Castro Gutierrez

Sesion 04: Programación de Procesos en C para Linux


U.N.S.A Sistemas Operativos (Laboratorio) Página: 14

Esto significa que hay un proceso en el tope de la estructura de árbol, el equivalente


de la raíz para los sistemas de archivos: INIT, que siempre posee PID =1.

El proceso init
Cuando arranca el sistema se desencadena una secuencia de procesos que a
grandes rasgos es la siguiente. Primero se carga el núcleo de Linux (Kernel) de una
forma totalmente especial y distinta a otros procesos. Dependiendo de los sistemas
puede existir un proceso con PID=0 planificador, o swapper. Lee un fichero llamado
inittab donde se relacionan una serie de procesos que deben arrancarse para
permanecer activos (demonios). Algunos de ellos están definidos para que en caso
de morir sean arrancados de nuevo inmediatamente garantizando la existencia de
ese servicio de forma permanente.
Es decir 'init' es un proceso que va a generar nuevos procesos pero esta no es una
cualidad especial. Es muy frecuente que un proceso cualquiera genere nuevos
procesos y cuando esto ocurre se dice que genera procesos hijos.

ps y pstree
Estos dos comandos muestran una lista de los procesos presentes en el sistema de
acuerdo con los criterios que Ud., desee:

ps

Al enviar este comando sin un argumento se mostrarán solo los procesos iniciados
por Ud., en la terminal que está utilizando:

$ ps
PID TTY TIME CMD
5162 ttya1 00:00:00 zsh
7452 ttya1 00:00:00 ps

Las opciones son numerosas, citaremos las más comunes:


a: también muestra los procesos iniciados por los otros usuarios;
x: también muestra los procesos sin terminal de control alguna (esto se aplica a casi
todos los servidores);
u: muestra, para cada proceso, el nombre del usuario que lo inició y la hora a la cual
fue iniciado.
Hay muchas otras opciones. Refiérase a la página de manual para más información
(man ps).

La salida de este comando está dividida en campos diferentes: el que más le


interesará es el campo PID, que contiene el identificador del proceso. El campo CMD
contiene el nombre del comando ejecutado.

Una forma muy común de invocar a ps es la siguiente:


$ ps ax | less

Esto le da una lista de todos los procesos que se están ejecutando corrientemente,
entonces Ud., puede identificar uno o más procesos que estén causando problemas
y, subsecuentemente, puede matarlos.

pstree

Ing. Eveling Gloria Castro Gutierrez

Sesion 04: Programación de Procesos en C para Linux


U.N.S.A Sistemas Operativos (Laboratorio) Página: 15

El comando pstree muestra los procesos en forma de estructura de árbol. Una


ventaja es que Ud., puede ver inmediatamente quien es el proceso padre de otro:
cuando quiere matar toda una serie de procesos, y si son todos padres e hijos, es
suficiente matar al ancestro común. Se puede usar la opción -p, que muestra el PID
de cada proceso, y la opción -u que muestra el nombre del usuario que inició el
proceso. Como generalmente la estructura de árbol es grande, es más fácil invocar a
pstree de la siguiente manera:

$ pstree -up | less

Esto le da una visión general de toda la estructura de árbol de los procesos.


Envío de señales a los procesos: kill, killall y top

kill, killall

Estos dos comandos se usan para enviar señales a los procesos. El comando kill
necesita el número de un proceso como argumento, mientras que el comando killall
necesita el nombre de un comando.

Los dos comandos opcionalmente pueden recibir el número de una señal como
argumento. Predeterminadamente, ambos envían la señal 15 (TERM) a el o los
procesos relevantes. Por ejemplo, si quiere matar el proceso con PID 785, Ud.,
ingresa el comando:

$ kill 785

Si quiere enviarle la señal 9, ud., ahora ingresa:

$ kill -9 785

Supongamos que quiere matar un proceso del cual ud. conoce el nombre del
comando. En vez de encontrar el número de proceso usando ps, puede matar el
proceso directamente:

$ killall -9 netscape

Pase lo que pase, Ud., solo matará sus propios procesos (a menos que Ud. sea root),
por lo que no debe preocuparse acerca de los procesos de otro usuario, que tienen el
mismo nombre, ellos no serán afectados.

Identificadores PID, PPID, UID, EUID, GID y EGID


Process ID (PID)
Al crear un nuevo proceso se le asigna un identificador de proceso único. Este
número debe utilizarse por el administrador para referirse a un proceso dado al
ejecutar un comando.

Los PID son asignados por el sistema a cada nuevo proceso en orden creciente
comenzando desde cero. Si antes de un reboot del sistema se llega al nro. máximo,
se vuelve a comenzar desde cero, salteando los procesos que aún estén activos.

Parent Process ID (PPID)


La creación de nuevos procesos en Unix se realiza por la vía de duplicar un proceso
existente invocando al comando fork(). Al proceso original se le llama "padre" y al
nuevo proceso "hijo". El PPID de un proceso es el PID de su proceso padre.
Ing. Eveling Gloria Castro Gutierrez

Sesion 04: Programación de Procesos en C para Linux


U.N.S.A Sistemas Operativos (Laboratorio) Página: 16

UID y EUID
Los procesos tienen un EUID (Effective User ID), y un UID normalmente ambos
coinciden. El UID es el identificador de usuario real que coincide con el identificador
del usuario que arrancó el proceso. El EUID es el identificador de usuario efectivo y
se llama así porque es el identificador que se tiene en cuenta a la hora de considerar
los permisos.

El UID es uno de los atributos de un proceso que indica por decirlo de alguna
manera quien es el propietario actual de ese proceso y en función de ello podrá
hacer ciertas cosas.

El UID también es un atributo presente en otros elementos del sistema. Por ejemplo
los ficheros y directorios del sistema tienen este atributo. De esta forma cuando un
proceso intenta efectuar una operación sobre un fichero. El kernel comprobará si el
EUID del proceso coincide con el UID del fichero. Por ejemplo si se establece que
determinado fichero solo puede ser leido por su propietario, el kernel denegará todo
intento de lectura a un proceso que no tenga un EUID igual al UID del fichero salvo
que se trate del root

La forma más habitual de hacer que el EUID de un proceso sea el de un usuario


diferente del que lanza a correr el programa es activando el flag setuid en el archivo
del programa. Un ejemplo de esto son los comandos que permiten a un usuario
modificar su password, en que se debe modificar el archivo passwd o equivalente del
sistema sobre el cual el usuario obviamente no tiene permiso de escritura.
Habitualmente ese comando es un archivo de root con setuid y el proceso corre con
EUID de root.

GID y EGID
Es totalmente análogo a los identificadores de usuario pero para grupos de usuarios.
El GID se hereda del proceso padre. El EGID puede utilizarse igual que el EUID para
controlar el acceso del proceso a archivos.

En la mayoría de los versiones actuales de Unix el proceso puede estar en varios


grupos y se chequea contra toda la lista de grupos para definir si el proceso puede
acceder o no a un recurso.

Como se crea un nuevo proceso


El núcleo del sistema llamado también kernel es el encargado de realizar la mayoría
de funciones básicas que gestiona entre otras cosas los procesos. La gestión de los
procesos se realizan con las llamadas al sistema estas están implementadas en
lenguaje C y hablaremos ahora un poco sobre un par de ellas llamadas fork() y
exec().

La llamada al sistema fork()

La forma en que un proceso arranca a otro es mediante una llamada al sistema con
la función fork(). Lo normal es que el proceso hijo ejecute luego una llamada al
sistema exec(). fork() duplica un proceso generando dos procesos casi idénticos. En
realidad solo se diferenciaran en los valores PID y PPID. Un proceso puede pasar al
proceso hijo una serie de variables pero un hijo no puede pasar nada a su padre a
través de variables. Además fork() retorna un valor numérico que será -1 en caso de
fallo, pero si tiene éxito se habrá producido la duplicación de procesos y retornará un
valor distinto para el proceso hijo que para el proceso padre. Al proceso hijo le
Ing. Eveling Gloria Castro Gutierrez

Sesion 04: Programación de Procesos en C para Linux


U.N.S.A Sistemas Operativos (Laboratorio) Página: 17

retornará el valor 0 y al proceso padre le retornará el PID del proceso hijo. Después
de hacer fork() se pueden hacer varias cosas pero lo primero que se utiliza después
del fork es una pregunta sobre el valor retornado por fork() para así saber si ese
proceso es el padre o el hijo ya que cada uno de ellos normalmente deberá hacer
cosas distintas.
int PID = fork() // crea un nuevo proceso (hijo del que hace la llamada a fork). padre e hijo
continúan la ejecución en la instrucción inmediatamente posterior al fork.

PID = 0 en el hijo,
PID > 0 en el padre (y contiene el pid del hijo)
PID < 0 si error
La llamada al sistema exec()

Un proceso puede evolucionar y cambiar totalmente desde que arranca hasta que
muere. Lo único que no cambia es su identificador de proceso PID. Una de las cosas
que puede hacer un proceso es cambiar por completo su código. Por ejemplo un
proceso encargado de procesar la entrada y salida de un términal (getty) puede
transformarse en un proceso de autentificacion de usuario y password (login) y este
a su vez puede transformarse en un interprete de comandos (bash). Si la llamada
exec() falla retornará un –1, cuando esta llamada tiene éxito no se produce jamás
un retorno. En realidad no tendría sentido retornar a ningún lado. Cuando un
proceso termina simplemente deja de existir. La llamada exec() mantiene el mismo
PID y PPID pero transforma totalmente el código de un proceso en otro que cargará
desde un archivo ejecutable.
EXEC. se ejecuta en el espacio del proceso llamador, posibilidades:

int execl(char *path, char *arg0, char *arg1,......, char *argn, null);
int execv(char *path, char *argv[]);
int execlp(char *file, char *arg0, char *arg1,......, char *argn, null);
int execvp(char *file, char *argv[]);
El sistema operativo UNIX ofrece una llamada al sistema llamada 'exec' para lanzar a
ejecución un programa, almacenado en forma de fichero. Aunque en el fondo sólo
existe una llamada, las bibliotecas estándares del C disponen de varias funciones,
todas comenzando por 'exec' que se diferencian en la manera en que se pasan
parámetros al programa.

La versión típica cuando se conoce a priori el número de argumentos que se van a


entregar al programa se denomina execl. Su sintaxis es
int execl ( char* fichero, char* arg0, char* arg1, ... , 0 );

Es decir, el nombre del fichero y luego todos los argumentos consecutivamente,


terminando con un puntero nulo (vale con un cero). Sirva este ejemplo:
Para ejecutar

/bin/ls -l /usr/include

escribiríamos

execl ( "/bin/ls", "ls", "-l", "/usr/include", 0 );

Obsérvese que el primer argumento coincide con el nombre del programa.

En caso de desconocer con anticipación el número de argumentos, habrá que


emplear la función execv, que tiene este prototipo:

execv ( char* fichero, char* argv [] );


Ing. Eveling Gloria Castro Gutierrez

Sesion 04: Programación de Procesos en C para Linux


U.N.S.A Sistemas Operativos (Laboratorio) Página: 18

El parámetro argv es una tira de cadenas que representan los argumentos del
programa lanzado, siendo la última cadena un nulo (un cero). El ejemplo anterior se
resolvería así:

char* parametros [] = { "ls", "-l", "/usr/include", 0 }; ...

execv ( "/bin/ls", parametros );

En los anteriores ejemplos se ha escrito el nombre completo del fichero para


ejecutar ("/bin/ls" en vez de "ls"). Esto es porque tanto execl como execv ignoran
la variable PATH, que contiene las rutas de búsqueda. Para tener en cuenta esta
variable pueden usarse las versiones execlp o execvp. Por ejemplo:

execvp ( "ls", parametros );

ejecutaría el programa "/bin/ls", si es que la ruta "/bin" está definida en la variable


PATH.

Todas las llamadas exec... retornan un valor no nulo si el programa no se ha podido


ejecutar. En caso de que sí se pueda ejecutar el programa, se transfiere el control a
éste y la llamada exec... nunca retorna. En cierta forma el programa que invoca un
exec desaparece del mapa.

Obtención de Identificadores (getpid(), getppid())


getpid(void) Devuelven el ID del proceso
getppid(void) devuelve el ID del padre (del proceso), en general, se ejecutan en
silencio sin necesidad de interacción.

Finalización de Procesos
exit
void exit (int status)
exit finaliza al proceso que lo llamó, con un código de estado igual al byte inferior
del parámetro status. Todos los descriptores de archivo abiertos son cerrados y sus
buffers sincronizados. Si hay procesos hijo cuando el padre ejecuta un exit, el PPID
de los hijos se cambia a 1 (proceso init). Es la única llamada al sistema que nunca
retorna.
El valor del parámetro status se utiliza para comunicar al proceso padre la forma en
que el proceso hijo termina. Por convenio, este valor suele ser 0 si el proceso
termina correctamente y cualquier otro valor en caso de terminación anormal. El
proceso padre puede obtener este valor a través de la llamada al sistema wait.

wait y waitpid:
Espera a que pare o termine un proceso hijo, permitiendo obtener sus estados de
salida. Una señal no bloqueada o no ignorada puede reactivar el proceso padre.

pid_t wait (int *statusp)


Si hay varios procesos hijos, wait espera hasta que uno de ellos termina. No es
posible especificar por qué hijo se espera. wait retorna el PID del hijo que termina
(o -1 si no se crearon hijos o si ya no hay hijos por los que esperar) y almacena el
Ing. Eveling Gloria Castro Gutierrez

Sesion 04: Programación de Procesos en C para Linux


U.N.S.A Sistemas Operativos (Laboratorio) Página: 19

código del estado de finalización del proceso hijo (parámetro status en su llamada
al sistema exit) en la dirección apuntada por el parámetro statusp.
Un proceso puede terminar en un momento en el que su padre no le esté esperando.
Como el kernel debe asegurar que el padre pueda esperar por cada proceso, los
procesos hijos por los que el padre no espera se convierten en procesos zombies (se
descartan su segmentos, pero siguen ocupando una entrada en la tabla de procesos
del kernel). Cuando el padre realiza una llamada wait, el proceso hijo es eliminado
de la tabla de procesos.
No es obligatorio que todo proceso padre espere a sus hijos.
Un proceso puede terminar por:
Causa de terminación Contenido de *statusp
byte más a la derecha = 0
Llamada al sistema exit
byte de la izquierda = parámetro status de exit
En los 7 bits más a la derecha se almacena el
número de señal que terminó con el proceso. Si el
Recibe una señal 8º bit más a la derecha está a 1, el proceso fue
detenido por el kernel y se generó un volcado del
proceso en un archivo core.
Caída del sistema
Todos los procesos desaparecen bruscamente. No
(Ej.: pérdida de la alimentación del
hay nada que devolver.
equipo)

Proceso zombi:
proceso parado que queda en la tabla de procesos hasta que termine su padre. Este
hecho se produce cuando el proceso padre no recoge el código de salida del proceso
hijo.
Proceso huérfano:
proceso en ejecución cuyo padre ha finalizado. El nuevo identificador de proceso
padre (PPID) coincide con el identificador del proceso init (1).

SEÑALES.
Una señal es evento que debe ser procesado y que puede interrumpir el flujo normal
de un programa.
Capturar una señal:
Una señal puede asociarse con una función que procesa el evento que ha ocurrido.
Ignorar una señal:
El evento no interrumpe el flujo del programa. Las señales SIGINT y SIGSTOP no
pueden ser ignoradas (ver tabla de señales).
Acción por defecto:
Proceso suministrado por el sistema para capturar la señal (ver tabla de señales).
Alarma:
Señal que es activada por los temporizadores del sistema.
Error:
Fallo o acción equivocada que puede provocar la terminación del proceso.
Error crítico:
Error que provoca la salida inmediata del programa.

Tabla de señales más importantes.


Núm. Nombre Comentarios
1 SIGHUP Colgar. Generada al desconectar el terminar.
2 SIGINT Interrupción. Generada por teclado.
3 SIGQUIT1 Salir. Generada por teclado.
Ing. Eveling Gloria Castro Gutierrez

Sesion 04: Programación de Procesos en C para Linux


U.N.S.A Sistemas Operativos (Laboratorio) Página: 20

4 SIGILL1 Instrucción ilegal. No se puede recapturar.


5 SIGTRAP1 Trazado. No se puede recapturar.
6 SIGABRT1 Abortar proceso.
8 SIGFPE1 Excepción aritmética, de coma flotante o división por cero.
9 SIGKILL1 Matar proceso. No puede capturarse, ni ignorarse.
10 SIGBUS1 Error en el bus.
11 SIGSEGV1 Violación de segmentación.
12 SIGSYS1 Argumento erróneo en llamada al sistema.
13 SIGPIPE Escritura en una tubería que otro proceso no lee.
14 SIGALRM Alarma de reloj.
15 SIGTERM Terminación del programa.
16 SIGURG2 Urgencia en canal de E/S.
17 SIGSTOP3 Parada de proceso. No puede capturarse, ni ignorarse.
18 SIGTSTP3 Parada interactiva. Generada por teclado.
19 SIGCONT4 Continuación. Generada por teclado.
20 SIGCHLD2 Parada o salida de proceso hijo.
21 SIGTTIN3 Un proceso en 2o plano intenta leer del terminal.
22 SIGTTOU3 Un proceso en 2o plano intenta escribir en el terminal.
23 SIGIO2 Operación de E/S posible o completada.
24 SIGXCPU Tiempo de UCP excedido.
25 SIGXFSZ Excedido el límite de tamaño de fichero.
30 SIGUSR1 Definida por el usuario número 1.
31 SIGUSR2 Definida por el usuario número 2.
34 SIGVTALRM Alarma de tiempo virtual.
36 SIGPRE Excepción programada. Definida por el usuario.

Notas sobre la acción por defecto para la señal.


1. Generar un fichero core.

2. Ignorar la señal.

3. Parar el proceso que recibe la señal.

4. Reiniciar o continuar el proceso que recibe la señal.


Las señales comprendidas entre la 37 y la 58 (ambas inclusive) están reservadas por
el sistema.
El rango de señales en el UNIX de Berkeley (BSD) es de 1 a 31.

Capturar señales.

Subrutina signal: Asocia una acción determinada con una señal.


- Formato:
#include <signal.h>
void (*signal (señal, accion)) ( )
int señal;
void (*accion) ( );
- Parámetros:
señal: Número de señal, excepto SIGKILL.
acción: Puntero a la rutina asociada con la señal o uno de los valores:

Ing. Eveling Gloria Castro Gutierrez

Sesion 04: Programación de Procesos en C para Linux


U.N.S.A Sistemas Operativos (Laboratorio) Página: 21

• SIG_DFL: acción por defecto para dicha señal.

• SIG_IGN: ignorar la señal,


- Devuelve:
Valor de la acción anteriormente asociada; -1, en caso de error.

GCC
Cada vez que ud. desea compilar un programa con extensión .c (dentro de las
principales) lo puede hacer con el comando gcc por ejemplo:

gcc programas.c –o programas.exe

el primer parámetro implica el programa a compilarse y el segundo el archivo objeto


de salida el cual posteriormente se podrá ejecutar.

./
Con la siguiente instrucción ud. Podrá ejecutar el programa del cual creo su
ejecutable anteriormente ejemplo:

./ programas.exe

por ahora solo necesitamos saber esa información para empezar a crear nuestros
programas y ejecutarlos en C.

Desarrollar en Laboratorio los siguientes ejercicios


I. Realice la escritura, compilación y ejecución de los siguientes programas

1. En cualquier editor digite el siguiente programa y evalúe el resultado

// Nos dá el PPID y el PID


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

int main(void)
{
printf("Proceso hijo PID= %d\n", getpid());
printf("Proceso padre PPID= %d\n", getppid());
exit(0);
}

2. Escriba el siguiente programa y verifique de acuerdo a la siguiente tabla:

Atributo............................Tipo............................Función
ID de proceso.....................pid_t.........................getpid(void);
ID de padre de proceso.......pid_t.........................getppid(void);
ID de usuario real...............uid_tg........................getuid(void);

Ing. Eveling Gloria Castro Gutierrez

Sesion 04: Programación de Procesos en C para Linux


U.N.S.A Sistemas Operativos (Laboratorio) Página: 22

ID de usuario efectivo.........uid_t.........................geteuid(void);
ID grupo real......................gid_t.........................getgid(void);
ID de grupo efectivo...........gid_tg........................getegid(void);

// Muestra los UID y los GUID


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

int main(void)
{
printf("ID de usuario real: %d\n", getuid());
printf("ID de usuario efectivo: %d\n", geteuid());
printf("ID de grupo real: %d\n", getgid());
printf("ID de grupo efectivo: %d\n", getegid());
exit(0);
}

Ing. Eveling Gloria Castro Gutierrez

Sesion 04: Programación de Procesos en C para Linux


U.N.S.A Sistemas Operativos (Laboratorio) Página: 23

3. Ejecutar el siguiente programa:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main ()
{
pid_t rf;
int i, j;
int global = 1000;
rf = -1;
rf = fork ();
for (i=0; i<500; i++) {
global = global + 1;
printf("\t%d (%d)", rf, global);
for (j=0; j<10000; j++) {
}
}
printf ("\nFinal de ejecucion del proceso %d \n", getpid());
exit (0);
}

4. Ejecute el programa anterior en segundo plano.


5. Verifique su ejecución con la orden ps. Observe los valores PID y PPID.
Describa lo que sucede
6. Digite el siguiente programa:
#include <stdio.h>
#include <unistd.h>
int main(void) {
int pid;
printf("Hasta aquí hay un único proceso...\n");
printf("Primera llamada a fork...\n");
/* Creamos un nuevo proceso. */
pid = fork();
if (pid == 0) {
printf("HIJO1: Hola, yo soy el primer hijo...\n");
printf("HIJO1: Voy a pararme durante 60 seg. y luego terminaré...\n");
sleep(60);
}
else if (pid > 0) {
printf("PADRE: Hola, soy el padre. El pid de mi hijo es: %d\n",pid);
/* Creamos un nuevo proceso. */
pid = fork();
if (pid == 0) {
printf("HIJO2: Hola, soy el segundo hijo...\n");
printf("HIJO2: El segundo hijo va a ejecutar la orden 'ls'...\n");
execlp("ls","ls",NULL);
printf("HIJO2: Si ve este mensaje, el execlp no funcionó...\n");
}
else if (pid > 0) {
printf("PADRE: Hola otra vez. Pid de mi segundo hijo: %d\n",pid);
printf("PADRE: Voy a esperar a que terminen mis hijos...\n");
printf("PADRE: Ha terminado mi hijo %d\n",wait(NULL));
printf("PADRE: Ha terminado mi hijo %d\n",wait(NULL));
}
else
printf("Ha habido algún error al llamar por 2ª vez al fork\n");
}
Ing. Eveling Gloria Castro Gutierrez

Sesion 04: Programación de Procesos en C para Linux


U.N.S.A Sistemas Operativos (Laboratorio) Página: 24

else
printf("Ha habido algún error al llamar a fork\n");
}

7. A los programas de los incisos 3 y 6 colocarles la llamada exec() donde crea


conveniente para finalizar correctamente los procesos.

8. Escriba un programa en el cual se creen procesos y uno de ellos se encuentre


en el estado zombie.

9. Digite los siguientes códigos y explique la salida de éstos

9.1/* fork_huerf.c - Ejemplo de proceso huérfano *


#include <stdio.h>
#include <unistd.h>
main ()
{
printf ("Ejemplo de proceso huérfano.\n");
printf ("Inicio del proceso padre. PID=%d\n", getpid ());
if (fork () == 0)
{
printf ("Inicio proceso hijo. PID=%d, PPID=%d\n",
getpid (), getppid ());
sleep (1);
printf ("El proceso queda huérfano. PID=%d PPID=%d\n",
getpid (), getppid ());
}
else
printf ("Continuación del padre. PID=%d\n", getpid ());
printf ("Fin del proceso %d\n", getpid ());
exit (0);
}

9.2/* signal.c - Contar el número de CTRL-C en 15 segundos */


#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

int numcortes=0;
int enbucle=1;
void alarma ();
void cortar ();

int main ()
{
signal (SIGINT, cortar);
signal (SIGALRM, alarma);
printf ("Ejemplo de signal.\n");
printf ("Pulsa varias veces CTRL-C durante 15 segundos.\n");
alarm (15);
while (bucle);
signal (SIGINT, SIG_IGN);
printf ("Has intentado cortar %d veces.\n", numcortes);
printf ("Hasta luego Lucas.\n");
exit (0);
}

void alarma ()
Ing. Eveling Gloria Castro Gutierrez

Sesion 04: Programación de Procesos en C para Linux


U.N.S.A Sistemas Operativos (Laboratorio) Página: 25

{
signal (SIGALRM, SIG_IGN);
bucle=0; /* Salir del bucle */
printf ("¡Alarma!\n");
}

void cortar ()
{
signal (SIGINT, SIG_IGN);
printf ("Has pulsado CTRL-C\n");
numcortes++;
signal (SIGINT, cortar);
}

V
CUESTIONARIO
1. ¿Qué hace el comando pstree –p?

2. ¿Cuál es el comando que permite visualizar los procesos en ejecución?

3. Realizar ejemplos con las diferentes llamadas de exec.

4. ¿Por qué cualquier comando en linux se considera un proceso?.Explique.

5. Deduzca que procesos intervienen en la ejecución de la orden man y la


jerarquía entre ellos. Para ello, ejecute dicha orden e interrumpa su ejecución
con CONTROL + Z y compruebe los procesos existentes con la orden ps y la
opción -l

6. ¿Cómo puede comprobar, antes de finalizar su sesión, si tiene trabajos


pendientes? ¿Cómo finalizaría dichos trabajos?

Salga de su usuario con logout y si está como root apague la maquina con :
$ shutdown –h
Y si está como usuario no se olvide de digitar :
$ poweroff

No se olvide de hacerlo cada vez que finalice la sesión de Linux

Ing. Eveling Gloria Castro Gutierrez

Sesion 04: Programación de Procesos en C para Linux