No es un bug, es una característica no documentada

martes, 29 de septiembre de 2015

Servicios y procesos. Procesos y C (I)

¿Qué es un proceso?

Proceso = Programa en ejecución

Cada una de las CPU se tienen que compartir a ratos con todos los procesos cargados en la memoria RAM.

Cuando un programa usa la CPU y sale, hay que hacer una especie de instantánea para guardar el contador de procesos pendientes, donde se ha quedado, etc.

Esto se conoce como el BCP (bloque de control de procesos). Es una tabla del sistema operativo que guarda la información de la situación de como se había quedado el programa en el momento de finalizar su tiempo en la CPU y dejar espacio a otro proceso.

Estados del proceso

Un proceso puede estar en la CPU, es decir, estará en ejecución.
Puede ser que esté cargado en la RAM, preparado para que el SO le de paso a la CPU. Estará listo.

O puede ser que esté esperando a utilizar los recursos que otro proceso ha cogido y hasta que no finalice el sistema operativo no le va a dejar continuar. Entonces estará bloqueado.


La contienda es como se llama a la pugna que hacen los procesos listos para conquistar la CPU.

Hay distintos modos de realizar la asignación de la contienda. Entre otros:
è Round-Robin. Método circular
è Por prioridades. Es decir, el antivirus tendrá más prioridad que la calculadora, por ejemplo. O el Word al estar tecleando conseguirá más prioridad de la ventana activa para no dar sensación de lentitud o ir a saltitos… Pero esto tiene varios problemas, como la inacción. Es decir, si mientras P1 (proceso 1) está en ejecución por ganar a la P2 pero entra P3 que tiene mayor prioridad, saldrá P1 y ganará la contienda P3.
è Por tiempos. Mayor prioridad el que menor tiempo estimado tiene para terminar.

Eso sí, al programar no podremos controlar los métodos de ordenación de la contienda. No podemos garantizar que los procesos vayan a coger la CPU en el momento que vayamos a querer.

Procesos en los SSOO

Para ver los procesos en Windows, iremos al Administrador de Tareas à Procesos


Para verlos desde la línea de comandos, en CMD escribiremos tasklist. Además al hacerlo por línea de comandos veremos el PID, el identificador del proceso.


En Ubuntu podemos verlo en la terminal con el comando ps.


Si hacemos ps –f también aparecerá el PPID, el identificador del proceso padre.


Con ps –AF aparecen todos los procesos lanzados en el sistema.

PID y procesos en C

Vamos a realizar las primeras prácticas con los procesos, los PID y los PPID en el lenguaje C. Para ello hemos instalado un entorno VitaLinux que trae ya incorporado el compilador gcc, pero podéis utilizar cualquier herramienta de vuestra elección que os permita trabajar programando en C.

EXECL
Execl sirve para ejecutar comandos del sistema y cualquier programa. Los argumentos son (const char *fichero, cons char *arg0, …, char *argN, (char*)NULL).
Devolverá -1 si hay condición de error.

Veamos un código de ejemplo
#include <stdio.h>
#include<unistd.h>

void main(){
     printf(“Los archivos del directorio son: \n”);
     execl(“/bin/ls”, “ls”, “-l”, (char *)NULL);
     printf(“ERROR!!!”);
}
Aquí estaremos haciendo un ls –l para ver todos los archivos del directorio desde donde ejecutemos este programa.

System

A diferencia de execl, system ejecutará sólo comandos del sistema, como podemos ver abajo. Vamos a ver el siguiente programa para hacernos una idea.
#include <stdio.h>
#include <stdlib.h>

void main(){
     system(“ls –l > ficSalida”);
     printf(“FIN”);
}
Lo que hace el código es guardar el resultado de un ls –l de la carpeta actual del PATH a un fichero salida.

PID

En la arquitectura cliente – servidor tenemos dentro de un servidor web un proceso que está escuchando, en listen. Le hacen una petición y este proceso crea un hijo que sirva la página web al cliente. Una vez finalizada la tarea, el hijo sale de la memoria al finalizar su tarea. Durante todo la ejecución, el proceso padre se ha mantenido activo permaneciendo a la escucha de otras peticiones.

Vamos a ver un programa que nos muestre los identificadores del proceso actual y de su padre.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void main(){
     pid_t id_pactual, id_padre;
     
     id_pactual = getpid();
     id_padre = getppid();

     printf(“PID actual: %d \n”, id_pactual);
     printf(“PID padre: %d \n”, id_padre);
}
Analicemos este código.

  • La línea de declaración pid_t nos indica el tipo de variable que será, una que nos sirva para almacenar el número de proceso que tenemos.
  • La función getpid() devuelve el id del proceso actual
  • Y la función getppid() devuelve el id del padre del proceso actual.
Ahora ya sabemos ver los id de los procesos, pero… ¿cómo se crean los procesos hijos?
Para eso tenemos la instrucción fork(), que es la encargada de crear un proceso hijo. Es decir, una copia exacta del proceso padre, pero a partir de ahora independientes.
Estos dos procesos, junto con todos los del sistema, entrarán en la contienda por la puja de la CPU independientemente. Y por tanto el control de tiempo también será independiente.

Para poder diferenciar uno de otro lo conseguimos con el valor devuelto por fork(). Con un ejemplo se verá más claro.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void main(){

     pid_t pid, hijo_pid;

     pid = fork(); // Aquí crea el proceso hijo
     
     if (pid == -1){
          printf(“Ha habido un error”);
          exit(-1);
     }
     if(pid == 0){
          // Nos encontramos dentro del proceso hijo
          printf("soy el proceso hijo \n\t Mi PID es %d. El PID de mi padre es: %d. \n", getpid(), getppid());
     }
     else{
          hijo_pid = wait(NULL);
          printf("Soy el proceso padre: \n\t Mi PID es %d. El PID de mi padre es: %d. \n\t Mi hijo %d terminó. \n", getpid(), getppid(), pid);
     }
}
Hay varios puntos que comentar. El primero es que como ya hemos dicho, los procesos pese a estar en un mismo programa, tanto el hijo como el padre son independientes en el momento de ejecutar fork().
Si nos devuelve un valor igual a 0, sabremos que estamos tratando con el hijo, mientras que cualquier otro valor que no indique una condición de error nos hará saber que está funcionando el padre.

Dentro del padre tenemos la línea
hijo_pid = wait(NULL);

Con ésta línea indicaremos que va a esperar a la finalización del proceso hijo, y la variable pid guardará el PID del padre.

¿Ha quedado claro? Atrevámonos con un ejercicio sencillico.

Ejercicio. Coged un proceso, cread una variable y guardad un valor. 7, por ejemplo. El proceso crea un hijo que le suma 5, y el padre le resta 5. Visualiza por pantalla el resultado de ambas operaciones así como el PID y el PPID de ambos procesos.

Solución.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void main(){
     pid_t pid, hijo;
     int n = 7;

     pid = fork();

     if (pid == -1){
          printf(“Error \n”);
          exit(-1);
     }
     if (pid == 0){
          n = n + 5;
          printf("Soy el hijo. Valor de n = %d.\n Proceso %d, padre %d \n\n", n, getpid(), getppid());
     }
     else{
          n = n – 5;
          printf("Soy el padre. Valor de n = %i.\n Proceso %d, padre %d \n\n", n, getpid(), getppid());
     }

}

0 comentarios:

Publicar un comentario