domingo, 27 de noviembre de 2011

NP3: Firewall

To complement our Third Practical Assignment, we implement a simple Firewall in NachOS. First, we wrote 2 files, Firewall.cc and Firewall.h

Firewall.h

Firewall.cc

How it works?

The firewall uses a function named isBlocked(). That function receive a parameter (integer) that is a machine address. The function open a file named blocked, read whole the file and check if the received parameter is in the file. If the address is in the file that address is blocked, if not, the address is secure.

The function returns an integer, 1 if the address is secure, 0 if the address is blocked.

The firewall is included in nettest.cc:





And used ind the function:

When a package of a blocked address is received, the host machine receive a warning and the answer of the machine also changes.

Execution:



For this example, our blocked addresses are: 1, 3, 5, 10, 22, 30, 140, 200, 250, 310

The file blocked must be in the network folder, also the files Firewall.cc and Firewall.h.

Also includes the new Makefile of the network folder, an the new Makefile.common of NachOS main directory.

miércoles, 23 de noviembre de 2011

NachOS: 3rd Practical Assignment


Hi!!
In this post we will explain how it that we fix the execution errors in the NachOS network module.

In our third theoretical presentation we explain the three task than you need to run correctly the NachOS network module:

FIRST: you must have correctly implemented the first phase of the project, that means, the part of synchronization.
This is because NachOS needs to synchronize the input and output data operations through the network, the synchronization is implemented in the file "synchlist.h". We share to you our files, you can download at the following link.

Synch Files

SECOND: run the network module with the correct command, we look the code and read the notes in the header of each file and we conclude that the correct way is with the following command:

./nachos -m 1 -o 0 & ./nachos -m 0 -o 1 &

Results:

  • First we can see how NachOS open the 2 threads, obviously each one with different PID.
  • Next, NachOS open two sockets, an then the comunication start.
  • Each thread send 2 messages, one "question" and an "answer" 
  • The next block is the NachOS System Info, you can see that the system makes 2 Disk reads an each one receive and send two packets.
  • Finally, the clean up and close the sockets.
  • The two threads finish well. 
We see that many of you tried to run the module with a single call, such as:

./nachos -m 1 -o 0 &

which is not quite right because you have to call two instances of the program to communicate with each other.

Others tried to run 2 instances in different terminals, it also was not possible with the normal way, if you want to comunicate 2 instances of NachOS in different terminals you need fisrt call the Ubuntu sockets, this could be done by including the file, and use the socket functions defined in "sysdep.h".


Other exception that we receive it when NachOS tried to delete the swap file, that error ocurred because when we run 2 instances of the system, the first instance, at the end of the execution, delete the swap file and then the second don't have anything to delete.
We correct this exception verifying first if the swap file is still existing at the end of the execution of each thread, and if that is true, well, delete the file. 


That was all that we did, we hope that this information be useful to you.


:)



lunes, 31 de octubre de 2011

Manejo de memoria en móviles.

Sistemas sin memoria virtual


Algunos sistemas no tienen el mecanismo para proporcionar memoria virtual completa con paginación bajo demanda, esto es porque no poseen un disco duro. 


Por lo general, podemos decir que hay dos tipos de memoria que nos proporcionan este tipo de sistemas como lo son: la RAM y la memoria flash
  • La RAM tiene el código del sistema operativo. 
  •  La memoria flash se utiliza como memoria operativa y para el guardado permanente. 
Algunas veces, podemos expander la memoria utilizando SD cards, lo cual es solamente para almacenamiento indefinido. 


En este tipo de sistemas, si tenemos paginas pero las paginas no pueden ser cambiados de la memoria y guardarse en almacenamiento externo. Las paginas se sustituyen, pero la pagina que se va sustituir, se descarta.  Solo paginas de código se pueden remplazar ya que estas se respaldan solamente en la memoria flash.   


Tareas de administración de memoria que debemos de considerar como programadores. 


1. Administración del tamaño de aplicación 
El tamaño de la aplicación tiene que ser un diseño pequeño y orientado a objetos. 


2.Administración del montículo 
El montículo que es el espacio para la asignación dinámica de memoria, deben ser gestionado muy bien ya que el es limitado. 


