miércoles, 28 de diciembre de 2016

Diseño y Administracion de SO - UNED (Usando semáforos)

Después de darle toda la tarde al tema pienso que más o menos lo entiendo (al menos lo básico)  por lo que lo voy a intentar explicar.

Lo primero, necesitamos la librería  #include <sys/sem.h>

Y además tenemos que declarar una union tipo semun

...........................
union semun
{
    int              val;
    struct semid_ds *buf;
    unsigned short  *array;
    struct seminfo  *__buf;
};
............................
 struct sembuf oper;
 union semun arg;
.............................

Ahora lo primero es obtener la clave del semáforo (cada mecanismo IPC debe estar identificado con una clave)

...........................
clave = ftok("ruta",'letra');
...........................

una vez tenemos la clave creamos el array de semáforos y obtenemos su identificador

...........................
ID = semget(clave, 1, IPC_CREAT | 0600); //en este caso un array de 1 semáforo.
...........................

A continuación inicializamos el semáforo (verde)

...........................
arg.val = 1;
int control = semctl (ID, 0, SETVAL, arg);
...........................

Ahora ya podemos trabajar con el semáforo en nuestra sección critica.

...........................
//probar(P)
oper.sem_num = 0;
oper.sem_op = -1;
oper.sem_flg = 0;
semop (ID, &oper, 1);

//seccion critica

//incrementar(V)
oper.sem_num = 0;
oper.sem_op = 1;
oper.sem_flg = 0;
semop (ID, &oper, 1);

En las operaciones oper.xxx estamos preparando el struct sembuf para luego realizar la operación de forma atómica con semop().

Culex.


viernes, 16 de diciembre de 2016

PED - Diseño y Administracion de SO - UNED (Top)

Como no hay segunda convocatoria pienso que no hay ningún problema por publicar la PED.
Aún no está evaluada por lo que puede que no sea un buen ejemplo :-).

Tiene como nota un 10.

El Objetivo de esta práctica es programar un script en bash llamado mitop.sh que muestre información sobre los procesos de forma similar a la mostrada por comando top.


Obtención de datos iniciales 

Como paso inicial se lee el contenido del directorio /proc filtrando el resultado para obtener solo los PID de todos los procesos independientemente de su estado (R, S, D, Z, T).

listadoProcesos=$(ls -d  [0-9]*)

A continuación realizamos la primera medición de tiempo inicio=`date +%s%N` y obtenemos la suma de ticks de reloj planificados (modo usuario + modo núcleo) de  los todos los procesos existentes usando un bucle for y el archivo proc/PID/stat. Esta información junto con el PID se almacena en el archivo ‘tiempo1’ 

Es necesario usar una sentencia condicional  if [ -d "$i" ]  para descartar procesos que han finalizado y que por lo tanto no existen en /proc.
Realizamos la pausa de 1 segundo y realizamos la segunda medición de tiempo fin=`date +%s%N`.  Con estas dos medidas obtendremos el tiempo real de entre las dos iteraciones.

Finalmente, volvemos a obtener la suma de ticks de reloj planificados (modo usuario  + modo núcleo) de  los todos los procesos existentes (archivo ‘tiempo2’), calculamos la diferencia y la guardamos (archivo ‘tiempoTotal’), ordenamos el resultado y nos quedamos con los 10 procesos con mayor tiempo de CPU (archivo ‘tiemporTotalOrdenado’).

paste $HOME/tiempo1 $HOME/tiempo2 | awk '{print $1, ($4 - $2)}' >> $HOME/tiempoTotal
sort -k2 -nr $HOME/tiempoTotal | head -10 >> $HOME/tiempoTotalOrdenado

Detalle de la cabecera

La información a  mostrar en la cabecera la obtenemos de la variable ‘listadoProcesos’, del archivo /proc/meninfo, del archivo temporal ‘CPU_PID’ y del comando ‘free’

numeroProcesos=$(echo "$listadoProcesos"| wc -w)
memoriaTotal=$(awk '/MemTotal/ {print $2}' < /proc/meminfo)
memoriaLibre=$(awk '/MemFree/ {print $2}' < /proc/meminfo)
memoriaUsada=$(free | awk 'NR==2{print $3}')
usoCPU=`awk '{sum+=$2};END{print sum}' $HOME/CPU_PID`

Detalle por Proceso

Esta parte es la más complicada ya que tenemos que realizar algunos cálculos para obtener los %CPU y %MEM. El resto de información mostrada se obtiene de nuevo del archivo proc/PID/stat (PR,VIRT, S, COMMAND, MEM por PID) y con el comando ‘ps’ (USER, TIME).

Se han utilizado una serie de bucles while cuya condición de salida es la finalización de las líneas de un archivo temporal que se le pasa como parámetro de entrada. Estos bucles generan, mediante la llamada a una función, mediante la lectura de proc/PID/stat o del comando ‘ps’ un archivo temporal de salida con la información requerida. 

Esta es la estructura genérica del bucle usado.