3.Ejecución en el lugar 
La memoria flash es mapeada en el espacio de direcciones virtuales y los programas pueden ser ejecutados desde la memoria flash sin tener que copiarlos en RAM primero. 


4. Cargar de DLLs 
Los DLLs son archivos que tienen código ejecutable y son cargados por un programa del sistema operativo, cargar todos los DLLs cuando la aplicación se carga por primera vez en la memoria esta bien pero cuando cargamos muchos DLLs al inicio tendremos demoras que cuando se carga durante la ejecución, por lo tanto debemos de saber cuando es necesario cargar este tipo de archivos. 


5. Transferencia de la administración de la memoria al hardware 
Si hay MMU (que es la unidad de manejo de memoria) disponible, lo tenemos que utilizar ya que así el rendimiento del sistema es mejor. 


Un ejemplo de este tipo de sistemas, pueden ser, Symbian OS. 


El tamaño de los fames en symbian es de 4KB. 
Se utiliza la estrategia de page table de dos niveles.
El primer nivel es llamado directorio de páginas, esta lo que hace es guardar la memoria y hacer referencia por TTBR que es la translation table base register cuyo objetivo es llevar a recabar la dirección fisica. En el segundo nivel, ayuda a pasar de virtual a fisica la dirección de memoria. 




El objetivo por el cual Symbian OS utiliza una tabla de páginas de dos niveles es como ustedes lo pueden ver, reducir el tiempo de acceso a las tablas y el almacenamiento.










Por otro lado tenemos iOS que para gestionar la memoria de programa, se utiliza esencialmente el mismo sistema de memoria virtual que en Mac OS X. 
En iOS, cada programa todavía tiene su propio espacio de direcciones virtuales, pero a diferencia de Mac OS X, la cantidad de memoria virtual disponible es limitada por la cantidad de memoria fisica disponible. Esto se debe a que iOS no admite la paginación en el disco cuando la memoria se llena. En cambio, el sistema de memoria virtual, simplemente libera de solo lectura las páginas de memoria, como por ejemplo las páginas de códigos, cuando se necesita más espacio. 
Este tipo de páginas siempre se puede volver a cargar en la memoria más adelante si se vuelven a necesitar. 


Referencias


Puedes encontrar mas información acerca de memoria virtual en iOS en este link en donde habla extensamente en como se maneja la memoria en el sistema operativo. 


El texto esta basado en el capitulo 12, en el caso de estudio 3 del libro de Operating System de Tanenbaum. Pueden encontrar ese capítulo en este link o en la pagina 941 si tienen la tercera versión en español.

Gestión de memoria Linux Windows

Linux

Memoria Virtual
Direccionamiento de la memoria virtual: Linux utiliza una tabla de página de 3 niveles, cada tabla en particular tiene el tamaño de una página:
  • Directorio de páginas: Los procesos activos tienen un directorio de páginas único que tiene el tamaño de página. El directorio de páginas debe recidir en la memoria principal para todo proceso activo.
  • Directorio intermedio de páginas: Este directorio se expande a múltiples páginas. Cada entrada en la tabla de páginas apunta a una página que contiene una tabla de páginas.
  • Tabla de páginas: Cada entrada en la tabla hace referencia a una página virtual del proceso.
Algoritmo de reemplazo de páginas
Linux se basa en el algoritmo de Reloj, como ya expliqué este algoritmo consiste en asociar un bit de usado y otro de modificado con cada una de las páginas de memoria principal. En Linux el usado se reemplaza por una variable de 8 bits. Cada vez que se accede a la página la variable se incrementa. Después Linux recorre periódicamente la lista completa de páginas y decrementa la variable de edad de cada página a medida que va rotando por todas ellas en memoria principal. Una página que tiene un 0 es una "página vieja" es decir no se ha hecho referencia a ella desde hace ya algún tiempo y es el mejor candidato para el reemplazo. Cuando el valor de edad es más alto, la frecuencia con la que se ha accedido a la página recientemente es mayor y por lo tanto tiene un a posibilidad menor de elegirse para el reemplazo. Este algoritmo es una variante de la política LRU.

Reserva de memoria del núcleo
La gestión de memoria del núcleo se realiza en base a los marcos de página de la memoria principal. Su función básica es asignar y liberar marcos para los distintos usos.
Los fundamentos de la reserva de memoria en Linux son los mecánismos de reservas de páginas ya usados para le gestión de memoria virtual de usuario. Como se utiliza el algoritmo buddy, se puede reservar y liberar unidades de una o más páginas. Debido a que el tamaño mínimo de memoria que se puede reservar es de una página, la reserva de páginas no sería muy adecuada ya que el núcleo requiere pequeños fragmentos que se utilizarán por poco tiempo y que son de distintos tamaños. Es poreso que Linux utiliza un esquema conocido como "asignación de láminas". En una máquina Pentium/x64, el tamaño de página es de 4 Kbytes y los fragmentos dentro de una página se pueden asignar en tamaños de 32, 64, 128, 252, 508, 2040 y 4080 bytes.



Windows

El gestor de memoria esta diseñado para trabajar con páginas que van desde los 4 Kbytes hasta 64Kbytes. Las plataformas Intel, PowerPC, y MIPS tienen 4096 bytes por página y las plataformas DEC Alpha tienen 8192 bytes por página.

Mapa de direcciones virtuales en Windows
Los procesos de usuario en Windows puede ver un espacio de direcciones independientemente de 32 bits, permitiendo 4Gbytes de memoria por proceso. Por defecto una parte de esta memoria se reserva para el SO de manera que cada usuario dispone de 2Gbytes de espacio de direcciones posibles y todos los procesos lo comparten.
Se puede modificar esto restringiendo al SO a solo 1Gbyte y 3Gbytes para el usuario, esto viene explicado en la documentación de Windows.
Espacio de direcciones en Windows:
  • 0x00000000 a 0x0000FFFF reservada para ayudar a los programadores a capturar asignaciones de punteros Nulos.
  • 0x00010000 a 0x7FFEFFFF espacio de direcciones disponible para el usuario. Este espacio de encuentra dividido en páginas que se pueden cargar a la memoria principal.
  • 0x7FFF0000 a 0x7FFFFFFF una página de guarda, no accesible para el usuario. Lo que hace es verificar referencias a punteros fuera del rango.
  • 0x80000000 a 0xFFFFFFFF espacio de direcciones del sistema. Esta área es de 2Gbytes se utiliza por parte del ejecutivo de Windows, el micronúcleo y los manejadores de dispositivos.

Paginación
Cuando se crea un proceso, puede, en principio utilizar todo el espacio de usuario de 2Gbytes(menos 128 Kbytes). Este espacio se encuentra dividido en páginas de tamaño fijo, cualquiera de las cuales se puede cargar en la memoria principal. Estados de las páginas:
  • Disponible: Páginas que no están actualmente usadas por este proceso.
  • Reservada: Conjunto de páginas contiguas que el gestor de memoria virtual separa para un proceso pero que no se cuentan para la cuota
  • Asignada: Las páginas para las cuales el gestor de la memoria virtual ha reservado espacio en el fichero de paginación.
La distinción entre memoria reservada y asignada es muy útil debido a que minimiza la cantidad de espacio de disco que debe guardarse para un proceso en particular, manteniendo espacio libre en disco para otros procesos; y permite que un hilo o un proceso declare una petición de una cantidad de memoria que puede proporcionarse rápidamente si se necesita.

En Windows cuando se activa un proceso por primera vez se le asigna un cierto numero de marcos de página de la memoria principal como conjunto de trabajo. Cuando un proceso hace referencia ha un proceso que no esta en memoria principal, una de las páginas de dicho proceso se expulsa, y se trae la nueva página.
Cuando hay espacio disponible en la memoria, el gestor de memoria virtual permite que los conjuntos residentes de los procesos activos crezcan. Entonces se trae una nueva página a la memoria sin expulsar ninguna.
Cuando la memoria empieza a escasear, el gestor de memoria virtual mueve las páginas que se han utilizado hace más tiempo de cada uno de los procesos hacia swap, liberando así memoria principal.

Referencias
William Stallings (2005). Sistemas Operativos. España: Pearson. 384-388

Assignment 2



Some codes that we implement are:
FIFO in C
Download




FIFO in Python
Download




LRU in Python
Download




Page Generator in Python


============================================================
Statistics


Roberto Hard Disk statistics