while read linea
do
read VARIABLE <<< $(echo -e "$linea"| awk '{print $COLUMNA;}')
//operaciones de lectura o calculos
printf "%s\t%s\n" $VARIABLE $VARIABLE CALCULADA >> $HOME/archivo salida
done < $HOME/archivo entrada;


A continuación explico en detalle el procedimiento seguido para obtener %CPU y %MEM.

%CPU, Porcentaje de uso del procesador en el intervalo de tiempo elegido.

Para realizar este cálculo, necesitamos saber cuál es el tiempo durante el cual cada proceso ha estado planificado en la CPU (utime+stime) durante el intervalo de tiempo elegido ‘tiempoTotal’. Esta información ya la tenemos ‘tiempoTotalOrdenado’ pero en ticks de reloj por lo que es necesario convertirla en milisegundos. 

Para poder realizar esta conversión lo primero que necesitamos saber cuánto equivale un tick de reloj en milisegundos. Esta información la hemos calculado previamente durante la preparación del entorno de ejecución y la tenemos  en la variable ‘miliSgPorTick’. Con estos dos datos, usando la función ‘ticksToMs’ convertimos los ticks de reloj a milisegundos para cada proceso. 

ticksToMs()
{
  let timeMs=($2*$miliSgPorTick)
  printf "%s\t %d\n" $1 $timeMs
}


Una vez que tenemos el tiempo de planificación de cada proceso en milisegundos, usamos otra función ‘CPUporPID’ para calcular el %CPU para cada proceso durante el intervalo de tiempo elegido

CPUporPID()
{
  porcentaje=$(echo "scale=4; $2/$tiempoTotal" | bc)  
  porcentajeT=$(echo "scale=4; $porcentaje*100" | bc) 
  porcentajeFormateado=`awk 'BEGIN{printf "%.4f", "'"$porcentajeT"'"}'`
  printf "%s\t%.4s\n" $1 $porcentajeFormateado
}

%MEM, Porcentaje de uso del procesador en el intervalo de tiempo elegido.

Para realizar este cálculo simplemente tenemos que sabe el tamaño en Kb de cada página de memoria, el número de páginas asignadas a cada proceso y el tamaño total de memoria principal. El tamaño de una página en Kb,  la hemos calculado previamente durante la preparación del entorno de ejecución y la tenemos  en la variable ‘pageSizeKB, el número de páginas en memoria asignadas al proceso ‘rss’ y el tamaño de la memoria también, en el archivo temporal ‘PVSCM_PID’ y la variable ‘memoriaTotal’ respectivamente. Ya solo nos queda llamar a la función MEMporPID pasando como parámetro el número de páginas para calcular el %MEM.

MEMporPID()
{
  porcentajeM1=$(echo "scale=4; $2*$pageSizeKB" | bc)  
  porcentajeM2=$(echo "scale=4; $porcentajeM1/$memoriaTotal" | bc)
  porcentajeMT=$(echo "scale=4; $porcentajeM2*100" | bc) 
  porcentajeMFormateado=`awk 'BEGIN{printf "%.4f", "'"$porcentajeMT"'"}'`
  printf "%s\t%.4s\n" $1 $porcentajeMFormateado
}

Como último paso en la sección DETALLE PROCESO  generamos un único archivo con todos los datos necesarios usando el comando paste.  El nombre de este archivo es ‘outputPID’.

Impresión de datos por pantalla

Se imprimen por pantalla los datos obtenidos usando los comandos echo y printf. El comando printf se ha elegido para el área de detalle del proceso ya que permite un mayor control del formato. Para la lectura del archivo ‘outputPID’ volvemos a usar un bucle while.





Y finalmente aquí está el código.

Culex.

jueves, 15 de diciembre de 2016

Diseño y Administracion de SO - UNED (Creando procesos)

Este año, entre otras cosas, toca darle duro al ‘Diseño y administración de SO’. Estamos usando UNIX o más bien GNG-Linux para poner en práctica toda la teoría que estamos viendo.

La asignatura me está resultando apasionante y el ED es buenísimo. El  caso es que el tema de procesos, hijos, señales y mecanismos IPC tiene su miga, por no decir algo más gordo.


Para intentar comprender bien el funcionamiento estoy haciendo algunas pruebas sencillas que quiero compartir y explicar por si son de utilidad a algún compañero.


Aquí también podéis ver el código --> código

El código va alternando la ejecución de dos procesos. Una parte del mismo solo la puede ejecutar el proceso hijo, pero la otra la pueden ejecutar hijo o padre de forma indistinta (depende de cómo son planificados por el SO).


En la parte que pueden ejecutar los dos procesos se observa que cuando lo ejecuta el hijo, se obtiene el PID correcto tanto del padre como del hijo. Por el contrario, cuando es el padre el que ejecuta esta parte del código el PID del hijo es incorrecto.



Esto es debido a que cuando la llamada al sistema fork() finaliza, el contexto de usuario del hijo es una copia idéntica al del padre y por tanto la lectura de la variable int padre es correcta. El padre por el contrario no puede acceder al contexto del hijo y por eso el PID del hijo es incorrecto. Muestra el valor de  int hijo=999. En lugar de hijo=getpid(). Recordar que la parte del if solo la ejecuta el hijo.

Culex.