Performance Specifications
Rotational Speed 7,200 RPM (nominal)
Buffer Size 16 MB
Average Latency 4.20 ms (nominal)
Load/unload Cycles 50,000 minimum

Seek Times
Read Seek Time 8.9 ms
Write Seek Time 10.9 ms (average)
Track-To-Track Seek Time 2.0 ms (average)
Full Stroke Seek 21.0 ms (average)

Transfer Rates
Buffer To Host (Serial ATA) 3 Gb/s (Max)

Physical Specifications
Formatted Capacity 500,107 MB
Capacity 500 GB
Interface SATA 3 Gb/s
User Sectors Per Drive 976,773,168


Juan Carlos Hard Disk statistics

Drive Capacity 320GB
Drive Interface Serial-ATA Revision 2.6 / ATA-8
Number of Platters (disks) 1
Number of Data Heads 2
Transfer Rate to Host 3 Gb/sec

Performance

Track-to-track Seek 2ms
Average Seek Time 12ms
Rotational Speed 5,400 RPM
Buffer Size 8MB





================================================================
You can have more information about virtual memory here:

miércoles, 14 de septiembre de 2011

PRIMERA PRESENTACIÓN PRÁCTICA

Hola Mundo!!

1. Para la primera parte realizamos un pequeño código que prueba la manera como funcionan los candados.
Lock *L = new Lock("testLock");

void printFunc(void* name) {
  int i = 0;
  
  L->Acquire();
  
  for(i; i<=5; i++) {
    printf("[%s] Testing Locks - No. [%d]\n", (char *)name, i);
  }
  
  L->Release();
}

void ThreadTest() {
  DEBUG('t', "Testing the lock and another things");
  printf ("TESTING:\n\n");
  
  const char* name;
  
  Thread* t1 = new Thread("Cecy");
  name = t1->getName();
  t1->Fork(printFunc, (void *)name);
  
  Thread* t2 = new Thread("Roberto");
  name = t2->getName();
  t2->Fork(printFunc, (void *)name);
  
  Thread* t3 = new Thread("Juan");
  name = t3->getName();
  t3->Fork(printFunc, (void *)name);
}
EJECUCIÓN
2. Para nuestra primera presentación práctica decimos dar solución al problema de productor
A) Solución usando candados:
Lock *L = new Lock("testLock");
int buffer = 0;

void bufferState() {
  printf("Buffer = [ %d ]\n", buffer);
}

void producer(void* name) {
  while(true) {
    L->Acquire();
    printf("[%s] producing data\n", (char *)name);
    buffer++;
    bufferState();
    L->Release();
    currentThread->Yield();
  }
}

void consumer(void* name) {
  while(true) {
    L->Acquire();
    while(buffer == 0) {
      L->Release();
      currentThread->Yield();
    }
    printf("[%s] consuming data\n", (char *)name);
    buffer--;
    bufferState();
    L->Release();
  }
}

void ThreadTest() {
  DEBUG('t', "Probando los candados y otras cosas");
  printf ("Producer-Consumer:\n\n");
  
  const char* name;
  
  Thread* t1 = new Thread("Cecy");
  name = t1->getName();
  t1->Fork(producer, (void *)name);
  
  Thread* t3 = new Thread("Juan");
  name = t3->getName();
  t3->Fork(producer, (void *)name);
  
  Thread* t2 = new Thread("Roberto");
  name = t2->getName();
  t2->Fork(consumer, (void *)name);
}
EJECUCIÓN
B) Solución usando semáforos
Semaphore* s1 = new Semaphore("cecy", 1);
Semaphore* s2 = new Semaphore("robert", 0);

int buffer = 0;

void bufferState() {
  printf("Buffer = [ %d ]\n", buffer);
}

void producer(void* name) {
  while(true) {
    s1->P();
    
    printf("[%s] producing data [S]\n", (char *)name);
    
    buffer++;
    bufferState();
    
    if(buffer == 1) {
      s2->V();	
    }
    
    s1->V();
    currentThread->Yield();
  }
}

void consumer(void* name) {
  int temp;
  
  s2->P();
  
  while(true) {
    s1->P();
    
    printf("[%s] consuming data. [S]\n", (char *)name);
    
    buffer--;
    temp = buffer;
    bufferState();
    s1->V();
				
    if(temp == 0){
      s2->P();
    }
  }
}

void ThreadTest() {
  DEBUG('t', "Probando los candados y otras cosas");
  printf ("Productor-Consumidor con semaforos:\n\n");
  
  const char* name;
  
  Thread* t1 = new Thread("Cecy");
  name = t1->getName();
  t1->Fork(producer, (void *)name);

  Thread* t2 = new Thread("Juan");
  name = t2->getName();
  t2->Fork(producer, (void *)name);
  
  Thread* t3 = new Thread("Roberto");
  name = t3->getName();
  t3->Fork(consumer, (void *)name);
}
EJECUCIÓN
C) Solución usando Variables Condicionales
Lock* l1 = new Lock("l1");

Condition* c = new Condition("cecy", l1);

int buffer;
int limite = 10;

void bufferState() {
  printf("Buffer = [ %d ]\n", buffer);
}

void anadir() {
  l1->Acquire();
  while(buffer == limite) {
    c->Wait();
  }
  buffer++;
  bufferState();
  c->Signal();
  l1->Release();
}

void extraer() {
  l1->Acquire();
  while(buffer == 0) {
    c->Wait();
  }
  buffer--;
  bufferState();
  c->Signal();
  l1->Release();
}

void producer(void* name) {
  while(true) {
    anadir();
    printf("[%s] producing data [CV]\n", (char *)name);
  }
}

void consumer(void* name) {
  while(true) {
    extraer();
    printf("[%s] consuming data [CV]\n", (char *)name);
  }
}

void ThreadTest() {
  DEBUG('t', "Probando los candados y otras cosas");
  printf ("Productor-Consumidor con variables de condicion:\n\n");
  
  const char* name;
  
  Thread* t1 = new Thread("Roberto");
  name = t1->getName();
  t1->Fork(producer, (void *)name);

  Thread* t2 = new Thread("Juan");
  name = t2->getName();
  t2->Fork(producer, (void *)name);
  
  Thread* t3 = new Thread("Cecy");
  name = t3->getName();
  t3->Fork(consumer, (void *)name);
}
EJECUCIÓN
REFERENCIAS: 
Nos basamos más que nada en los pseudocódigos vistos en las notas y en los libros de Stalling, Tanenbaum, y el de los dinosaurios :)


Códigos:

Ejecuciones:

viernes, 9 de septiembre de 2011

Primera presentación teórica


LOCKS (code)
Lock::Lock(const char* debugName) {
  name = debugName;
  s = new Semaphore(name,1);
  locked = true;
  owner = NULL;
}

Lock::~Lock() {
  delete s;
  delete owner;
}

void Lock::Acquire() {
  s->P();
  locked = true;
  owner = currentThread;
}

bool Lock::isHeldByCurrentThread() {
  return(owner == currentThread);
}

void Lock::Release() {
  if(locked && isHeldByCurrentThread()) {
    locked = false;
    owner = NULL;
    s->V();
  }
}
CONDITIONAL VARIABLES (code)
Condition::Condition(const char* debugName, Lock* conditionLock) {
  name = debugName;
  l = conditionLock;
  queue = new List;
}

Condition::~Condition() {
  delete l;
  delete queue;
}

void Condition::Wait() {
  Semaphore *temp;
  
  if(l->isHeldByCurrentThread()) {
    temp = new Semaphore("condition", 0);
    queue->Append(temp);
    l->Release();
    temp->P();
    l->Acquire();
    delete temp;
  }
}

void Condition::Signal() {
  Semaphore *temp;
  
  if(l->isHeldByCurrentThread()) {
    if(!queue->IsEmpty()) {
      temp = queue->Remove();
      temp->V();
    }
  }
}

void Condition::Broadcast() {
  while(!queue->IsEmpty()) {
    Signal();
  }
}

martes, 23 de agosto de 2011

Videos

We made two video tutorials.How can you get nachOS 3.4 on 64 bits and how can you get nachOS 4.0 on 32 bits computer.



lunes, 22 de agosto de 2011

lunes, 8 de agosto de 2011

Introduction

Sandra Cecilia Urbina Coronado 1413021
Roberto Carlos Martínez Moreno 1451473
Juan Carlos Espinosa Ceniceros 1535012