Universidad Tecnológica Nacional Facultad Regional Buenos Aires Proyecto Final “Sistema Operativo Routix” Profesor: Eduardo Ioli Ayudantes: Claudia Orlandi – Basilio Robino Curso: R6051 - Viernes Integrantes del Grupo: Candurra, Martín Legajo: 110397-0 Ortega, Mariano Gastón Legajo: 106511-7 Año: 2004
357
Embed
Universidad Tecnológica Nacional - Public Git Hostingrepo.or.cz/w/pfinal.git/blob?f=informe+final.doc · Web viewAlgunos ejemplos de núcleos monolíticos: Núcleos Unix tradicionales,
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
2. Características y Especificaciones técnicas_______________________________9Características______________________________________________________________9
3. Descripción detallada del proyecto_____________________________________11Protección, Segmentación y Paginación________________________________________12
9. Lista de Componentes_______________________________________________57
10. Cálculo de Confibilidad en los circuitos y programas de Computación______58Definición de Confiabilidad__________________________________________________58
Concepto de Falla__________________________________________________________58
Predicción de la confiabilidad________________________________________________63
Macromodelo de Musa______________________________________________________63
Estimación por momentos___________________________________________________66
Estimación por cuadrados mínimos___________________________________________67
11. Instrucciones de puesta en marcha, ensayo funcional, ajuste y calibración, registros.______________________________________________________________68
12. Ensayos de aceptación y registro____________________________________71
13. Instrucciones de instalación y operación._____________________________72
14. Mantenimiento y diagnóstico de fallas________________________________78
15. Cálculo de costos y estudio del mercado para su comercialización._________79Análisis de Costos__________________________________________________________79
16. Análisis de la planificación proyectada en la propuesta y la resultante real.__84
17. Bibliografía y normativa utilizada.___________________________________86Bibliografía_______________________________________________________________86
Antes de pasar a una descripción del proyecto mediante bloques, consideramos necesario definir dos tipos de arquitectura de Kernel de sistemas operativos, ellos son: Kernel Monolítico (el cuál hemos implementado en nuestro proyecto) y Micro Kernel.
Kernel Monolítico
Un Kernel monolítico define una interfaz virtual de alto nivel sobre el hardware, con un conjunto de primitivas o llamadas al sistema (system calls) que implementan servicios del sistema operativo como administración de procesos, concurrencia y administrador de memoria (memory manager) en distintos módulos que se ejecutan en modo protegido.
Incluso si cada módulo está separado del resto, la integración del código es muy fuerte, y es difícil hacerla bien. Además, como todos los módulos se ejecutan en el mismo modo, un error en uno de ellos puede colgar todo el sistema. Sin embargo, cuando se ha completado la implementación y ésta es fiable, la fuerte integración de los componentes permite un uso muy efectivo de las características de bajo nivel del sistema subyacente, lo que hace que un buen núcleo monolítico sea muy eficiente. Los partidarios del enfoque monolítico afirman que si el código no es correcto, no debería hallarse en un núcleo, y que si lo es, entonces hay pocas ventajas en la aproximación micro núcleo.
La mayoría de núcleos monolíticos, como Linux y el núcleo de FreeBSD, pueden cargar módulos ejecutables en cualquier momento, lo que permite una forma fácil de ampliar las capacidades del núcleo según se necesite, pero permitiendo que la cantidad de código que se ejecuta en espacio de núcleo se mantenga al mínimo.
Algunos ejemplos de núcleos monolíticos:
Núcleos Unix tradicionales, como los núcleos de los BSD. El núcleo Linux
MicroKernel
El enfoque de MicroKernel consiste en definir una abstracción muy simple sobre el hardware, con un conjunto de primitivas o llamadas al sistema que implementan servicios del sistema operativo mínimos, como la administración de hilos (threads ), el administrador de memoria y la comunicación entre procesos.
El objetivo principal es la separación de la implementación de los servicios básicos y de la política de funcionamiento del sistema. Por ejemplo, el proceso de bloqueo de E/S se puede implementar con un servidor en
espacio de usuario ejecutándose encima del micro núcleo. Estos servidores de usuario, usados para gestionar las partes de alto nivel del sistema, son muy modulares y simplifican la estructura y diseño del núcleo. Si falla uno de estos servidores, no se colgará el sistema entero, y se podrá reiniciar este módulo independientemente del resto.
Algunos ejemplos de micro núcleos:
AIX La familia de micronúcleos L4 El micronúcleo Mach, usado en GNU Hurd y en MAC OS X Minix MorphOS QNX RadiOS VSTa
Modularización del Kernel Monolítico de Routix
Kernel (DPL 0)
Memory Manager
Scheduler
Drivers File System
SignalsSystem Calls
Aplicaciones de Usuario (DPL 3)
Tareas de Usuario Process Status (PS) Kill
Hardware
Alternativa 1: Modelo MultiCapas (Multi Layer)
Scheduler (capa 0)
Memory Manager (capa 1)
Message Interpreter (capa 2)
I/O Manager (capa 3)
Aplicaciones de Usuario (capa 4)
Alternativa 2: microKernel
Hardware
microKernel
2. Características y Especificaciones técnicas
Características
Kernel monolítico
Interfaz POSIX compatible
Soporte cuasi preentivo
Manejo de señales
Cambio de tareas por software (software task switching)
Dos niveles de seguridad (modo usuario y modo kernel)
Temporizadores
Acceso a dispositivo de disco
Acceso directo a memoria (DMA)
Acceso a dispositivo de video
Acceso a dispositivos de consola
Manager de memoria
Cache de dispositivos de bloque (disco por ejemplo).
Lenguage de programación: Assembler Intel y AT&T, C.
Drivers:
o Video (manejo texto)o Tecladoo Manejador de Memoriao Disco Floppyo Filesystem FAT12o Timerso Puertos de entrada y salida
3. Descripción detallada del proyecto
Este proyecto comenzó como un desarrollo orientado a un Router de capa 3. A medida que fuimos desarrollando parte de él, asimilando realmente como programar un procesador Intel 80386 (con todas sus prestaciones) y comprendiendo como funcionan los sistemas operativos actuales, nos dimos cuenta que requeríamos una base sólida sobre la cuál en un futuro podríamos implementar cualquier tipo de sistema operativo, ya sea orientado a un Router como a un Firewall, servidor o workstation. Es decir dejamos un poco de lado el objeto final del proyecto y nos centramos en desarrollar un kernel sólido y abstracto, sobre el cuál podamos escalar en buenas proporciones (queríamos evitar por todos los medios, desarrollar un código que en poco tiempo se viera saturado de errores, que no pueda crecer, y que sea difícil de comprender por terceros).
A continuación pasamos a detallar cada modulo que compone al proyecto.
Protección, Segmentación y Paginación
Es sabido que no todo el código posee la misma prioridad o importancia dentro de un sistema. Existen partes vitales, las cuales en caso de fallar, colapsarían todo el conjunto. Por otro lado, muchas partes del código son prescindibles, es decir, que si llegaran a fallar o a faltar, el sistema completo o al menos parte de él podría seguir funcionando sin grandes inconvenientes. Podría citar como ejemplo un device driver de una placa Token Ring, el cuál en un router podría momentáneamente fallar, sin embargo la conmutación y enrutamiento de paquetes en las demás interfaces seguiría funcionando. Es por esto que decidimos utilizar dos niveles de protección (los cuales coinciden con el Anillo 0 y 3 de la unidad de segmentación y con los privilegios User y admin de la de paginación).
Segmentación
A medida que fuimos aprendiendo como utilizar objetos realizados en C en modo protegido fuimos descubriendo que esta técnica de protección era útil en algunos casos, pero nos imposibilitada desarrollar el código libremente. ¿Por qué pasa esto?. Los compiladores de C, utilizan un modelo de segmentación Flat, el cual equivale a decir que los segmentos de Código, Datos y Stack se encuentran superpuestos en sus comienzos. Esto a primera vista parece romper los esquemas y técnicas creadas por Intel para proteger los sistemas multitarea sin embargo, como es lógico, posee una explicación.
El lenguaje C fue concebido mucho antes que Intel creara su primer microprocesador, por eso la mayoría de las cualidades de este lenguaje son genéricas para casi todas las arquitecturas de microprocesadores que existen.
Teniendo todo esto en cuenta nos limitamos a utilizar sólo dos niveles de privilegio (Kernel y User), superponiendo los segmentos de código y datos con el fin de poder programar libremente.
Paginación
Esta técnica es la que nos permite realmente evitar que un proceso corriendo en modo User pueda inferir con código o datos vitales del sistema, los cuales se encuentran en modo Kernel.
Algo muy interesante que nos permitió la paginación y que realmente nos facilitó mucho la tarea de correr paralelamente aplicaciones fue la memoria virtual. Como es bien conocido por todos la unidad de paginación permite que una aplicación “crea” que está corriendo en una dirección dada (o
lógica) cuando en realidad su posición física es diferente. Esta excelente capacidad nos permitió dos cosas:
Reacomodar el código de Kernel, sus Datos y Su Pila, a direcciones virtuales, las cuales nos permitirán en un futuro expandir con facilidad el tamaño del kernel.
Correr varias aplicaciones de usuario simultáneamente. Cuando uno linkea un archivo objeto, el linker puede traducir todas esas direcciones relativas al comienzo del código a direcciones específicas dictadas por el programador. Por ejemplo nuestras aplicaciones fueron linkeadas para correr en la dirección 0x80000000 (equivalente a 2GB). ¿ Porqué tuvimos que hacer esto ? Porque no teníamos forma de saber que posiciones están libres, asi que sencillamente buscamos cualquier espacio de memoria capaz de contener el proceso a correr y lo mapeamos virtualmente en la dirección deseada ( 0x80000000 en este caso).
Memory Manager
El Memory Manager tiene por función primordial la administración de memoria, es decir, deberá saber que bloques (páginas en nuestro caso) están libres y cuáles fueron entregados a algún proceso o aplicación. El Memory Manager comienza por su inicialización contando la memoria disponible, y calcula cuantas páginas podrá brindar a los procesos que las soliciten.
Para administrar la memoria utilizamos una estructura de tipo Pila, es decir, en ella se encuentran “apiladas” las direcciones de todas las páginas disponibles. Cada vez que un proceso pide una página, el MM simplemente la “popea” del stack, mientras que cuando un proceso la libera, la “pushea” nuevamente dentro de él.
Las funciones que se encargan de esto son:
int kmalloc_page_start (addr_t *inicio, addr_t *fin);addr_t kmalloc_page();int kfree_page(addr_t direccion);
El Memory Manager también implementa las clásicas funciones de alocación dinámica de bloques de tamaño variable: malloc y free.
Hay muchas tareas que debe implementar a nivel paginación. Una de las más importantes es kmapmem la cuál se encarga de mapear en el directorio y tabla de página correspondiente una dirección física en una lógica. Su prototipo es:
int kmapmem ( addr_t fisica, addr_t logica, addr_t directorio, word atributo
Existen bastantes funciones auxiliares dentro del memory manager, para simplificar y estructurar el código que lo conforma.
Multitarea
Algo fundamental e infaltable en cualquier sistema operativo es la capacidad de simular la ejecución de varias tareas en forma simultanea, brindando una performance más alta y un mayor aprovechamiento de los recursos.
En un comienzo utilizamos todos los recursos que nos brindaba la arquitectura IA32, es decir, usábamos una estructura TSS por cada proceso que fuera a correr en el CPU. ¿ Que inconvenientes tuvo esto ? Existen algunos casos, donde necesito frenar la ejecución del proceso actual y realizar ciertas comprobaciones, pero sin llegar a realizar un cambio de contexto (el cuál significa transferir 104 bytes de la memoria al microprocesador y viceversa). Por ejemplo, cada vez que se cumple un TIC de reloj (impuesto por el Timer Tick) el scheduler debe verificar si la tarea actual excedió su tiempo de ejecución (por lo que deberá dar el control a otra tarea, luego de realizar todas las acciones administrativas correspondientes) en caso negativo, debe permitir que esta tarea siga ejecutándose. Si en vez de realizar una conmutación de contexto vía TSS (es decir, mover 208 bytes) sólo “pusheamos” y “popeamos” algunos registros de propósito general, estaremos ahorrando mucho tiempo, más si se tiene en cuenta que se produce un Tick cada 10 mili segundos (es decir, 100 ticks por segundo).
Todas las tareas, ya sean en modo Usuario o modo Kernel, utilizan el mismo TSS el cual es modificado por una rutina para ese fin. A este tipo de multitarea se la llama “Multitarea por software”.
Cada vez que llega una interrupción de Timer Tick, el Kernel produce un Stack Switching (en caso que se pase de nivel de privilegio 3 a nivel 0), para poder guardar en el Stack de modo Kernel de la tarea, los valores de los registros EFLAGS, EIP y CS. Esta acción es totalmente automática y es la
que permite asegurar que una tarea nunca pueda corromper la información salvada en una pila al ocurrirse una interrupción o una excepción.
Como señalamos anteriormente, cada 10 milisegundos aparece un pulso de timertick, originado por el timer 0 del 8254, esto hace que el Controlador Programable de interrupciones genera una interrupción externa que es atendida por el Kernel.
Ocurre entonces la siguiente secuencia de eventos:
Con la llegada de la interrupción de timertick, se produce un cambio de nivel (de modo Usuario a modo Kernel), este cambio es realizado por el microprocesador quien realiza un stack switching permitiendo que el kernel trabaje sin riesgos de desborde del array de usuario. Con lo que en nuestro stack de modo kernel tenemos la dirección y segmento de retorno (registros EIP y CS), el estado de los flags (EFLAGS) y la dirección y segmento del stack de modo usuario.
En este momento guardamos en la pila (recordemos que ya estamos en modo kernel) los registros que cambian entre tareas (el código siguiente pertenece al archivo task.asm):
_switch_to_kernel_mode: ;(punto de entrada de la IRQ0)....push ebppush esipush edipush edxpush ecxpush ebxpush eaxpush dspush espush fspush gs.....
Resguardados estos registros, debemos comenzar a utilizar un stack de modo kernel pero de mayor tamaño e independiente de la tarea, para esto primero guardamos el puntero de pila y registro de paginación en las variables esp0 y cr3 de la estructura task_struct_t propia de la tareas:
; Guardamos el valor del esp de modo kernel de la tareamov ebx, dword [ _actual ]mov dword [ ebx ], esp
; Guardamos en la task_struct el valor del CR3
mov ebx, dword [ _actual ]add ebx, 4 ;desplazar 4 bytes la direccion ya que
;CR3 es el 2do elemento de la estructuramov eax, cr3mov dword [ ebx ], eax
Y cargamos un stack de modo kernel de mayor tamaño:
; Recuperamos el stack del kernel, así trabajamos ; tranquilos y sín límitesmov eax, KERNEL_STACK_TOPmov esp, eax
Por fin, entonces pasamos el control al kernel
; Pasamos el control al kernelcall _timertick_handler
El handler de timertick es una función que actualiza los ticks transcurridos, actualiza el reloj del sistema, actualiza y cheque que no se hayan vencidos los timers, actualiza la estadísticas de uso del proceso en curso (ticks restantes y ticks totales utilizados), y en caso que haya terminado el tiempo de ejecución del proceso se llama al scheduler quien selecciona la próxima tarea a ejecutar. Todo esto lo veremos en mayor detalle en los punto “Timers” y “Scheduler”
El scheduler puede seleccionar otra tarea para ejecutar, con lo que cambiará la dirección apuntada por la variable actual a la estructura de la nueva tarea.
Una vez que retornamos de la función timertick_handler debemos recuperar el stack de modo kernel de la tarea:
_entrada_de_inicio:; Volvimos, ahora recuperamos el stack de modo kernel de la
tarea; que puede ser diferente a la anterior, todo depende de si se
venció; el tiempo de permanencia y/o el scheduler la switcheo (por
; También acá recuperamos el CR3mov ebx, dword [ _actual ]add ebx, 4mov eax, dword [ ebx ]mov cr3, eax
Si se a producido un cambio de tarea, el valor de puntero de pila de modo kernel (SS0) del TSS debe cambiarse, para poder acceder en la próxima interrupción, para ello:
; Pisamos el valor de esp0 en el TSS para que la próxima; interrupción pueda producirse; el cambio de modo ( usuario -> kernel ) de manera correcta; calculamos el top del esp0, recordando que estamos usando ; una página para el descriptor del proceso; y el stack, el cual encuentra su top al final del mismo, o sea:; top_esp0 = ( esp0 & 0xffffff00 ) + 4096
mov eax, espand eax, 0xfffff000add eax, 0x1000
mov dword [ _tss + OFFSET_ESP0 ], eax
Para entender un poco el código precedente debe tenerse en cuenta que cada tarea aloja en una misma página de 4Kb el contenido de su estructura (task_struct_t) al principio y su stack de modo kernel desde el final de dicha página.
Finalmente recuperamos todos los registros de este stack y retornamos:
; Recuperamos el contenido de todos los registrospop gspop fspop espop dspop eaxpop ebxpop ecxpop edxpop edipop esipop ebp
iret
Cuando el microprocesador ejecuta la instrucción iret pasa de modo Kernel a modo Usuario y recupera los registros de contexto de ejecución CS, EIP y EFLAGS, y la tarea vuelve a su ejecución habitual.
Llamadas al sistema
Tienen por objeto que los procesos que corren en espacio de Usuario puedan tener acceso a diferentes funciones del kernel, acceso a periféricos, comunicación con otros procesos, etc.
Las llamadas al sistema las implementamos vía interrupciones, específicamente en la int 0x50. Cuando alguna función de librería o el código usuario ejecuta la instrucción “int 0x50” inmediatamente se realiza un Stack Switching y la correspondiente toma de los parámetros. Estos últimos son recibidos mediante los registros de propósito general (eax, ebx, ecx, edx, esi y edi).
A diferencia de Linux que recibe las llamadas y las ejecuta apoyandose en un vector de punteros, nosotros implementamos varios vectores de punteros, separando las llamadas y clasificándolas en grupos). Si bien nuestro sistema es levemente mas lento que el utilizado por linux, permite mayor claridad al mirar el código fuente, con una consecuente mayor facilidad para interpretarlo.
Qué código es el que corre al ejecutarse “int 0x50”
_intSysCall:push eaxshr eax, 16 ;Verificar si el numero de grupo existecmp eax, MAX_SYSCALLS_GROUPSja error_syscall_nopop eaxPusheo los registros donde recibo los parámetros push ebppush esipush edipush edx
push ecxpush ebxUsar como directorio de paginas el CR3 del Kernelmov ebx, [ _KERNEL_PDT ]mov cr3, ebxmov ebx, eaxshr ebx, 16 ;Pongo en ebx el numero de grupo de llamadas
jmp [_syscall_group_vector + ebx * 4] ; (El vector de grupos de llamadas a funciones se encuentra definido en syscalls.c )
Las llamadas implementadas al momento son:
Funciones de grupo PROCESS#define SYS_EXEC 0#define SYS_VOID 1#define SYS_FORK 2
Funciones de grupo CONSOLE#define SYS_PRINT 0#define SYS_GETS 1#define SYS_CLRSCR 2
Funciones de grupo TIMER#define SYS_SLEEP 0#define SYS_PROC_DUMP 1#define SYS_KILL 2#define SYS_USLEEP 3
Controlador de Floppy y DMA
Este driver lo implementamos para poder comprobar que tan estable era nuestro kernel ejecutando tareas dinámicas (es decir, cargadas desde algún medio, luego de haberse establecido las funcionalidades de sistema operativo).
Realmente nos tomó un poco más del tiempo del que esperábamos, ya que nos topamos con problemas “mecánicos” que nunca antes habíamos tenido. Como el controlador de floppy Intel 82077ª fue evolucionando a lo largo del tiempo para poder manejar discos de diferente tamaños físicos, densidades y estructuras lógicas, fue manteniendo compatibilidad hacia atrás, a medida que pasaba le tiempo. Esto que desde algún punto de vista es una característica muy positiva, se transforma en una carga a la hora de escribir un driver que pueda manejarlo. Existen múltiples comandos que realizan las mismas tareas, pero dejan algunos registros en estados diferentes, por lo que el programador debe tener mucho cuidado en como los utiliza.
El acceso a los sectores de disco es de tipo CHS (cilindro, cara, pista) por lo que el driver deberá encargarse de hacer las traducciones pertinentes dependiendo de la capacidad y del formateo del disco.
El hecho que posea una gran cantidad de mecánica hace que deban tenerse en cuenta consideraciones necesarias, por ejemplo, para esperar a que el motor alcance la velocidad de régimen permanente. También deberá mantenerse una relación de compromiso en el apagado del motor, ya que si este permanece siempre encendido, su vida útil será muy baja; mientras que si lo apagamos inmediatamente después de un acceso, deberemos nuevamente esperar a que alcance la velocidad de régimen permanente antes de volver a realizar otra lectura (aumentando mucho el tiempo de lectura).
Además nos vimos en la obligación de aprender a configurar el controlador de DMA 8237ª, para lograr hacer transferencias por bloques y no por bytes.
Las funciones que componen al driver son:
1. void motor_on();(Enciende el motor)
2. void motor_off();(Apaga el motor)
3. int floppy_sendbyte(byte valor);(Envia un byte al registro de datos del controlador )
4. int floppy_get_result();(Lee un byte del registro de datos del controlador)
5. int init_floppy();(Inicializa al controlador)
6. int recalibrate();(Recalibra la posicion de la cabeza a la pista 0)
7. byte seek (byte cara, byte pista);(Posicionar el cabezal sobre la pista indicada)
8. int read_sector (byte cara, byte cilindro, byte sector);(Lee un sector físico (previamente el cabezal debe estar calibrado y posicionado)
9. int block(void);(block: bloquea la ejecución del proceso que la llama hasta recibir una interrupción de floppy)
10. void init_floppy_DMA ( byte comando );(Inicializa el controlador DMA, tanto para leer como para escribir (según comando))
11. int leer_escribir(byte operacion, dword sector_logico, byte *buffer);
(Acepta como operación READ_SECTOR y WRITE_SECTOR. Es la encargada de convertir un sector lógico (o LBA) en el sector físico (CHS) correspondiente. Inicializa el controlador de floppy y enciende el motor. En caso de time outs, intenta hasta MAX_TRYS)
Controlador de FAT12
Implementamos este file system ya que es uno de los más sencillos, es leído por casi todos los sistemas operativos y es el sistema de archivos utilizado generalmente en los discos flexibles.
Las funciones comprendidas por este controlador tienen por objeto hacer transparente a las llamadas de archivos el acceso lógico a los archivos. El controlador está formado por:
1. int fat_12(void);(Lee el sector logico 0 (Boot sector), y levanta todos los parametros fisicos y logicos del disco para que sean usados por las demás funciones)
2. int levantar_fat(void);(Levanta todos los sectores correspondientes a la FAT y los coloca en la memoria Debera tenerse en cuenta que al cambiar el diskette, el flag "fat_levantada" debera volverse a FALSE)
3. fat12_entry_t *fat_file_find (char *nombre, fat12_entry_t *datos_archivo); (Recibe un nombre de archivo, y lo busca en el disco. En caso negativo retorna NULL)
4. int fat_next_sector ( dword sector_inicial );(Esta función recibe como parámetro un numero de sector y mediante la FAT obtiene cual es el siguiente)
5. void fat_adapta_name (byte *nombre_fat, byte *adaptado);(Recibe un nombre en formato FAT ( KERNEL BIN ) y lo transforma a un formato tipo string: KERNEL.BIN\0 )
6. void *floppy_cache (dword sector);Esta funcion mantiene una lista enlazada con sectores leidos del floppy. Cualquier funcion de fs que quiera leer o escribir un sector, deberá recurrir a esta. Devuelve un puntero a la dirección de memoria donde se encuentra el sector pedido)
Llamadas para el manejo de archivos
Dentro de este conjunto caen las funciones open, close, read y lseek (la función write no ha sido implementada, debido a que requiere para un
correcto funcionamiento la implementación de spinlocks o candados dentro del kernel). Estas funciones poseen el mismo prototipo que sus pares del estándar Unix , con el fin de mantener cierta compatibilidad en lo que a desarrollo de aplicaciones se refiere.
Los prototipos son:
int open (char *nombre);int close (int fd);ssize_t read (int fd, void *buf, size_t len);int lseek (int fd, int offset, int donde);
Timers
Los timers son la base para el manejo de tiempos en el sistema, desde la simple actualización horaria pasando por funciones que permiten detenar la ejecución de una tarea durante cierto.
En nuestro sistema operativo implementamos el manejo y actualización de la fecha y hora, leyendo el Real Time Clock durante el inicio y actualizándolo con cada tick recibido, así como las funciones de librería necesarias para acceder a ellas:
// Estructura tmstruct tm { int tm_sec; /* seconds after the minute - [0, 61] */ /* for leap seconds */ int tm_min; /* minutes after the hour - [0, 59] */ int tm_hour; /* hour since midnight - [0, 23] */ int tm_mday; /* day of the month - [1, 31] */ int tm_mon; /* months since January - [0, 11] */ int tm_year; /* years since 1900 */ int tm_wday; /* days since Sunday - [0, 6] */ int tm_yday; /* days since January 1 - [0, 365] */ int tm_isdst; /* flag for alternate daylight savings time */};
typedef int time_t;
El kernel maneja también timers, que son activados ante ciertos eventos, por ejemplo, surgió como necesidad fundamental cuando se requería realizar demoras controladas en los programas, veamos el siguiente ejemplo:
for ( i=0; i<CUENTA_DEMORA; i++) { ....}
el código precedente permite realizar una demora, pero tiene dos falencias fundamentales:
No puede controlarse con exactitud el tiempo de demora, y de hecho varía según la velocidad de ejecución del microprocesador
Durante el tiempo de demora se sigue utilizando el procesador.
Estas dos condiciones resultan inaceptables. Para solucionar este inconveniente aparecen las funciones sleep y usleep que permiten dormir una tarea durante un cierto tiempo, o sea, la tarea queda en estado TASK_STOPPED mientras no se llego al tiempo especificado, con lo que no es ejecutada por el scheduler y entonces libera tiempo para que otros procesos utilicen el procesador.
1. int sleep(int segundos)2. int usleep(int usegundos)
Scheduler
Es el encargado de seleccionar la próxima tarea que se ejecutará en el microprocesador.
Cuando una tarea en ejecución vence el tiempo actividad (ver la secuencia explicada en la sección “Multitarea”) el scheduler toma control de la situación y decide cuál será la tarea a ejecutarse.
Para realizar esta selección el Scheduler cuenta con una lista enlazada de tareas (figura 1), cada tarea esta unívocamente caracterizada por la estructura task_struct_t:
typedef struct task_struct_t{
dword esp0; dword cr3; dword cr3_backup; pid_t pid; struct task_struct_t *proxima; char descripcion[MAX_DESCRIPTION]; byte estado; word prioridad; word cuenta; dword tiempo_cpu; struct file *open_files [MAX_FILES_POR_TAREA]; //definido en file.h void *paginas[MAX_PAGINAS_POR_TAREA]; word num_code; //cantidad de paginas de codigo de la tarea word num_data; //cantidad de paginas de datos y bss int err_no; signal_struct_t senales;} task_struct_t ;
El scheduler utiliza los campos proxima y estado para encontrar la próxima tarea a ejecutar, lo cual es un método sencillo pero eficiente de selección, en una primer etapa, luego seguiremos avanzando con el fin de que el algoritmo de selección tenga en cuenta muchas mas variables (prioridad por ejemplo).
Aplicaciones
Como ya hemos comentado anteriormente, el kernel corre aplicaciones en un nivel de ejecucion 3 de la unidad de segmentacion y en modo usuario de la de paginacion. Al referirnos a aplicaciones podemos estar hablando tanto
Tarea 1
T ASK_ RUNN I NG
Proxim a
Tarea 2
T ASK_ STO PPED
Proxima
Tarea 3
T ASK_ RUNN I NG
P roxima
Tarea 4
T ASK_ RUNN I NG
Proxima
Tarea 5
T ASK_ ST O PPED
Proxima
I nicio
N ULL
Figura 1
de programas de usuario normales, de drivers, o de la implementacion de algun tipo de servicio de red. Esto dependera de las funcionalidades del sistema, ya que como dijimos anteriormente nuestra intencion es general un kernel funcional, el cual sea facilmente adaptable para cumplir diferentes tareas. Alguien podria querer, por ejemplo, implementar un Firewall compuesto por varios modulos independientes al kernel, entonces para evitar o reducir los problemas que pueda causar alguno de ellos, simplemente se los ejecuta en modo User, es decir como una aplicacion.
Las aplicaciones deben estar realizadas en formato COFF32 (en un futuro proximo se implementara la ejecucion de tareas en formato ELF32).
Poseen como requisito fundamental ser linkeadas en la direccion virtual VMA (virtual memory address) 0x80000000 mediante el GNU LD.
Cada tarea poseera, al ejecutarse, su propio espacio de direcciones, es decir, que al ser ejecutada, se cambia el directorio de paginas de la tarea anterior por el de la tarea actual, el cual contiene los mapeos de los datos y codigo del kernel, sumados a los de esta tarea en particular.
4. Conexiones Eléctricas
Generalidades
En esta sección del informe se presentarán las conexiones eléctricas que utiliza el hardware que se utiliza como base para la ejecución del sistema operativo.
Comenzaremos mostrando un esquema funcional del microprocesador utilizado para luego poder entendar las conexiones que se realizan a los dispositivos externos al mismo por medio de la placa base (motherboard).
Este primer diagrama nos muestra el bus del sistema y la interrelación del mismo con el bus de cache de nivel 1 y 2 fundamentales para proporcionar performance al sistema que se lo utilice como base.
Vemos a continuación el mismo diagrama con un mayor grado de detalle. Pueden ver los tres bloques que conforman el decodificador de instrucciones (Instruction Decoder) junto con los cinco bloques que pertenecen a la Unidad de Ejecución (Execution Unit).
A continuación se presentan los diagramas eléctricos de la interconexión del microprocesador con los dispositivos como la unidad controladora de floppy, memoria, controladora ide, isa, teclado, video, etc...
Circuitos
Diagrama general de conexión del microprocesador. para una mejor visualización se adjuntan los diagramas en formato pdf).
Diagrama de conexión del emulador.(para una mejor visualización se adjuntan los diagramas en formato pdf).
Diagrama general de conexión de la controladora de floppy (para una mejor visualización se adjuntan los diagramas en formato pdf).
Diagrama general de conexión del microprocesador. (para una mejor visualización se adjuntan los diagramas en formato pdf).
Diagrama general de conexión del bus IDE. (para una mejor visualización se adjuntan los diagramas en formato pdf).
Diagrama general de conexión del bus ISA. (para una mejor visualización se adjuntan los diagramas en formato pdf).
Diagrama general de conexión del teclado. (para una mejor visualización se adjuntan los diagramas en formato pdf).
Diagrama general de conexión del LA. (para una mejor visualización se adjuntan los diagramas en formato pdf).
Diagrama general de conexión del mouse y teclado. (para una mejor visualización se adjuntan los diagramas en formato pdf).
Diagrama general de conexión PCMCIA. (para una mejor visualización se adjuntan los diagramas en formato pdf).
Diagrama general de la fuente de alimentación. (para una mejor visualización se adjuntan los diagramas en formato pdf).
Diagrama general de conexión del generador de clock. (para una mejor visualización se adjuntan los diagramas en formato pdf).
Diagrama general de conexión de los puertos seriales. (para una mejor visualización se adjuntan los diagramas en formato pdf).
Diagrama general de conexión del terminador. (para una mejor visualización se adjuntan los diagramas en formato pdf).
Diagrama general de conexión VGA. (para una mejor visualización se adjuntan los diagramas en formato pdf).
Diagrama general de conexión de la memoria. (para una mejor visualización se adjuntan los diagramas en formato pdf).
5. Circuitos impresos
Antes que nada, debemos recordar que hemos elegido desarrollar este proyecto basándonos en la familia de microprocesadores de 32 bits de Intel. La elección fue realizada partiendo de la base que son la familia de microprocesadores más vendida, y de más bajo costo; por lo que es muy fácil encontrar una arquitectura de este tipo en nuestro país, a fin de hacer sobre la misma el deploy de Routix.
Debido al tamaño de las imágenes referidas a los circuitos, hemos preferido referirnos directamente a las hojas de dato del fabricante.
Figura 1: (Figura 2-1, página 22 del manual Intel EXPLR1 Embedded PC Evaluation Platform Board Manual)
Figura 2: (Figura 5.1 , página 51 del manual Intel EXPLR1 Embedded PC Evaluation Platform Board Manual)
Figura 3: (Figura 5.2 , página 52 del manual Intel EXPLR1 Embedded PC Evaluation Platform Board Manual)
Vale aclarar que las figuras corresponden a un hardware Embebido conteniendo un microprocesador de la Familia Intel modelo 386SX. Podrían haberse seleccionado otras configuraciones, pero hemos decidido mostrar aquí sólo la más económica.
6. Diagrama de Interconexión de Componentes
Adjuntamos a continuación algunas figuras que muestran posibles distribuciones de los componentes sobre una placa base o motherboard, recordar que esto es función de la marca y modelo, por lo que nos limitaremos a mostrar algunas de ellas.
7. Diagramas de Interconexionado
NOTA: recordemos que el hardware del proyecto puede depender de la versión del microprocesador utilizado (siempre y cuando, obviamente, pertenezca a la familia Intel IA32). Por cuestiones de simplicidad hemos elegido mostrar los diagramas pertenecientes al interconexionado de un microprocesador 80386.
Estos diagramas esquemáticos han sido obtenidos del sitio oficial de Intel Co.
Veamos algunos dibujos de frente, posterior y de la placa base de una PC convencional donde corre el sistema operativo Routix.
9. Lista de Componentes
A continuación se detallan los componentes principales de hardware necesarios para llevar a cabo el proyecto.
1. Procesador Intel de la familia IA32
2. Placa base (Motherboard, Intel Compatible.)
3. 8259 Controlador programable de Interrupciones (PIC)
4. 8254 Timer Tic.
5. 82077AA Controlador de disquetera (Single-Chip Floppy Disk Controller)
6. Disquetera3 ½ (Floppy Drive)
7. 8452 Controlador de Teclado (Keyboard Controller)
8. 8237ª Controlador de DMA
Se adjuntan hojas de datos de una familia de fabricantes. Si bien existe una gran cantidad de fabricantes para los periféricos (8259, 8254, 8452, 84077AA) todos cumplen con un standar predefinido, por lo que la única diferencia es el costo y la calidad técnica de los manuales, a nivel funcional son exactamente iguales.
Si bien el desarrollo del sistema de archivos FAT a sido nuestro, nos hemos basado en un standar ya existente, por lo que también incluimos la especificación FAT de Microsoft Co. Denominada: “Microsoft Extensible Firmware Initiative FAT32 File System Specification”
10. Cálculo de Confibilidad en los circuitos y programas de Computación
Definición de Confiabilidad
Es la probabilidad que tiene un elemento de funcionar correctamente durante un determinado período de tiempo bajo ciertas condiciones de funcionamiento.
Debemos tener en cuenta los siguientes aspectos al momento de tratar con la confibilidad:
Al representar una entidad probabilística su valor varía entre 0 (cero) y 1 (uno).
Se debe precisar el tiempo de funcionamiento esperado, sobre el que se estima la confiabilidad.
Debe haber una correcta especificación de los requerimientos del elemento, para poder determinar con exactitud si presenta o no una falla.
Las condiciones de funcionamiento deben ser especificadas en detalle, ya que de ellas depende fuertemente la confibilidad resultante.
Concepto de Falla
Se produce una falla cuando el elemento o sistema no es capaz de cumplir adecuadamente lo indicado por las especificaciones.
Podemos clasificarlas dentro de los siguientes grupos:
1. Fallas paramétricas2. Fallas aleatorias3. Fallas por desgaste
Fallas paramétricas
También se las denomina por degradación y se producen en los elementos o equipos electrónicos cuando una característica o parámetro empieza a degradarse, disminuyendo su prestación hasta quedar por debajo de lo especificado y convertirse en falla.Dependen parcialmente del tiempo y por lo tanto pueden prevenirse. Suelen producirse por corrosión o contaminación con influencia de condiciones ambientales.
Fallas aleatorias
Pueden lograr la destrucción total de un elemento. Aparecen súbitamente, sin aviso previo, y sin que sea posible predecir su ocurrencia individual. Pueden traer aparejado una disminución en las prestaciones hasta la destrucción total del equipo.
A diferencia de las fallas paramétricas, no presentan dependencia temporal, pero si puede hablarse de probabilidades en grandes números.
Fallas por desgaste
Se deben al desgaste de un elemento por su propio uso, generalmente son de caracter mecánico y de fuerte dependencia temporal, siempre y cuando se haga un uso adecuado del mismo dentro de las especificaciones dadas, o sea, si por ejemplo se trabaja un transistor la mayor parte del tiempo casi al 100% de la potencia máxima que puede disipar, su probabilidad de fallas por desgaste crece drásticamente. (De hecho la vida útil de un semiconductor aumenta al doble por cada 8°C que disminuimos de la temperatura máxima de juntura).
Confiabilidad del hardware
En nuestro proyecto particularmente el hardware utilizado sirvió como base para el desarrollo y funcionamiento del Sistema Operativo Routix. La línea de microprocesadores utilizados fue Intel y por tanto la confiabilidad del microprocesador varía de en cada línea de los mismos soportada, y es función de la marca de motherboard utilizada:
Intel Pentium Pro Intel Pentium MMX Intel Pentium II Intel Pentium III Intel Pentium IV
Confiabilidad del Software
Se define confiabilidad del software como la probabilidad de que el mismo se ejecute correctamente, de acuerdo a especificaciones dada, y para un determinado período. Involucra el código y el entorno donde se esta ejecutando.
La confiabilidad de un software está integramente relacionada con errores de programación, los cuales clasificaremos en la sección siguiente.
Clasificación de los errores
Errores previos fijos:Son aquellos que subsisten luego que se ha trabajado en el debug del software o cambiando el código.
GeneradosAquellos que se producen como consecuencia de un error involuntario al modificar una parte determinada de código, ya sea al intentar erradicar un ‘bug’ o agregar una nueva funcionalidad.
Es fundamental que el programador realice un análisis detallado de su código al terminar un módulo del mismo, o sea, conviene dividir el análisis de errores por bloques de código asociados a una determinada funcionalidad. En nuestro caso, esta división se realizó de la siguiente manera:
Módulo de Arranque
Módulo de Scheduler
Módulo de Manejo de memoria Modo Kernel
Modo usuario
Módulo de drivers
Video
Teclado
Entrada-Salida (I/O)
Floppy
FAT12
Timers (8254)
Módulo de Timers
Módulo de Señales
Módulo de Llamadas al Sistema (syscalls)
Definición Matemática
Basemos en el siguiente gráfico para entender como interactúa un sistema respecto a sus entradas y como genera sus salidas:
Dado un conjunto de entradas posibles:
y un grupo de salidas:
SOFTWARESOFTWAREEntradaEi
SalidaF(Ei)
Y entonces fácilmente podemos decir que falla cuando:
Debido a que existen errores (bugs) producidos en la etapa de diseño y en la etapa de desarrollo del software.
El conjunto de entradas tiene asociado probabilidades que denominaremos Pi, y por tanto podemos generalizar la siguiente expresión:
Siendo Mi nulo cuando existe un error y de valor unitario en caso de haberlo.
y considerando las entradas independientes:
Y se puede determinar el valor de C(1) realizando varias corridas, y mediante la siguiente expresión:
siendo nf el número de fallas y n el número total de corridas.
Para el sistema operativo hemos realizado, mediante automatizaciones y chequeos de diversos tipos la cantidad de 5000 pruebas fallando 15 veces, las cuales luego de un análisis pudimos reducir a 0.
Por lo tanto podemos determinar el valor de la confiabilidad:
Predicción de la confiabilidad
Los modelos de confiabilidad no son simples y no hay uno que se utilice por sobre los demás.
La precisión puede variar bastante para un mismo programa en función de las entradas, pero no puede determinarse desde el momento inicial cual será el mejor grupo de ellas a aplicar.
Debido a esta problemática se han desarrollado técnicas que permiten evaluar el error en la estimación de confiabilidad de los distintos métodos.
Macromodelo de Musa
Se utiliza para estimar la confiabilidad, se dice que es un macromodelo por que no tiene en cuenta la estructura interna del software, se basa en conteo de errores.
La hipótesis que se asume es que todas las falla en el software son debidas a errores residuales o remanentes, en consecuencia la tasa de fallas es proporcional al número de errores remanentes en el programa.
Se parte de la tasa de fallas:
y entonces la probabilidad de operación correcta será:
y el tiempo medio entre fallas (MTTF) será:
considerando una tasa de remoción de errores constante,
el MTTF queda:
con:
una vez definido esto resta comentar que para la estimación de los coeficientes del macromodelo pueden usarse los siguientes métodos:
a. Estimación por Máxima verosimilitudb. Estimación por momentosc. Estimación por cuadrados mínimos
Estas 3 técnicas se basan en los tiempos del proceso de Debbuging, el computo del tiempo total de horas de ejecución (H), el numero de corridas exitosas y fallidas para luego estimar ET y K.
Las técnicas de máxima verosimilitud y mínimos cuadrados requieren conocer en formas mas precisa en conjunto de datos T1,T2,T3,..,Tn-r y t1,t2,..tn-r. Obteniéndose mas precisión que la estimación por momentos.
En nuestro caso, según las pruebas realizadas tenemos:
Corridas exitosas = n-r
donde n:corridas del programa de las cuales falla r veces.
Tiempos de funcionamiento sin falla: T1,T2,T3,...,Tn-r
Tiempos de funcionamiento hasta la falla: t1,t2,t3,...,tr
Entonces las horas totales de funcionamiento serán:
y la tasa de fallas será:
y su inversa el MTTF
En nuestro caso, conociendo IT (cantidad de instrucciones en lenguaje máquina).
IT=300000
Con un tiempo de ejecución H=100 horas y realizando ET=5000 corridas.
Estimación por momentos
Consiste en realizar una prueba funcional en dos tiempos de depurado diferentes td1 y td2, con td1 < td2 y ec(td1) < ec(td2), es decir, se corrigieron errores entre estos dos tiempos.
Para cada intervalo debemos computar el tiempo total de ejecución, la cantidad de corridas exitosas y las fallidas; con esto podemos estimar el MTTF:
y de este sistema de ecuaciones se puede estimar K y Et,
y en nuestro caso será:
Et=49
K=172.45
Estimación por cuadrados mínimos
Utilizando la siguiente ecuación:
o expresada mediante la ecuación de la recta:
Estimaremos los parámetros de la recta en base a las muestras, utilizando mínimos cuadrados (minimización del error cuadrático medio).
tdi ec(tdi) n r λ MTTFhoras errores ejecuciones fallas falla/hora hora/falla
y graficando la tasa de fallas en función del tiempo de ejecución:
Tasa de fallas
0,002,004,006,008,00
10,0012,0014,0016,0018,00
1 2 3 5 10 15 20 30 50 70 100
horas
falla
s po
r hor
a
11. Instrucciones de puesta en marcha, ensayo funcional, ajuste y calibración, registros.
Existen dos formas de poner en marcha el proyecto.
Directamente sobre una PC Corriéndolo sobre un emulador de PC (nosotros elegimos el Bochs
Emulator, pero podría haber sido el VmWare, VirtualPC, etc.).
Sea cuál sea la forma elegida como primer paso debe tenerse la distribución del mismo en el archivo denominado routix.zip que puede ser descargado desde la página Web routix.sf.net. También pueden obtenerse las fuentes, para que cualquiera pueda entender su funcionamiento, corregir errores o adaptarlo a tareas personales.
Deberá descomprimirse utilizando la herramienta WinZip o Winrar. Luego de realizada la descompresión, deberá ejecutar el programa “Install.exe”, el cuál lo guiará por pasos muy simples para poder instalar routix en un disquetera booteable.
Una vez instalado, simplemente se deberá reiniciar la máquina con el disquete colocado en la disquetera, con lo que iniciará el sistema, dejándolo en el shell de routix para que pueda introducir comandos.
A continuación se detalla el proceso para modificar el código de programa, cosa que no es recomendada excepto que se tengan los conocimientos suficientes del microprocesador y de arquitectura elegida para el proyecto.
Para el desarrollo se recomienda el siguiente software
Software recomendado.
Bochs Emulator DJGPP Nasm Winimage
Obtención de los Fuentes
La obtención de la última versión disponible vía CVS se encuentra en SourceForge. Allí mismo se detalla el método de acceso: Project: Routix OS: CVS
No se trata de una mera maquina virtual sino más bien de un autentico emulador PC de tipo 386, 486, Pentium, Pentium Pro o AMD64, incluyendo opciones como MMX, SSE, SSE2 y hasta instrucciones 3DNow, capaz de emular la CPU, la BIOS y cargar la imagen de cualquier sistema operativo, sea éste último Windows XP o cualquiera de las distribuciones GNU/Linux disponibles en el mercado
Por lo que podremos ejecutar, por ejemplo, un sistema tipo Windows XP desde una ventana de nuestro GNU/Linux Mandrake, con el propósito de ir probando programas Windows XP sin renunciar a la comodidad de nuestro GNU/Linux. ¿Eres usuario Windows y quieres probar Linux sin cargarte al Windows? Pues, creas un PC virtual y montas una imagen GNU/Linux, sea cual sea la distribución. Brinda soporte 3DNow, SSE, SSE2, PNI(SSE3), imágenes de disco en el formato Vmware3 y la emulación VGA.
Basado en Wintel, se trata de un proyecto multiplataforma y totalmente open source, por lo que podremos probarlo de forma gratuita y hasta disfrutar de una pequeña imagen del Linux Mandrake. Es el programa ideal tanto para los programadores y desarrolladores de utilidades como usuarios Windows que quieran darse el primer chapuzón con GNu/Linux, pasando por un suave artificio: Bochs.
Una vez que tenemos a Bochs configurado podemos bajar el archivo Routix- Bochs.zip el cuál nos simplificará la configuración del emulador para correr Routix. Como verán, dentro de Routix-Bochs.zip se encuentra un directorio, el cuál deberá ser colocado dentro del dir donde se instaló a Bochs (el cual denominaremos Bochs_PATH). Solo basta ejecutar el bochsrc.bxrc para poder tener a Routix corriendo.
GCC
Sólo daremos una breve descripción sobre este compilador, podrá encontrar el resto de la información en gcc.gnu.org
GCC es un compilador integrado del proyecto GNU para C, C++, Objective C y Fortran; es capaz de recibir un programa fuente en cualquiera de estos lenguajes y generar un programa ejecutable binario en el lenguaje de la máquina donde ha de correr. La sigla GCC significa "GNU Compiler Collection". Originalmente significaba "GNU C Compiler"; todavía se usa GCC para designar una compilación en C. G++ refiere a una compilación en C++. Como curiosidad, mencionar que en realidad gcc no genera código binario alguno, sino código ensamblado. La fase de ensamblado a código binario la realiza el ensamblador de GNU (gas), y el enlazado de los objetos resultantes, el enlazador de GNU (ld). Este proceso es transparente para el usuario, ya que a no ser que se lo
especifiquemos, gcc realiza el paso desde código en C a un binario ejecutable automáticamente.
Winimage
Este software es utilizado para colocar dentro de la imagen routix.img contenida en Bochs_PATH/routix los archivos que uno desee, sean o no ejecutables. Lo que nos permite hacer Wininage es crear discos crudos (o raw disks) los cuales pueden tener dentro de sí el binario conteniendo el kernel, listo para ejecutar. No queremos extendernos en todas las opciones y capacidades de este software, el cuál al poseer interfaz gráfica es muy intuitivo y fácil de utilizar.
Fuentes
El kernel de Routix se encuentra contenido en el archivo kernel.bin, el cuál es compilado al tipear “make” dentro del directorio base de Routix (suponiendo que djgpp y nasm están perfectamente configurados). En “src” se encuentran los fuentes del kernel mismo, mientras que en tareas pueden encontrar los fuentes de algunos programas que corren en modo usuario, incluido el Shell2 (utilizado actualmente por nosotros para realizar pruebas). En el directorio “Template” dentro de tareas, se encuentra un ejemplo de cómo debe realizarse un programa en routix. Allí se incluye el script de LD necesario para generar un COFF32 compatible con Routix.
12. Ensayos de aceptación y registro
El sistema operativo ha sido probado en la totalidad de los aspectos respecto a las características propias (features como más se lo conoce en la jerga técnica).
Las primeras pruebas apuntaron al testeo de un inicio coherente y sin problemas, se buscaron “pérdidas de memoria” (memory leaks), segmentos de datos sin inicializar, excepciones mal trapeadas, errores en el sistema de páginación inicial.
Luego de esta exhaustiva prueba, y habiendo finalizado los drivers de teclado y video, se procedió a el ingreso de información a una velocidad mayor que el promedio de tipeo (entre 50 y 100 ms entre cada presión de tecla) para intentar lograr desbordar los buffers internos del sistema, tarea que arrojo los resultados esperados, y que permitió redimensionar el buffer de modo de evitar errores. Posteriormente se realizaron programas de testeo apuntando a detectar desbordes y errores en el driver de video, llevando las condiciones de testeo al límite tuvo un excelente respuesta y nos permitió realizar muchos ajustes (funciones de borrado de pantalla, de impresión de caracteres y strings como printf).
El siguiente ensayo fue sobre el gestionador de memoria, sobre quien se realizaron cientos de pruebas en búsqueda de un error que proporcionara o fuera indicador de una futura inestabilidad del sistema, encontramos sobre este muchos errores, y tuvimos que implementar un wrapper que permitiera disminuir los tiempos de debugging ya que el alocador de memoria era usado desde casi todo el ancho del sistema operativo.
El paso siguiente fue el testeo de los timers, para ello se pusieron a correr numerosa cantidad de procesos en paralelo generando cada uno de ellos llamadas a sistema que generaban instancias de timers, vimos aquí algunas posibilidades de optimización en las estructuras de ordenación de los mismos, y las aprovechamos mejorando la performance considerablemente.
Paso siguiente fue el testeo del driver de floppy, ya que luego de haber escrito su segunda versión con soporte asincrónico (el cual permite una utilización más eficiente de los recursos del sistema como Cpu) presentó muchos cambios y trajo aparejado ciertos errores que desaparecieron luego de esta etapa.
Luego fue el turno de las llamadas a sistema que permiten crear múltiples procesos como por ejemplo fork(), allí pudimos gracias a los testeos detectar algunos errores más que tenían que ver con la asignación de los directorios de páginas e índices del sistema de páginación.
Finalmente nos centramos en el testeo de la parte IPC del kernel, particularmente el intercambio de señales entre procesos, donde se arrojaron algunas excepciones y finalmente, luego de un intensivo análisis, pudieron corregirse exitosamente.
Como comentario final, podemos decir que la versión final de Routix testeada cumple con las expectativas esperadas tanto en estabilidad, confiabilidad y performance.
13. Instrucciones de instalación y operación.
Una vez que se ha obtenido el archivo denominado routix.zip, se está listo para hincar la instalación. Hemos agregado a la distribución un instalador el cuál simplifica las tareas de formateo del disquete sobre el cuál deseamos trabajar. También, este instalador es el encargado de copiar en el boot sector al loador y al archivo binario del kernel. La ejecución es realmente muy simple para cualquiera que ya haya instalado en una computadora algún software. Sólo deberá posicionarse sobre el icono de instalar.exe.
NOTA: El instalador sólo corre sobre plataforma Windows, ya sea 95-98/2000-XP. Hemos considerado que cualquier persona que posea conocimientos de Linux/Unix no encontrará inconveniente alguno en copiar la imagen en el boot sector de un disquete.
Existen unos requerimientos mínimos que deben suplirse para poder instalar Routix:
1. Procesador compatible familia Intel IA32.
2. 4MB Memoria RAM
3. Disquetera con capacidad de auto arranque 5 ¼ o 3 ½
4. Controlar VGA compatible o Superior
A continuación detallamos los pasos para dejar a Routix corriendo.
1. Colocar un disquete vacío (en caso de no estarlo se perderá el contenido del mismo).
2. Ejecutar el instalador (instalar.exe)
3. Seleccionar el drive o disquetera de destino.
4. Una vez finalizada la instalación, ya se encuentra en condiciones de reinar su PC.
5. Deberá asegurarse que su BIOS le permita arrancar desde el disquete, en caso de no saberlo deberá seguir por el paso 6, en caso de estar seguro o saber como configurar a su PC para que arranque desde disquete salte al paso número 12.
6. Al reiniciar la máquina presione la tecla DELETE para ingresar en el BIOS.
7. Desplazarse con las flechas y ubicarse sobre las opciones avanzadas (Advanced options). Presionar ENTER.
8. Una vez allí, deberá posicionarse sobre la opción de “Primera opción de Arranque”.
9. Con las flechas derecha e izquierda deberá moverse hasta encontrar la palabra “Floppy Drive”.
10.Una vez hecho esto debe presionar ESCAPE.
11.El sistema le consultará sobre si desea grabar los cambios, a lo que usted contestará: “salir guardando los cambios”. En caso de que haya modificado erróneamente alguna opción no debida, elija “Salir sin guardar cambios”.
12.En este punto usted está listo para iniciar Routix desde su disquete. Al iniciar la PC la luz de la disquetera se encenderá indicando que se está realizando la búsqueda en el sector de arranque.
13.En caso de haber realizado hasta aquí todos los pasos correctamente verá la inicialización del sistema.
14.Una vez detectados y configurados los periféricos el sistema lo dejará parado en un SHELL, en el cuál está listo para ejecutar comandos.
Imagen que muestra como se ve la pantalla luego de arrancar exitosamente Routix.
Clear: borra la pantalla
Exec: ejecuta una tarea de usuario, la cuál deberá encontrarse en formato COFF32.
Echo: imprime un texto con echo.
Ps: Process Status. Muestra información referida a los procesos que se encuentran en ejecución.
Timers: Muestra información acerca de los timers que se encuentran en ejecución.
Read: lee un sector del disquete.
kill: envía una señal a un proceso.
Info: brinda información detallada del proceso seleccionado.
Renice: modifica la prioridad de un proceso.
free mem: Muestra en pantalla la cantidad de páginas de memoria disponibles
show morecores: muestra la cantidad de morecores realizados hasta el momento. Un Morecore es realizado cada vez que la fragmentación interna de una página de memoria no posibilita una asignación vía malloc.
show cache: muestra en pantalla los sectores que se encuentran en el sistema de caché.
show zombies: muestra los procesos zombies que posee el sistema.
Setvar: asigna un valor a una variable.
Getvar: obtiene el valor d euna variable.
Información obtenida luego de ejecutar el comando “info”.
Se ha solicitado información sobre el proceso el cuál posee PID 2, en este caso el SHELL. Se ve en la imagen la dirección de el directorio de páginas del proceso, la ubicación del stack de kernel, del stack de usuario, y las direcciones de las páginas de ´codigo y datos de la tarea. Se muestran tanto las direcciones físicas como las lógicas (paginadas).
En este caso se ha ejecutado un “show cache”, mostrando por pantalla que sectores de disco han sido “cacheados” y cuantas páginas de memoria ocupa.
A continuación mostramos como se ve por pantalla la ejecución de un comando erróneo, y cuál es la visualización tipo de la ejecución de un comando exitoso.
Finalmente mostramos la ejecución exitosa de los comandos getvar y setvar.
14. Mantenimiento y diagnóstico de fallas
Para poder realizar de forma amena el mantenimiento y diagnóstico de fallas, realizaremos un cuadro de síntoma, falla y solución muy típico en productos de todo tipo.
Síntoma de la falla Causa posible Solución
Routix no arranca Disco flexible no insertado Coloque el disco en la ranura de la disquetera y reinicie el equipo
La BIOS no tiene como dispositivo de booteo la unidad de disco removible
Reinicie la máquina accediendo al menú de la BIOS de su equipo y agregue la opción de "Booteo desde unidad Removible"
Routix no está instalado en el disquete
Siga las instrucciones de la sección "Instalación" de este mismo manual
Disco defectuosoCambielo por uno nuevo y siga las instrucciones de la sección "Instalación" de este mismo manual
Se interrumpe la cuenta de memoria
Uno de los bancos de memoria tiene problemas Reemplace el banco defectuoso.
Al intentar ejecutar un proceso, el sistema arroja el error "No such file or directory"
No existe el binario en el disquete
Compile e Instale el programa a ejecutar tal como se especifica en la sección de "Instalación"
Al ejecutar el comando "clear" no se borra la pantalla
Probablemente su placa de video tenga algún problema, ya que el driver utilizado por routix es genérico y corre en cualquier modelo de cualquier placa.
Verifique el correcto funcionamiento de su placa de video.
Luego de ejecutar un proceso deseo finalizarlo abruptamente El proceso no a concluído aún.
Ejecute el comando "kill" con el número de proceso a finalizar. Esto envía la señal SIGKILL que finaliza un proceso y dealoca los recursos del sistema por el tomados.
La primera vez que cargo un programa tarda más tiempo que las posteriores.
Esta actuando el cache de bloques del sistema.
La primera vez que se lee de disco se accede al dispositivo magnético leyendo de a bloques de 512bytes, devolviendo esta información al proceso y a su vez almacenándola en un cache interno, de modo que la próxima petición sobre los mismos sectores será servida desde el cache y no de disco, aumentando sustancialmente la performance del sistema.
Luego de la carga de un archivo la luz de la disquetera permanece encendida durante algún tiempo.
Funcionamiento normal del sistema.
Cada vez que se realiza una lectura desde disco, debe a nivel mecánico encenderse el motor que permite mover al mismo para realizar la lectura, luego de encencer el motor debe esperarse unos 500 mseg aproximadamente hasta que alcance la velocidad de régimen permamente. Es por esto que una vez concluida la lectura de un sector el sistema operativo crea un timer para que se apague el motor luego de 2 segundos de inactividad, de modo que si antes de dicho período se continúa leyendo información no habrá que esperar alcanzar el régimen permamente del motor.
El shell tarda en procesar las teclas Un proceso esta monopolizando el uso de cpu
Verifique mediante el comando "ps" que ningún proceso este tomando excesivo tiempo de cpu, si detecta esta condición y no es lo que esta esperando realice un kill del mismo.
15. Cálculo de costos y estudio del mercado para su comercialización.
Como todo desarrollo de software el principal componente es el valor agregado que le da cada programador, incluyendo además el conocimiento de arquitecturas necesarias para lograr un diseño flexible y sólido, altamente escalable y disponible.
Análisis de Costos
Antes de comenzar con el análisis puntual para nuestro proyecto sería bueno clarificar como se define el precio de un producto, para ello deberemos realizar algunas definiciones.
El costo de producción de una empresa puede subdividirse en los siguientes elementos: alquileres, salarios y jornales, la depreciación de los bienes de capital (maquinaría y equipo, etc.), el costo de la materia prima, los intereses sobre el capital de operaciones, seguros, contribuciones y otros gastos misceláneos. Los diferentes tipos de costos pueden agruparse en dos categorías: costos fijos y costos variables.
Costos fijos
Los costos fijos son aquellos en que necesariamente tiene que incurrir la empresa al iniciar sus operaciones. Se definen como costos porque en el plazo corto e intermedio se mantienen constantes a los diferentes niveles de producción. Como ejemplo de estos costos fijos se identifican los salarios de ejecutivos, los alquileres, los intereses, las primas de seguro, la depreciación de la maquinaria y el equipo y las contribuciones sobre la propiedad.
El costo fijo total se mantendrá constante a los diferentes niveles de producción mientras la empresa se desenvuelva dentro de los límites de su capacidad productiva inicial. La empresa comienza las operaciones con una capacidad productiva que estará determinada por la planta, el equipo, la maquinaria inicial y el factor gerencial. Estos son los elementos esenciales de los costos fijos al comienzo de las operaciones.
Hay que dejar claro, que los costos fijos pueden llegar a aumentar, obviamente si la empresa decide aumentar su capacidad productiva, cosa que normalmente se logra a largo plazo, por esta razón, el concepto costo fijo debe entenderse en términos de aquellos costos que se mantienen constantes dentro de un período de tiempo relativamente corto.
Costos Variables
Los costos variables son aquellos que varían al variar el volumen de producción. El costo variable total se mueve en la misma dirección del nivel de producción. El costo de la materia prima y el costo de la mano de obra son los elementos más importantes del costo variable.
La decisión de aumentar el nivel de producción significa el uso de más materia prima y más obreros, por lo que el costo variable total tiende a aumentar la producción. Los costos variables son pues, aquellos que varían al variar la producción.
GASTOSADMINISTRATIVOS
GASTOS DEFÁBRICA
M.O. DIRECTA MATERIAPRIMADIRECTA
COSTODIRECTO
COSTO DEFABRICACIÓN
COSTO DEPRODUCCIÓN
Nuestro proyecto, al igual que la gran mayoría de los proyectos de software se basan en tener gran parte de su costo apoyado en los costos fijos. Es decir, posee una gran cantidad de horas hombre de investigación y desarrollo de un núcleo central. Una vez desarrollado el mismo, podría reutilizarse infinidad de veces sin necesidad de ser modificado.
Existen casos excepcionales donde el cliente podría necesitar una modificación, también denominada “customización”, en la que se pide hacer leves variaciones para personalizar ciertos aspectos de software.
Por excepción de estas “customizaciones”, las cuales momentáneamente dejaremos de lado a fin de simplificar los cálculos, sólo nos quedan los costos variables referidos al embalaje, impresión de manuales y distribución.
Para lograr darle un precio de mercado a nuestro proyecto debemos hacer consideraciones sobre cuantas unidades se venderán.
Costos Fijos:
1. Bibliografía: si bien la mayoría de los textos ya los teníamos antes de realizar el proyecto, consideremos el costo necesario para adquirirlos a fin involucrar estos valores en el precio final del producto.
1. The design of the Unix Operating System: Maurice Bach U$S 552. Operating Systems: Andrew Tanenbaum U$S 703. The Design and Implementation of the 4.4 BSD Operating System: Marshall Kirk McKusik U$S604. Undestanding the Linux Kernel: Daniel Bovet, Marco CesatiU$S425. Los microprocesadores Intel: B. Brey U$S 556. Microprocesadores Avanzados 386 y 486. Pentium: Angulo U$S
30
Total: 312 U$S
2. Computadoras: Ocurre exactamente lo mismo que con la bibliografía, si bien poseíamos todo el equipamiento necesario para realizar el desarrollo, consideraremos el costo de adquirir el equipamiento nuevamente. Consideramos aproximadamente unos U$S 2000 de cuatro equipos hogareños utilizables para nuestro desarrollo.
3. Mano de obra/horas hombre dedicadas: Si bien el desarrollo se ha realizado en dos años, en los cuales es difícil contabilizar cuanto tiempo se le ha dedicado con gran precisión, consideraremos aprox. Unas 1000 Hs de trabajo. Si valuamos cada hora de investigación, diseño y desarrollo en $20, estaremos alcanzando la cifra de $20.000
El Total de los costos fijos asciende aproximadamente a los $27.000, o su equivalente en dolares a cambio acual (1 U$S= $2.92), la cifra asciende a U$S 9300.
Como los productos de este tipo se venden por licencia, y por cada dispositivo embebido donde se colocará el sistema requiere una licencia, consideramos una venta la cuál podría ascender a las 100 licencias, por lo que el precio “final” podría aproximarse a los U$S93, sumados algunos gastos de embalaje, rondamos los U$S 100.
Es bastante evidente como disminuye el precio final a medida que aumentan las ventas, sobre todo en un producto de este tipo donde la mayor parte del costo se encuentra absorbido en los costos fijos.
Mercado
El mercado de los sistemas operativos crece día a día. En los años sesenta todos los sistemas eran operados por lotes o por tareas, es decir, debía terminar de ejecutarse una para comenzar a ejecutarse otra. Esto generaba retardos muy grandes debido a que la gran mayoría de las operaciones de ingreso/salida de datos desde/hacia el computador eran de tipo mecánicas. Es decir, mientras se realizaba una impresión vía matriz de puntos, el procesador quedaba en estado “idle”, es decir, esperando a que se termine de realizar la impresión, para ejecutar otra tarea. Este tiempo, era tiempo de procesamiento perdido, el cuál representaba una gran pérdida de dinero, sobre todo por lo costoso de los computadores de aquella época.
Con la aparición de Unix, los sistemas operativos multitarea fueron abriéndose paso, permitiendo ejecutar varios procesos “simultáneamente”, reduciendo en más de un 90% los tiempos muertos de los prehistóricos procesos por lotes.
Hoy en día, estos sistemas multitarea, son aplicados en una amplia variedad de campos, desde los electrodomesticos, hasta las plantas nucleares, pasando por telefonos celulares, satélites, computadoras hogareñas o automóviles.
Día a día son más los dispositivos que dejan de tener una lógica en hardware y pasan a tener un sistema operativo, el cuál es más simple de reemplazar, actualizar e incluso mucho más económico.
Nuestro sistema Routix es la base para un sistema operativo abocado al networking, pero con suficientes capacidades para poder ser escalable y extensible, pudiendo ser utilizada para aplicaciones embebidas. Nuestro sistema es fácilmente adaptable a las necesidades de muchos dispositivos de robótica, los cuales por cuestiones de tamaño no podrían utilizar un sistema operativo tan gran y voluminoso como Windows.
El mercado en el que entramos es en de los sistemas embebidos, donde los principales competidores son QNX y Windows CE. Estos sistemas tienen más
horas hombre de desarrollo e investigación por lo que nuestro sistema no competirá con ellos en capacidades de tiempo real, sólo en el control planificado de tareas y dispositivos.
Comparación
Costo por procesador (dispositivo en caso de embebidos)QNX: U$S 700Windows CE: U$S 500Routix: U$S100
Es evidente la gran diferencia de costo que existe entre una licencia de QNX y una de nuestro sistema Routix. Desde ya que por el momento nuestra intención no es competir en el mismo mercado que un sistema como QNX que lleva ya 24 años de desarrollo ininterrumpido.
Nuestra gran ventaja se encuentra en que si una Pyme planea vender 100 dispositivos que requieran control embebido, deberán adquirir 100 licencias. La diferencia es de 600 U$S por licencia, lo cuál en una producción mayorista de 100 dispositivos, la diferencia asciende a U$S 60.000.
16. Análisis de la planificación proyectada en la propuesta y la resultante real.
Nuestra proyección original fue la siguiente:
ACTIVIDAD DESCRIPCION ANTECESOR DURACIÓN1 Inicio - 02 Modulo Cache de Disco 1 03 Busqueda bibliografica 2 34 Analisis de funcionalidades 2 25 Estudio general y Diseño 3;4 66 Pseudocodificacion y Codificación 5 157 Pruebas de funcionamiento 6 48 Pruebas de performance 6 49 Documentacion del modulo 7;8 3
10 Modulo Driver Floppy 9 011 Busqueda bibliografica 10 312 Analisis de funcionalidades 10 213 Estudio general y Diseño 11;12 814 Pseudocodificacion y Codificación 13 1715 Pruebas de funcionamiento 14 416 Pruebas de performance 14 517 Documentacion del modulo 15;16 318 Modulo Llamada al sistema execve 1 019 Busqueda bibliografica 18 320 Analisis de funcionalidades 18 321 Estudio general y Diseño 19;20 322 Pseudocodificacion y Codificación 21 1523 Pruebas de funcionamiento 22 424 Pruebas de performance 22 425 Documentacion del modulo 23;24 326 Modulo Señales 25 027 Busqueda bibliografica 26 328 Analisis de funcionalidades 26 229 Estudio general y Diseño 27;28 730 Pseudocodificacion y Codificación 29 1831 Pruebas de funcionamiento 30 432 Pruebas de performance 30 433 Documentacion del modulo 31;32 334 Documentacion General del Proyecto 33;17 735 Fin 34 0
Particularmente tuvimos algunos desvíos en el transcurso del proyecto, el primero de ellos se dió en el módulo de Cache de Disco donde encontramos que las estructuras de datos involucradas en el mismo presentaban una complejidad importante y no permitían un interpretación rápida y amena del código, lo que nos llevó a analizar y desarrollar nuevas formas de manejo de
estas estructuras mediante macros apropiadas bajo la concepción de encapsulación y generalización, esto provoco un desvío de una semana adicional.
El segundo desvío que experimentamos se dió durante las pruebas de funcionamiento del módulo de floppy, donde aparecieron algunos bugs cuya procedencia nos era totalmente desconocida e identificable, con lo cual la detección y corrección de los mismos nos incremento la duración de 4 días a un total de 12 días.
Durante la etapa de Pruebas de Performance del módulo de Señales, encontramos que ante ciertas circunstancias particulares el desempeño en el envío de señales daba un poco más bajo de lo que habíamos pensado, lo cual llevó a realizar un análisis exhaustivo de todo el código del módulo a fin de encontrar posibles puntos de optimización, este trajo aparejado un desvío de 9 días adicionales.
Y finalmente, el mayor desvío lo sufrimos en la etapa de documentación general del proyecto, donde por problemas laborales nos vimos imposibilitados de concluir algunos puntos que nos faltaban del mismo.
17. Bibliografía y normativa utilizada.
Bibliografía
The design of the Unix Operating System: Maurice Bach Operating Systems: Andrew Tanenbaum The Design and Implementation of the 4.4 BSD Operating System:
Marshall Kirk McKusik Undestanding the Linux Kernel: Daniel Bovet, Marco Cesati Los microprocesadores Intel: B. Brey Microprocesadores Avanzados 386 y 486. Pentium: Angulo Intel Architecture Software Developer’s Manual, vol. 3: Intel Press GCC Manual: Richard Stallman Using ld - GNU linker: Steve Chamberlain Nasm Manual: Alexei A. Frounze How to write a COFF exec: DJ Delorie
Normativas
Single Unix Specification vol 2: http://www.opengroup.org POSIX (The Portable Operating System Interface): http://www.ieee.org FAT: http://www.microsoft.com/mscorp/ip/tech/fat.asp
Hemos además recurrido a los siguientes tutoriales/manuales online para adquirir los conocimientos necesarios para el desarrollo del proyecto:
Memory Managment 1: Tim Robinson http://www.osdever.net/tutorials/pdf/memory1.pdf
Memory Managment 2: Tim Robinson http://www.osdever.net/tutorials/pdf/memory2.pdf
00058 "popf"00059 : "=m" (retorno), "=m" (*candado)00060 : "r" (*candado));00061 00062 /* cli();00063 00064 __asm__ __volatile__ ("movl %0, %%ebx ; movl $1, %%eax ; xchg %%eax, (%%ebx)" : : "m" (candado) : "ebx");00065 sti();00066 */ 00067 return retorno; 00068 }00069 00070 /* Momentaneamente solo usadas por funciones de kernel */00071 00072 // Candado, utilizado para verificar si algun proceso está evaluando el contenido de "valor"00073 spinlock_t kernel_lock = 0;00074 00075 00076 //Funcion de bloqueo del semaforo (equivalente a down o wait)00077 void spin_lock (spinlock_t *valor)00078 {00079 while(1) {00080 // Esperar mientras algún proceso este dentro de spin_lock/spin_unlock00081 while (TestAndSet(&kernel_lock));00082 00083 if (*valor > 0) {00084 (*valor)--;00085 break;00086 }00087 kernel_lock = 0; 00088 }00089 kernel_lock = 0;00090 }00091 00092 00093 //Funcion de desbloqueo del semaforo (equivalente a up o signal)00094 void spin_unlock (spinlock_t *valor)00095 {00096 // Esperar mientras algún proceso este dentro de spin_lock/spin_unlock00097 while (TestAndSet(&kernel_lock));00098 (*valor)++;00099 kernel_lock = 0; 00100 }
blockcache.c00001 /* blockcache.c */00002 00003 00004 /* 00005 * El cache de bloques se encarga de interactuar entre los pedidos de los diferentes00006 * filesystems y los drivers determinados, siendo su principal función la de mantener00007 * un cache con los bloques más accedidos.00008 * Presenta una lista de bloques libres y un hash accedido por device y sector lógico00009 * que conduce a listas de bloques (sus headers más precisamente).00010 * Existe otra lista de bloques libres (sin uso y utilizados pero de menor a mayor00011 * tiempo de acceso LRU) donde puede haber bloques de listas pertenecientes al hash.00012 *00013 */00014 00015 #include <routix/device.h>00016 #include <routix/kalloc.h>00017 #include <routix/kstdio.h>00018 #include <routix/task.h>00019 #include <routix/system.h>00020 #include <fs/blockcache.h>00021 #include <sys/list.h>00022 #include <routix/atomic.h>00023 00024 #define RUNSTATS 100025 00026 00027 #define PAGE_SIZE 409600028 00029 #define MAXHASHENTRIES 1000030 #define HASH(maxhash,dev,sector) ( (dev+sector)%maxhash )00031 #define HASH_DEVICE(dev) (dev)00032 00033 #define MAKE_MULTIPLE(original,multiple) ((original)+((multiple)-(original)%(multiple)))00034 00035 // Creamos los headers para manejar las listas de bloques libres y de hash.00036 static LIST_NEW(block_cache_t) free_list;00037 static LIST_NEW(block_cache_t) cached_list[MAXHASHENTRIES];00038 static LIST_NEW(block_cache_t) drv_req_list[MAXDEVICEENTRIES];00039 00040 00041 // Esta función se encarga de prealocar los bloques y sus respectivos headers00042 // de acuerdo a la cantidad de memoria total del sistema, utiliza la definición00043 // de MAX_BLOCKCACHEMEM_PCT en system.h (dado en un valor porcentual).00044 // NOTA: por ahora esta definición la dejo acá.00045 //00046 // Máximo uso de memoria por parte del cache de bloques: 5%00047 #define MAX_BLOCKCACHE_MEM 500048
00049 00050 // Funciones locales00051 static inline byte *alloc_block();00052 static inline block_cache_t *alloc_header();00053 static inline block_cache_t *getfreeblock(void);00054 00055 // Funciones locales de debug00056 static void show_cached_list(void);00057 00058 00059 void start_block_cache(int totalmemory)00060 {00061 int blocks;00062 int tmp;00063 //int pagesforblocks,pagesforheaders;00064 00065 00066 // Inicializamos los headers de las listas free_list, cached_list y drv_req (drivers request)00067 LIST_INIT(free_list);00068 00069 for (tmp=0; tmp<MAXHASHENTRIES; tmp++) {00070 LIST_INIT(cached_list[tmp]);00071 }00072 00073 for (tmp=0; tmp<MAXDEVICEENTRIES; tmp++) {00074 LIST_INIT(drv_req_list[tmp]);00075 }00076 00077 // Transformamos la cantidad de memoria de Mb a bytes00078 totalmemory *= 1024*1024;00079 00080 // Calcalumos la cantidad de bloques (redondeando hacia abajo),00081 // pero teniendo en cuenta que un bloque posee 512 bytes entrarán00082 // 8 bloques por página, por lo tanto redondemos la cantidad de00083 // blocks de modo que sea múltiplo de 800084 blocks=totalmemory*MAX_BLOCKCACHE_MEM/(100*BLOCK_SIZE);00085 blocks=MAKE_MULTIPLE(blocks,PAGE_SIZE/BLOCK_SIZE);00086 00087 // Un bloque es de 512bytes, por lo tanto en una página entran 8 bloques00088 //pagesforblocks=blocks/8;00089 00090 // Oks, ahora veamos, la cantidad de headers es igual a la cantidad de bloques,00091 // pero su tamaño es de sizeof(buffer_block_t), o sea que necesitaremos:00092 // memoria_headers=blocks*sizeof(buffer_block_t)00093 // y en páginas de 4KB:00094 // memoria_headers_pag=roundup(memoria_headers/4096)00095 // y redondeamos para arriba00096 //pagesforheaders=blocks*sizeof(buffer_block_t)/4096 + ( (blocks*sizeof(buffer_block_t))%4096 ) ? 1 : 0;00097 kprintf("Inicializando Cache de Bloques con %d bloques\n",blocks);00098 byte *datablock;00099 block_cache_t *headerblock;00100
00158 return( LIST_FIRST(drv_req_list[HASH_DEVICE(device)]) );00159 }00160 00161 // El floppy nos devolvió el control, ahora debemos analizar su respuesta00162 inline void endrequest(block_cache_t *block)00163 {00164 // Lo eliminamos de la lista de request00165 LIST_DEL(drv_req_list[HASH_DEVICE( BLOCK_CACHE_DEVICE(block) )],driver_request_list,block);00166 00167 // despertamos al proceso que generó la solicitud (es el primero en la lista)00168 despertar_task( LIST_FIRST(BLOCK_CACHE_PENDING_PROCESS_LIST(block)) );00169 00170 }00171 00176 inline void sendrequest(block_cache_t *block)00177 {00178 // Agregamos el bloque a la lista de solicitudes correspondiente00179 // al dispositivo00180 LIST_ADD_TAIL(drv_req_list[ HASH_DEVICE( BLOCK_CACHE_DEVICE(block) ) ],driver_request_list,block);00181 00182 // Activa el flag que indica tarea para un determinado driver, por ahora no hace nada00183 00184 }00185 00186 00287 int cache_read(device_t device, word sector, unsigned int start, char *dstbuffer, unsigned int len)00288 {00289 00290 block_cache_t *tmp;00291 task_struct_t *proc_iterator;00292 00293 #ifdef RUNSTATS00294 long long counter;00295 00296 if ( getvar("debugcache") == 1 ) 00297 kprintf("cache_read: starting, dev: %d, sector: %d, start: %d buffer: 0x%x len: %d\n",device,sector,start,dstbuffer,len);00298 00299 if ( getvar("debugcache") == 2 )00300 rdtscl(counter);00301 #endif00302 00303 restart:00304 00305 if ( getvar("debugcache") == 1 )00306 kprintf("cache_read: buscando bloque en la lista de cache\n");00307 00308 START_ATOMIC_OPERATION;00309 00310 #ifdef RUNSTATS00311 if ( getvar("debugcache")==1 )
00312 show_cached_list();00313 #endif00314 00315 // Primero buscamos si ya existe el bloque en la lista del cache00316 LIST_FOREACH(tmp,cached_list[ HASH(MAXHASHENTRIES,device,sector) ], cached)00317 if ( BLOCK_CACHE_DEVICE(tmp)==device && BLOCK_CACHE_SECTOR(tmp)==sector )00318 break;00319 00320 // Está cacheado ?00321 if ( tmp ) {00322 00323 #ifdef RUNSTATS00324 if ( getvar("debugcache") == 1 )00325 kprintf("cache_read: cache hit !\n");00326 #endif00327 00328 // Esta tomado por alguien más00329 if ( BLOCK_CACHE_STATE(tmp)==WRITE_LOCKED || BLOCK_CACHE_STATE(tmp)==SYNCRONIZING ) {00330 00331 // Colocamos el proceso actual al final de la lista de procesos con IO pendiente (el primer00332 // nodo es el proceso original que solicitó la IO)00333 LIST_ADD_TAIL(BLOCK_CACHE_PENDING_PROCESS_LIST(tmp),io_pending,actual);00334 00335 00336 // Ponemos a dormir la tarea00337 dormir_task(actual);00338 00339 FINISH_ATOMIC_OPERATION;00340 00341 // Rescheduleamos00342 _reschedule();00343 00344 // Oks, en este punto alguien nos despertó, volvemos a intentar tomar el bloque00345 // (odio los goto ! :P pero en este momento es una salida rápida y permite no00346 // oscurecer el código.00347 goto restart;00348 }00349 00350 // Se encuentra disponible00351 else {00352 00353 // Si esta deslockeado lo lockeamos, inicializamos la cuenta de locks a cero00354 // y lo quitamos de la lista de bloques free00355 if ( BLOCK_CACHE_STATE(tmp)==UNLOCKED ) {00356 BLOCK_CACHE_STATE(tmp)=READ_LOCKED;00357 LIST_DEL(free_list,free,tmp);00358 BLOCK_CACHE_LOCKCOUNT(tmp)=0;
00359 }00360 #ifdef RUNSTATS00361 else {00362 if ( getvar("debugcache")==3 )00363 kprintf("Sector: %d relockeando!\n",sector);00364 //show_cached_list();00365 }00366 #endif00367 00368 }00369 00370 }00371 00372 // No está cacheado00373 else {00374 00375 #ifdef RUNSTATS00376 if ( getvar("debugcache") == 1 )00377 kprintf("cache_read: cache miss(%d) ",sector);00378 #endif00379 00380 00381 // Obtenemos un bloque de la lista free00382 tmp=getfreeblock();00383 00384 #ifdef RUNSTATS00385 if ( getvar("debugcache") == 1 )00386 kprintf("cache_read: bloque obtenido de la free list 0x%x\n",tmp);00387 00388 if ( getvar("debugcache") == 1 )00389 kprintf("cache_read: eliminamos este nuevo bloque de la free list\n");00390 #endif00391 00392 // lo marcamos en Sincronización00393 BLOCK_CACHE_STATE(tmp)=SYNCRONIZING;00394 00395 // Seteamos el dispositivo, sector y operacion00396 BLOCK_CACHE_DEVICE(tmp)=device;00397 BLOCK_CACHE_SECTOR(tmp)=sector;00398 BLOCK_CACHE_OPERATION(tmp)=IOREAD;00399 00400 00401 #ifdef RUNSTATS00402 if ( getvar("debugcache") == 1 ) {00403 kprintf("cache_read: lo LOCKEAMOS\n");00404 kprintf("cache_read: entrada numero %d en la cached list\n",HASH(MAXHASHENTRIES,device,sector));00405 }00406 #endif00407 00408 // y lo insertamos en la lista de bloques cacheados00409 LIST_ADD_TAIL(cached_list[ HASH(MAXHASHENTRIES,device,sector) ],cached,tmp);00410 00411 00412 #ifdef RUNSTATS
00413 if ( getvar("debugcache") == 1 )00414 kprintf("cache_read: y lo insertamos al final de la lista de bloques cacheados\n");00415 #endif00416 00417 // colocamos el proceso actual al principio de la lista de procesos con IO pendiente00418 LIST_ADD(BLOCK_CACHE_PENDING_PROCESS_LIST(tmp),io_pending,actual);00419 00420 #ifdef RUNSTATS00421 if ( getvar("debugcache") == 1 ) {00422 kprintf("cache_read: insertamos el proceso actual en la lista de procesos con io pendiente\n");00423 kprintf("cache_read: enviamos el request\n");00424 }00425 #endif00426 00427 // Realizamos el request, en este punto, sendrequest pondrá a dormir el00428 // proceso actual hasta que se complete la petición.00429 sendrequest(tmp);00430 00431 // Ponemos a dormir la tarea00432 dormir_task(actual);00433 00434 #ifdef RUNSTATS00435 if ( getvar("debugcache") == 1 )00436 kprintf("cache_read: nos dormimos\n");00437 #endif00438 00439 FINISH_ATOMIC_OPERATION;00440 00441 // Rescheduleamos00442 _reschedule();00443 00444 START_ATOMIC_OPERATION;00445 00446 #ifdef RUNSTATS00447 if ( getvar("debugcache") == 1 )00448 kprintf("cache_read: nos despertamos !\n");00449 #endif00450 00451 // lo quitamos de la lista de procesos io pending00452 LIST_DEL(BLOCK_CACHE_PENDING_PROCESS_LIST(tmp),io_pending,actual);00453 00454 // despertar al resto de los procesos en espera de este bloque00455 LIST_FOREACH(proc_iterator,BLOCK_CACHE_PENDING_PROCESS_LIST(tmp),io_pending) {00456 despertar_task(proc_iterator);00457 }00458 00459 // vaciar la lista de procesos en estado IO
00460 LIST_CLEAN(BLOCK_CACHE_PENDING_PROCESS_LIST(tmp));00461 00462 00463 // El dispositivo concluyó su trabajo, por lo tanto recuperamos el control en este punto.00464 //00465 // si falló debemos:00466 // 1. quitarlo de la lista de bloques cacheados00467 // 2. colocarlo al ppio de la lista de bloques libres00468 // 3. retornar error (-1)00469 if ( BLOCK_CACHE_RETURN(tmp) == -1 ) {00470 00471 LIST_DEL(cached_list[ HASH(MAXHASHENTRIES,device,sector) ],cached,tmp);00472 00473 LIST_ADD(free_list,free,tmp);00474 00475 FINISH_ATOMIC_OPERATION;00476 00477 return -1;00478 00479 }00480 00481 }00482 00483 // Incrementamos la cantidad de locks en él00484 BLOCK_CACHE_LOCKCOUNT(tmp)++;00485 00486 // lo marcamos READ_LOCKED00487 BLOCK_CACHE_STATE(tmp)=READ_LOCKED;00488 00489 FINISH_ATOMIC_OPERATION;00490 00491 char *origin = BLOCK_CACHE_BUFFER(tmp) + start;00492 00493 // Copiamos los datos leidos al buffer del proceso00494 while ( len-- )00495 *dstbuffer++ = *origin++;00496 00497 START_ATOMIC_OPERATION;00498 00499 // Decrementamos la cantidad de locks en él00500 BLOCK_CACHE_LOCKCOUNT(tmp)--;00501 00502 if ( BLOCK_CACHE_LOCKCOUNT(tmp)==0 ) {00503 00504 // Deslockeamos el bloque00505 BLOCK_CACHE_STATE(tmp)=UNLOCKED;00506 00507 // Lo colocamos al final de la free list00508 LIST_ADD_TAIL(free_list,free,tmp);00509 00510 // Despertamos todos los procesos que están esperando por el bloque00511 // y vaciamos la lista00512 LIST_FOREACH(proc_iterator,BLOCK_CACHE_PENDING_PROCESS_LIST(tmp),io_pending)
00054 unsigned short f_opthdr; /* sizeof(optional hdr) */00055 unsigned short f_flags; /* flags */00056 };00057 00058 // Valor de f_magic para un COFF3200059 #define COFF32_TYPE 0x14c00060 00061 struct coff_sections{00062 char s_name[8]; /* section name */00063 unsigned long s_paddr; /* physical address, aliased s_nlib */00064 unsigned long s_vaddr; /* virtual address */00065 unsigned long s_size; /* section size */00066 unsigned long s_scnptr; /* file ptr to raw data for section */00067 unsigned long s_relptr; /* file ptr to relocation */00068 unsigned long s_lnnoptr; /* file ptr to line numbers */00069 unsigned short s_nreloc; /* number of relocation entries */00070 unsigned short s_nlnno; /* number of line number entries */00071 unsigned long s_flags; /* flags */00072 } ;00073 00074 #define COFF32_TEXT 0x2000075 #define COFF32_DATA 0x4000076 #define COFF32_BSS 0x8000077
error.c 00001 00005 #include "routix/system.h"00006 #include "routix/task.h"00007 #include <routix/kstdio.h>00008 00009 00010 extern task_struct_t *actual;00011 00012 00013 char error_types[][55] = {00014 "OK",00015 "Operation not permitted",00016 "No such file or directory",00017 "No such process",00018 "Interrupted system call",00019 "I/O error", 00020 "No such device or address",00021 "Arg list too long",00022 "Exec format error",00023 "Bad file number",00024 "No child processes",00025 "Try again",00026 "Out of memory",00027 "Permission denied",00028 "Bad address",00029 "Block device required",00030 "Device or resource busy",00031 "File exists",00032 "Cross-device link",00033 "No such device",00034 "Not a directory",00035 "Is a directory",00036 "Invalid argument",00037 "File table overflow",00038 "Too many open files",00039 "Not a typewriter",00040 "Text file busy",00041 "File too large", 00042 "No space left on device",00043 "Illegal seek",00044 "Read-only file system",00045 "Too many links",00046 "Broken pipe",00047 "Math argument out of domain of func",00048 "Math result not representable",00049 "Resource deadlock would occur",00050 "File name too long",00051 "No record locks available",00052 "Function not implemented", 00053 "Directory not empty",00054 "Too many symbolic links encountered",00055 "Operation would block",00056 "No message of desired type",00057 "Identifier removed",00058 "Channel number out of range",00059 "Level 2 not synchronized",00060 "Level 3 halted",00061 "Level 3 reset",00062 "Link number out of range",
00063 "Protocol driver not attached",00064 "No CSI structure available",00065 "Level 2 halted",00066 "Invalid exchange",00067 "Invalid request descriptor",00068 "Exchange full",00069 "No anode",00070 "Invalid request code",00071 "Invalid slot",00072 "File locking deadlock error",00073 "Bad font file format",00074 "Device not a stream",00075 "No data available",00076 "Timer expired",00077 "Out of streams resources",00078 "Machine is not on the network",00079 "Package not installed",00080 "Object is remote",00081 "Link has been severed",00082 "Advertise error",00083 "Srmount error",00084 "Communication error on send",00085 "Protocol error",00086 "Multihop attempted",00087 "RFS specific error",00088 "Not a data message",00089 "Value too large for defined data type",00090 "Name not unique on network",00091 "File descriptor in bad state",00092 "Remote address changed",00093 "Can not access a needed shared library",00094 "Accessing a corrupted shared library",00095 ".lib section in a.out corrupted",00096 "Attempting to link in too many shared libraries",00097 "Cannot exec a shared library directly",00098 "Illegal byte sequence",00099 "Interrupted system call should be restarted",00100 "Streams pipe error",00101 "Too many users",00102 "Socket operation on non-socket",00103 "Destination address required",00104 "Message too long",00105 "Protocol wrong type for socket",00106 "Protocol not available",00107 "Protocol not supported",00108 "Socket type not supported",00109 "Operation not supported on transport endpoint",00110 "Protocol family not supported",00111 "Address family not supported by protocol",00112 "Address already in use",00113 "Cannot assign requested address",00114 "Network is down",00115 "Network is unreachable",00116 "Network dropped connection because of reset",00117 "Software caused connection abort",00118 "Connection reset by peer",00119 "No buffer space available",00120 "Transport endpoint is already connected",00121 "Transport endpoint is not connected",00122 "Cannot send after transport endpoint shutdown",00123 "Too many references: cannot splice",
00124 "Connection timed out",00125 "Connection refused",00126 "Host is down",00127 "No route to host",00128 "Operation already in progress",00129 "Operation now in progress",00130 "Stale NFS file handle",00131 "Structure needs cleaning",00132 "Not a XENIX named type file",00133 "No XENIX semaphores available",00134 "Is a named type file",00135 "Remote I/O error",00136 "Quota exceeded"00137 };00138 00139 00140 00141 void perror (char *str)00142 {00143 kprintf("%s: %s\n", str, error_types[actual->err_no]);00144 actual->err_no = 0; // Limpiar error de proceso00145 } 00146 00147 00148 void kpanic(char *str)00149 {00150 __asm__ __volatile__ ("cli");00151 00152 kprintf("\nKernel Panic: %s\n", str);00153 kprintf("Sistema detenido");00154 while(1);00155 }
00001 /* errno.h */00002 00003 /* agrego las que no encontre :-( a partir del numero 10000*/00004 00005 void perror (char *str);00006 00007 #ifndef _I386_ERRNO_H00008 #define _I386_ERRNO_H00009 00010 #define EPERM 1 /* Operation not permitted */00011 #define ENOENT 2 /* No such file or directory */00012 #define ESRCH 3 /* No such process */00013 #define EINTR 4 /* Interrupted system call */00014 #define EIO 5 /* I/O error */ //no se puede leer el sector00015 #define ENXIO 6 /* No such device or address */00016 #define E2BIG 7 /* Arg list too long */00017 #define ENOEXEC 8 /* Exec format error */00018 #define EBADF 9 /* Bad file number */00019 #define ECHILD 10 /* No child processes */00020 #define EAGAIN 11 /* Try again */00021 #define ENOMEM 12 /* Out of memory */00022 #define EACCES 13 /* Permission denied */00023 #define EFAULT 14 /* Bad address */00024 #define ENOTBLK 15 /* Block device required */00025 #define EBUSY 16 /* Device or resource busy */00026 #define EEXIST 17 /* File exists */00027 #define EXDEV 18 /* Cross-device link */00028 #define ENODEV 19 /* No such device */00029 #define ENOTDIR 20 /* Not a directory */00030 #define EISDIR 21 /* Is a directory */00031 #define EINVAL 22 /* Invalid argument */00032 #define ENFILE 23 /* File table overflow */00033 #define EMFILE 24 /* Too many open files */00034 #define ENOTTY 25 /* Not a typewriter */00035 #define ETXTBSY 26 /* Text file busy */00036 #define EFBIG 27 /* File too large */ //tarea muy grande00037 #define ENOSPC 28 /* No space left on device */00038 #define ESPIPE 29 /* Illegal seek */00039 #define EROFS 30 /* Read-only file system */00040 #define EMLINK 31 /* Too many links */00041 #define EPIPE 32 /* Broken pipe */00042 #define EDOM 33 /* Math argument out of domain of func */00043 #define ERANGE 34 /* Math result not representable */00044 #define EDEADLK 35 /* Resource deadlock would occur */00045 #define ENAMETOOLONG 36 /* File name too long */00046 #define ENOLCK 37 /* No record locks available */00047 #define ENOSYS 38 /* Function not implemented */ //llamada al sistema no valida00048 #define ENOTEMPTY 39 /* Directory not empty */00049 #define ELOOP 40 /* Too many symbolic links encountered */00050 #define EWOULDBLOCK EAGAIN /* Operation would block */00051 #define ENOMSG 42 /* No message of desired type */00052 #define EIDRM 43 /* Identifier removed */
00001 /* event.c */00002 00003 00004 #include "routix/event.h"00005 00006 // Puntero al inicio de la lista de eventos00007 event_t *event_inicio=NULL;00008 00009 int insertar_evento(event_t *nuevo)00010 {00011 event_t *tmp;00012 00013 if ( nuevo == NULL ) { return 0; }00014 00015 // Nos paramos al ppio de la lista00016 tmp = event_inicio;00017 00018 if ( event_inicio == NULL ) { event_inicio = nuevo; }00019 00020 else {00021 00022 // Buscamos la última tarea00023 for ( tmp = event_inicio; tmp->proximo != NULL ; tmp = tmp->proximo );00024 00025 // Nos colgamos de ella00026 tmp->proximo = nuevo;00027 }00028 00029 // La nueva tarea queda apuntando a NULL00030 nuevo->proximo = NULL;00031 00032 return 1;00033 }00034 00035 00036 // Función crítica, tengo que agregarle la deshabilitación de 00037 // interrupciones (bajo análisis)00038 int remover_evento(event_t *event)00039 {00040 event_t *tmp;00041 00042 // Es el primer event ?00043 if ( event == event_inicio ) {00044 event_inicio = event->proximo;00045 // Lo tengo que reemplazar por la constante correcta según la definición de errno.h00046 return 0;00047 }00048 00049 // Buscamos nuestro event entonces00050 for ( tmp=event_inicio; (tmp->proximo != event) && (tmp != NULL) ; tmp = tmp->proximo ) ;00051 00052 // Si no encontramos el event devolvemos error00053 if ( tmp == NULL ) {00054 // Lo tengo que reemplazar por la constante correcta según la definición de errno.h00055 return -1;
00001 /* floppy.h */00002 00003 #ifndef __SYSTEM00004 #include "routix/system.h"00005 #endif00006 00007 #ifndef __FAT1200008 #define __FAT1200009 00010 //Macros para simplificar el uso de la funcion leer_escribir00011 #define leer_sector(sec,buf) leer_escribir(READ_SECTOR,sec,buf)00012 #define escribir_sector(sec,buf) leer_escribir(WRITE_SECTOR,sec,buf)00013 00014 #define BOOTSECTOR 000015 00016 // Estructura utilizada para caracterizar una particion FAT00017 typedef struct dev_fat_t00018 {00019 word fat_size; //Cantidad de sectores que ocupa cada FAT00020 word fat_start; //Sector de comienzo de la FAT00021 byte cantidad_de_fats; //Cantidad de FATs por disco (geenralmente 2)00022 word root_dir_start; //Sector de comienzo del Directorio raiz00023 word root_dir_size; //Cantidad de sectores asignados al root00024 dword sectores_ocultos; 00025 word sectores_totales; //cantidad total de sectores del disco00026 byte sectores_por_cluster;00027 word sector_size;00028 byte fat_levantada;00029 byte boot_leido; //Indica si el BPB ha sido leido y los parametros han sido cargados en las variables00030 }dev_fat_t;00031 00032 00033 // Estructura que contiene los campos principales de un BootSector tanto en FAT12 como FAT16 (son los primeros 62 bytes)00034 typedef struct boot_sector_t00035 {00036 unsigned BS_jmpBott: 24;00037 qword BS_OEMName;00038 unsigned BPB_BytsPerSec: 16;00039 unsigned BPB_SecPerClus: 8; 00040 unsigned BPB_RsvdSecCnt: 16;00041 unsigned BPB_NumFATs: 8;00042 unsigned BPB_RootEntCnt: 16;00043 unsigned BPB_TotSec16: 16;00044 unsigned BPB_Media: 8;00045 unsigned BPB_FATSz16: 16;00046 unsigned BPB_SecPerTrk: 16;00047 unsigned BPB_NumHeads: 16;00048 unsigned BPB_HiddSec: 32;00049 unsigned BPB_TotSec32: 32;00050 unsigned BS_DrvNum: 8;00051 unsigned BS_Reserved1: 8;
00052 unsigned BS_BootSig: 8;00053 unsigned BS_VolID: 32;00054 char BS_VolLab[11];00055 char BS_FilSysType[8];00056 }boot_sector_t;00057 00058 00059 //Estos son los datos que identifican a cada archivo dentro de un directorio00060 typedef struct fat12_entry_t00061 {00062 byte nombre[8];00063 byte extension[3];00064 byte atributo;00065 byte reservado[10];00066 word hora;00067 word fecha;00068 word sector; //Cluster de comienzo del archivo (relativo al final del ultimo sector de ROOT00069 dword size; //Tamaño en bytes00070 }fat12_entry_t;00071 00072 //Esta estructura es reciente. La idea es que con *fat12_data recoja la info del directorio, y que mediante la funcion00073 //fat_adapta_name la escriba en formato string dentro de nombre.00074 #define MAX_PATH_LEN 2900075 00076 typedef struct fat12_entry_ext_t00077 {00078 fat12_entry_t fat12_data;00079 char nombre[MAX_PATH_LEN];00080 }fat12_entry_ext_t;00081 00082 struct floppy_cache00083 {00084 dword sector;00085 byte bloque[512];00086 struct floppy_cache *next;00087 00088 };00089 00090 //Valores que puede tomar el 1er byte del campo nombre ( estructura dir_entry_t)00091 //Cualquier otro valor, significa el valor del primer caracter del nombre de archivo00092 #define FAT_EMPTY 0 // Campo aun no usado (luego del formateo)00093 #define FAT_SUBDIR 0x2E // El archivo es un directorio00094 #define FAT_DELETED 0xE5 // El archivo fue borrado00095 00096 #define SECTOR_SIZE 51200097 00098 00099 // structura utlizada para generar una lista enlazada que apunta a bloques en memoria que contienen la FAT00100 // del floppy que se encuentra en la diskettera.00101 typedef struct fat_t00102 {00103 byte *bloque;00104 struct fat_t *next;00105 }fat_t;
00106 00107 00108 //Tipo usado para mantener una lista enlazada con los sectores de un archivo contenidos en memoria00109 /*00110 typedef struct file_loaded_t00111 {00112 byte *bloque; //Apunta a un sector00113 struct file_loaded_t *next;00114 00115 } file_loaded_t;00116 */00117 00118 int init_floppy_fs(void);00119 00120 // Adapta un nombre de la forma fat (KERNEL BIN) a formato string: KERNEL.BIN\000121 // asi es mas facil su comparacion, impresion y usos varios00122 void fat_adapta_name (byte *nombre_fat, byte *adaptado);00123 00124 // Recibe un nombre de archivo, y lo busca en el disco. En caso negativo retorna NULL00125 fat12_entry_t *fat_file_find (char *nombre, fat12_entry_t *datos_archivo);00126 00127 // Esta funcion recibe como parametro un numero de sector y mediante la FAT obtiene cual es el siguiente00128 int fat_next_sector ( dword sector_inicial );00129 00130 void *floppy_cache (dword sector);00131 00132 // Abrir un archivo desde un dispositivo con formato FAT1200133 int open_FAT12 (char *nombre, int fd);00134 00135 00136 #define DIR_FAT_VIRTUAL 0xF800000000137 00138 00139 #endif00140
00057 dev_fat[0].sector_size = p->BPB_BytsPerSec;00058 dev_fat[0].fat_start = 1; 00059 dev_fat[0].root_dir_start = 1 + dev_fat[0].sectores_ocultos + (dev_fat[0].cantidad_de_fats * dev_fat[0].fat_size);00060 00061 dev_fat[0].boot_leido=TRUE;00062 return OK;00063 }00064 00066 // Levanta todos los sectores correspondientes a la FAT y los coloca en la memoria00067 // Debera tenerse en cuenta que al cambiar el diskette, el flag "fat_levantada" debera volverse a FALSE00069 00070 fat_t fat_header; //Header de una lista enlazada que va apuntando a bloques en memoria de 512 (fragmentos de la FAT)00071 00072 int levantar_fat(void)00073 {00074 //CUIDADO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!00075 // malloc(100);00076 00077 //Cantidad de paginas que requiere la FAT (en gral para 9 sectores de FAT se requieren 2 paginas)00078 word fat_paginas = (dev_fat[0].fat_size * BLOQUE_SIZE) / PAGINA_SIZE;00079 00080 00081 //Contador para saber cuantos sectores de la fat se levantaron del diskette00082 word sec_fat_levantados=0;00083 word i, j;00084 00085 if ( dev_fat[0].boot_leido == FALSE ) {00086 kprintf("Debe levantarse el BOOT\n");00087 return ERR_NO_BOOT;00088 }00089 00090 if ( dev_fat[0].fat_levantada == TRUE ) {00091 return OK;00092 }00093 00094 00095 //Si la division (fat_size * BLOQUE_SIZE) / PAGINA_SIZE no da entera, redondear para arriba00096 if ( (dev_fat[0].fat_size * BLOQUE_SIZE) % PAGINA_SIZE)00097 fat_paginas++;00098 00099 00100 //Puntero a la lista enlazada de los bloques de FAT, inicializado a NULL en sus dos campos00101 fat_t *aux= &fat_header;00102 aux->bloque = NULL;00103 aux->next = NULL;00104 00105 for(i=0 ; i < fat_paginas ; i++) {00106 00107 //Obtener una pagina00108 if (!(aux->bloque = (byte *) kmalloc_page())) {00109 kprintf("No hay memoria disponible para levantar la FAT\n");
00110 return ERR_NOMEM;00111 }00112 00113 //Mapeo las paginas que contienen bloques en memoria virtual, para poder asi tenerlos en forma contigua00114 //(nadie me asegura que los bloques entregados por kmalloc_page sean contiguos)00115 kmapmem((addr_t) aux->bloque, DIR_FAT_VIRTUAL + (i * PAGINA_SIZE), KERNEL_PDT, PAGE_PRES | PAGE_SUPER | PAGE_RW); 00116 aux->bloque = (byte *) (DIR_FAT_VIRTUAL + (i * PAGINA_SIZE));00117 00118 //Hacer que cada nodo de la lista apunte a 512 bytes dentro de la pagina obtenida.00119 for(j=0;(j<((PAGINA_SIZE/BLOQUE_SIZE))) && (sec_fat_levantados<dev_fat[0].fat_size);j++, sec_fat_levantados++ ) {00120 //if (leer_sector(sec_fat_levantados + dev_fat[0].fat_start, aux->bloque)!=OK) {00121 if (CACHE_READ(fd0,sec_fat_levantados + dev_fat[0].fat_start, aux->bloque) == -1 ) {00122 kprintf("No se pudo levantar la FAT");00123 return -1;00124 }00125 00126 aux->next = (fat_t *) malloc( sizeof(fat_t) );00127 aux->next->bloque = aux->bloque + BLOQUE_SIZE;00128 aux = aux->next;00129 aux->next = NULL;00130 }00131 }00132 00133 //Levantar flag para que las funciones que requieran buscar algo en FAT, sepan que pueden hacerlo00134 dev_fat[0].fat_levantada = TRUE;00135 return OK;00136 }00137 00138 // **************************************************************************************************************00139 // Prepara el entorno (carga la FAT y el BPB) para que puedan ser utilizadas las demas funciones de acceso00140 // a disco00141 // **************************************************************************************************************00142 int init_floppy_fs(void)00143 {00144 dev_fat[0].fat_start = 1;00145 if ( (fat_12()!=OK) || (levantar_fat()!=OK) )00146 return -1;00147 00148 return OK;00149 } 00150 00151 00152 00153 // **************************************************************************************************************
00154 // Recibe un nombre en formato FAT ( KERNEL BIN ) y lo transforma a un formato tipo string: KERNEL.BIN\000155 // **************************************************************************************************************00156 void fat_adapta_name (byte *nombre_fat, byte *adaptado)00157 {00158 byte x=0,z;00159 strcpy(adaptado," ");00160 for( z=0 ; z<11 ; z++) {00161 if ( (z==8) && (nombre_fat[8]!=' ') ) {00162 adaptado[x]='.';00163 x++;00164 }00165 adaptado[x] = nombre_fat[z];00166 if (nombre_fat[z] != ' ')00167 x++;00168 }00169 adaptado[x]='\0';00170 00171 } 00172 00173 00174 00175 00177 //Esta funcion recibe un numero de sector (perteneciente a un archivo) y halla cual es el sector que le sigue.00178 //retorna el sector correspondiente, o LAST_SECTOR en caso de que el sector recibido ("sector") sea el ultimo.00180 int fat_next_sector ( dword sector )00181 {00182 //Si no se levanto la FAT del disco, no se puede hacer nada00183 if (dev_fat[0].fat_levantada==FALSE)00184 return ERR_NO_FAT;00185 00186 if (sector == 0xfff)00187 return ERR_LAST_SECTOR;00188 00189 sector -= 31; //Corrijo el sector, ya que la primera entrada disponible de la FAT apunta al sector 32, y no al 100190 00191 // Tener en cuenta que FAT12 utiliza codificacion inversa. Cualquier duda recurrir a Microsoft Hardware White PAPER00192 // FAT General Overview of On-Disk Format00193 byte lsb, msb;00194 00195 word indice; 00196 00197 indice = (sector * 3) >> 1;00198 lsb = fat_header.bloque[indice];00199 msb = fat_header.bloque[indice + 1];00200 00201 word proximo_sector;00202 00203 if (sector % 2) //Si es impar, tomo los tres nibbles mas altos00204 proximo_sector = (((msb << 8) | lsb) & 0xfff0) >> 4;00205 00206 else //Si es par, tomo los mas bajos00207 proximo_sector = ((msb << 8) | lsb) & 0x0fff;
00208 00209 if ( proximo_sector==0xFFF )00210 return LAST_SECTOR;00211 00212 return (proximo_sector + 31);00213 } 00214 00215 // **************************************************************************************************************00216 // Recibe un PATH que puede contener directorio y devuelve estilo mnt/files/prog.bin y devuelve el largo hasta00217 // la primera '/' o -1 en caso de que no sea una cadena de paths sino un solo nombre (estilo mnt).00218 // Esta funcion se usa multiples veces hasta llegar al archivo final.00219 // **************************************************************************************************************00220 int str_dir_len (char *nombre)00221 {00222 int len=0;00223 while( *nombre++ != '/') {00224 len++;00225 if ( (len > 12) || (*nombre=='\0') )00226 return -1;00227 }00228 00229 return len; 00230 } 00231 00232 00234 // fat12_entry_t *fat_file_find (char *nombre, fat12_entry_t *datos_archivo);00235 //00236 // Esta funcion recibe un nombre de archivo, y devuelve el sector de comienzo.00237 // Esta version soporta directorios, asi que, cualquier funcion o comando que la llame puede pasarle como parametro00238 // un archivo dentro de un directorio. (mnt/files/pepe.txt)00239 //00240 // NOTA: el directorio Root tiene un tamaño fijo, y sus sectores estan contiguos. Los demas 00241 // directorios, tienen por tamaño 0, sus sectores pueden no ser contiguos, y para saber que en que sector continua00242 // el directorio debera llamarse a fat_next_sector. El flag root_dir_flag es utilizado para indicar si el directorio00243 // en el que se busca es el Root o algun otro (ya que el programa debera comportarse de forma diferente).00245 00246 fat12_entry_t *fat_file_find (char *nombre, fat12_entry_t *datos_archivo)00247 {00248 00249 char nombre_aux[MAX_PATH_LEN]; //String auxiliar donde se va parseando el nombre por tramos (entre "/")00250 char nombre_archivos[MAX_PATH_LEN]; //String en el que se guarda cada nombre levantado del disco00251 00252 byte x; 00253
00254 byte root_dir_flag=TRUE; //Directorio en el que se busca es el Root.00255 00256 //Pasar el nombre a buscar a mayusculas (recordar que la FAT solo usa mayusculas)00257 str_to_upper(nombre);00258 00259 //Sector de comienzo y cantidad de sectores del directorio a leer00260 dword dir_sector, dir_size;00261 00262 //El primer directorio a leer sera el Root Dir, asi que cargo sus parametros 00263 dir_sector = dev_fat[0].root_dir_start;00264 dir_size = dev_fat[0].root_dir_size;00265 00266 int dir_len; //nombre actual es un archivo o directorio (ver funcion str_dir_len). Si es archivo dir_len=-100267 //si es directorio, dir_len=cantidad de caracteres del nombre00268 00269 fat12_entry_t *aux_fat; //Estructura para levantar los datos de cada archivo de disco00270 kprintf("1 Mallocs: %d\tFrees: %d\n", num_mallocs, num_frees);00271 00272 do {00273 fat_file_find_break:00274 if ((dir_len=str_dir_len(nombre)) > 0) { //Si es un directorio, buscarlo y apuntar00275 strncpy(nombre_aux, nombre, dir_len); //nombre al proximo directorio/archivo00276 nombre = nombre + dir_len + 1;00277 }00278 else strcpy(nombre_aux, nombre); //Si llego aca, *nombre es un archivo00279 00280 do {00281 kprintf("2 Mallocs: %d\tFrees: %d\n", num_mallocs, num_frees);00282 //if ( leer_sector(dir_sector, buffer) != OK) {00283 if ( CACHE_READ(fd0,dir_sector, buffer) == -1 ) {00284 kprintf("No se puede leer sector\n");00285 return NULL;00286 }00287 kprintf("3 Mallocs: %d\tFrees: %d\n", num_mallocs, num_frees);00288 00289 aux_fat = (fat12_entry_t *) buffer;00290 00291 00292 //Recorro desde 0-16 (que son las cantidad de entradas de archivos que hay por sector)00293 for( x=0 ; x < (SECTOR_SIZE / sizeof(fat12_entry_t)) ; x++) { 00294 fat_adapta_name( aux_fat->nombre , nombre_archivos);00295 00296 if ( strcmp( nombre_archivos, nombre_aux)==0 ) { 00297 if (dir_len < 0) {
00351 #define MAX_CACHE_ENTRYS 10000352 struct flop_cache cache_index[MAX_CACHE_ENTRYS];00353 */00354 void floppy_cache_init (void)00355 {00356 00357 return; 00358 00359 /* 00360 word i;00361 for(i=0 ; i< MAX_CACHE_ENTRYS ; i++) {00362 cache_index[i].sector=0;00363 cache_index[i].bloque=NULL;00364 }00365 */00366 }00367 00368 /*00369 void *floppy_cache (dword sector)00370 {00371 word i;00372 byte flag=FALSE;00373 00374 //kprintf("Entrando a floppy_cache\n");00375 00376 for (i=0 ; i<MAX_CACHE_ENTRYS ; i++) {00377 if (cache_index[i].bloque==NULL)00378 break;00379 if (cache_index[i].sector == sector) {00380 //kprintf("***Sector se encontraba en el cache***");00381 return (cache_index[i].bloque);00382 }00383 } 00384 00385 if ( i >= MAX_CACHE_ENTRYS ) { // Debe implementarse algun algoritmo de sustitucion00386 //kprintf("***excedido de MAX_CACHE_ENTRYS en el cache***");00387 return NULL;00388 }00389 00390 // Como los sectores leidos los alloco en paginas, puedo meter en cada uno de ellos PAGINA_SIZE / SECTOR_SIZE (en casos00391 // normales 8 sectores por pagina). Los indices 0,8,16,24,etc deberan pedir una nueva pagina, mientras que los sectores00392 // "internos" (1,2,3,4,5,6,7) no.00393 00394 if ( (i % (PAGINA_SIZE / SECTOR_SIZE))==0 ) {00395 //kprintf("***Sector multiplo de: %d***", PAGINA_SIZE / SECTOR_SIZE);00396 if ( (cache_index[i].bloque = (byte *) kmalloc_page()) == NULL )00397 return NULL;00398 flag=TRUE;00399 }00400 // El sector es un sector "interno", no es necesario pedir memoria00401 else {00402 if ( cache_index[i-1].bloque==NULL ) { //Hay problemas de consistencia ?
00403 kprintf("Floppy cache PANIC");00404 return NULL;00405 } 00406 else { //Si todo esta bien00407 //kprintf("***Sector NO multiplo de: %d***", PAGINA_SIZE / SECTOR_SIZE);00408 cache_index[i].bloque = cache_index[i-1].bloque + SECTOR_SIZE;00409 }00410 }00411 00412 00413 if (leer_sector( sector, cache_index[i].bloque)!=OK) {00414 if (flag==TRUE)00415 kfree_page(cache_index[i].bloque);00416 return NULL;00417 }00418 00419 sectores_cache++; //Fines depurativos00420 cache_index[i].sector = sector;00421 return cache_index[i].bloque;00422 00423 }00424 */00426 // Funcion de cache que utiliza listas enlazadas (malloc y free) la cual tiraba excepciones al cachear el sector quinceavo00427 // no logre averiguar porque pero intuyo que tiene que ver con la implementacion de las primitivas de malloc y free00428 /* 00429 void *floppy_cache (dword sector)00430 {00431 struct floppy_cache *aux = header_floppy_cache;00432 00433 // Verificar si aun no se aloco ningun sector en el cache00434 if ( aux==NULL ) {00435 kprintf("ACA\n");00436 aux = (struct floppy_cache *) malloc ( sizeof(struct floppy_cache));00437 if ( aux == NULL) {00438 kprintf("Malloc no me dio memoria 1\n");00439 while(1);00440 } 00441 if ( leer_sector(sector, aux->bloque)!=OK ) {00442 free (aux);00443 aux->next = NULL;00444 return NULL;00445 }00446 aux->sector = sector;00447 aux->next = NULL;00448 header_floppy_cache = aux;00449 sectores_cache++;00450 return aux->bloque;00451 }00452 00453 // Verificar si en la lista enlazada del cache se encuentra el sector buscado 00454 for ( ; aux->next ; aux=aux->next ) {00455 if ( aux->sector == sector ) {00456 return aux->bloque;00457 }00458 }
00459 00460 // Este bloque es necesario para saber si el sector buscado es el ultimo nodo en la lista00461 if (aux->sector == sector) {00462 return aux->bloque;00463 }00464 // Si estoy aca, es que el sector no se encuentra en el cache00465 aux->next = (struct floppy_cache *) malloc ( sizeof(struct floppy_cache));00466 if ( aux->next == NULL) {00467 kprintf("Malloc no me dio memoria 2\n");00468 while(1);00469 } 00470 00471 if ( leer_sector(sector, aux->next->bloque)!=OK ) {00472 free (aux->next);00473 aux->next = NULL;00474 return NULL;00475 }00476 00477 aux = aux->next;00478 aux->sector = sector;00479 aux->next = NULL;00480 00481 sectores_cache++;00482 return aux->bloque;00483 00484 } 00485 */00486 00487 // **************************************************************************************************************00488 // Estas funciones solo son usadas a modo de prueba, no son parte del manejo de FAT1200489 // **************************************************************************************************************00490 00491 void print_fat_info (void)00492 {00493 putchar('\n');00494 boot_sector_t *p= (boot_sector_t *) buffer;00495 00496 if ( p->BPB_RsvdSecCnt != 1)00497 kprintf("Filesystem no reconocido\n");00498 else kprintf("Disco es FAT12\n");00499 00500 byte i;00501 kprintf("BS_FilSysType:");00502 for(i=0; i<8;i++)00503 putchar(p->BS_FilSysType[i]);00504 putchar('\n');00505 00506 kprintf("Sectores por FAT: %d\n", dev_fat[0].fat_size);00507 kprintf("Cantidad de FATs: %d\n", dev_fat[0].cantidad_de_fats);00508 kprintf("Fat start: %d\n", dev_fat[0].fat_start);00509 kprintf("Sectores ocultos: %d\n", dev_fat[0].sectores_ocultos);
00001 /* file.c */00002 #include "routix/system.h"00003 #include "string.h"00004 #include "drivers/fat.h"00005 #include "routix/paging.h"00006 #include "routix/kalloc.h"00007 #include <routix/kstdio.h>00008 #include "routix/file.h"00009 #include "routix/task.h"00010 #include <drivers/floppy.h>00011 #include <fs/blockcache.h>00012 #include <error.h>00013 00014 #define RUNSTATS 100015 00016 file_opened_t *header_archivos_abiertos;00017 00018 extern dev_fat_t dev_fat[0];00019 00021 // Funcion auxiliar usada para buscar en la lista enlazada, el file descriptor fd00022 // en caso de no encontrarse devuelve NULL, en caso contrario devuelve un puntero al nodo correspondiente00024 00025 00026 // Header a una lista enlazada con todos los archivos abiertos por el sistema00027 file_opened_t *header_archivos_abiertos=NULL;00028 00029 // Como abrir_archivo va a retornar un fd, creo una variable dword file_descriptor_actual, la cual va incrementando00030 // el fd por cada archivo abierto. Es decir, el primer fd que entregaria seria el 100031 00032 dword file_descriptor_actual=1; 00033 00034 // La idea es que haya una lista enlazada con todos los archivos abiertos en el sistema (independientemente a que00035 // cada proceso tengo en su estructura de datos info de los archivos que abrio.00036 // Cada nodo de la lista, contiene una estructura fat12_entry_ext_t datos, un puntero a un bloque de memoria (que sera00037 // levantado de disco por la funcion read, y un dword sector_actual que indica a que sector corresponde el bloque00038 // apuntado por byte *bloque.00039 // Si bien es ineficiente, para hacer bien claro el manejo, se levantara de disco de a 1 sector por vez.00040 00041 00042 extern task_struct_t *actual;00043 00044 int open (char *nombre)00045 {00046 word i;00047 int fd = -1;00048 00049 00050 // Verificar que el proceso tenga algun slot libre para abrir un archivo
00051 for (i=0 ; i < MAX_FILES_POR_TAREA ; i++)00052 if ( actual->open_files[i] == NULL ) {00053 fd = i;00054 break;00055 }00056 00057 if ( fd < 0 ) {00058 actual->err_no = EMFILE;00059 return -1; //el proceso posee la maxima cantidad de archivos abiertos, no se puede abrir otro00060 }00061 struct file aux;00062 00063 /* Esta funcion me devuelve en que dispositivo (aux.device) y en que file system (aux.fs) se encuentra el archivo00064 vfs_find(nombre, aux); 00065 como el file system virtual y el sistema de montajes no esta implementado, completo los valores a mano.00066 */ 00067 aux.device = DRIVE_0 ;00068 aux.fs = FAT12;00069 00070 actual->open_files[fd] = (struct file *) malloc (sizeof(struct file));00071 if ( ! actual->open_files[fd] )00072 return -1;00073 00074 file_alloc++;00075 00076 if ( aux.fs == FAT12 ) 00077 if ( open_FAT12(nombre, fd)!=OK ) {00078 kprintf("POR ACA....\n");00079 free(actual->open_files[fd]);00080 actual->open_files[fd] = NULL;00081 return -1;00082 }00083 00084 return fd;00085 } 00086 00087 // Abrir un archivo desde un dispositivo FAT1200088 int open_FAT12 (char *nombre, int fd)00089 {00090 if ((dev_fat[0].boot_leido==FALSE) || (dev_fat[0].fat_levantada==FALSE) ) 00091 if ( init_floppy_fs() != OK ) {00092 kprintf("No se puede leer disco\n");00093 return -1;00094 }00095 00096 fat12_entry_ext_t archivo;00097 00098 00099 if ( fat_file_find(nombre , &archivo.fat12_data) == NULL ) {//Archivo no se encuentra o no se puede abrir00100 kprintf("Cantidad de Mallocs: %d\tFrees: %d\n", num_mallocs, num_frees);00101 kprintf("No se encuentra el archivete\n");00102 return -1;00103 }00104
00105 actual->open_files[fd]->sectores = archivo.fat12_data.size / SECTOR_SIZE;00106 00107 if ( archivo.fat12_data.size % SECTOR_SIZE ) //Si no es un sector justo, aumentarlo en 100108 actual->open_files[fd]->sectores++;00109 00110 actual->open_files[fd]->offset = 0;00111 actual->open_files[fd]->offset_rel = 0;00112 actual->open_files[fd]->sector_origen = archivo.fat12_data.sector + dev_fat[0].root_dir_start + \00113 dev_fat[0].root_dir_size - 2;00114 actual->open_files[fd]->sector_actual = actual->open_files[fd]->sector_origen;00115 00116 actual->open_files[fd]->size = archivo.fat12_data.size;00117 00118 return OK;00119 } 00120 00121 00122 00123 int lseek (int fd, int offset, int donde)00124 {00125 00126 struct file *aux = actual->open_files[fd];00127 00128 dword sector_aux; // Numero de orden del sector (no es el numero del sector en el dispositivo. Toma los valores00129 // 0,1,2,3,4,5, etc dependiendo del sector sobre el cual este el offset00130 if (aux == NULL)00131 return -1;00132 00133 00134 if (donde == SEEK_SET) { //Posicionarse relativo al comienzo00135 00136 aux->sector_actual = aux->sector_origen; //Apuntar al primer sector (corrigiendolo)00137 if (offset < 0) {00138 aux->offset = 0;00139 return 0; //Posiciono al origen00140 }00141 00142 sector_aux = offset / SECTOR_SIZE;00143 00144 // Recorrer la FAT en busca del sector del proxumo sector00145 while ( sector_aux-- ) {00146 aux->sector_actual = fat_next_sector(aux->sector_actual);00147 if (aux->sector_actual <= 31) //provisorio00148 return 0; //El offset esta mas alla del tamaño del archivo00149 }00150 00151 aux->offset = offset;
00205 // 00206 // char *bloque; //Puntero a bloque que contiene el sector ( mantenido por floppy_cache)00207 //00208 // //Esta variable me indica a cuantos bytes estoy del final del sector actual00209 // word remanente = SECTOR_SIZE - aux->offset_rel;00210 // word desplaz=0;00211 // 00212 //00213 // //Este ciclo va a ser el encargado de ir leyendo el sector que corresponde, y poniendolo en el buffer (buf)00214 // while (len > 0) {00215 // bloque = floppy_cache( aux->sector_actual );00216 // if (bloque == NULL) //Si no se pudo levantar al cache ese bloque...00217 // return -1;00218 //00219 // if (len <= remanente) //Si lo que voy a leer es menor que lo que falta para el final del sector00220 // aux_len = len; //no es necesario que cargue lea desde el disco (o desde el cache) es sector siguiente00221 //00222 // else aux_len = remanente; //Si en cambio, lo que tengo que leer es mayor a lo que queda de este sector00223 // //voy a tener que ir cargando del disco (o del cache) otros sectores hasta completar00224 // //el total00225 //00226 // //Poner en el buffer del llamante lo que lei del bloque actual00227 // memcpy( (buf + desplaz), (bloque + aux->offset_rel), aux_len);00228 // 00229 // //Corregir el offset00230 // lseek(fd, aux_len, SEEK_CUR );00231 // desplaz = desplaz + aux_len;00232 // len = len - aux_len;00233 // remanente = SECTOR_SIZE - aux->offset_rel;00234 // }00235 // 00236 // return desplaz;00237 //}00238 00239 ssize_t read (int fd, void *buf, size_t nbytes)00240 {00241 size_t len=nbytes;00242 ssize_t aux_len;00243 00244 struct file *aux; //Apunta a la estructura de archivo abierto00245 00246 #ifdef RUNSTATS00247 long long counter;00248 00249 if ( getvar("debugread") == 2 ) {00250 rdtscl(counter);00251 }00252 #endif00253 00254
00255 if ( (aux=actual->open_files[fd] )==NULL) //Archivo no esta abierto00256 return -1;00257 00258 //Corregir len en caso de que sea mayor que el tamaño del archivo00259 if ( (aux->size) < (len + aux->offset) )00260 len = (aux->size) - aux->offset;00261 00262 aux_len = len;00263 00264 //Esta variable me indica a cuantos bytes estoy del final del sector actual00265 word remanente = SECTOR_SIZE - aux->offset_rel;00266 word desplaz=0;00267 00268 00269 //Este ciclo va a ser el encargado de ir leyendo el sector que corresponde, y poniendolo en el buffer (buf)00270 while (len > 0) {00271 00272 /*00273 bloque = floppy_cache( aux->sector_actual );00274 if (bloque == NULL) //Si no se pudo levantar al cache ese bloque...00275 return -1;00276 */00277 00278 //setvar("debugcache",1);00279 //setvar("debugfloppy",1);00280 if ( cache_read(fd0,aux->sector_actual,aux->offset_rel,buf+desplaz,aux_len) == -1 )00281 return -1;00282 00283 00284 if (len <= remanente) //Si lo que voy a leer es menor que lo que falta para el final del sector00285 aux_len = len; //no es necesario que cargue lea desde el disco (o desde el cache) es sector siguiente00286 00287 else aux_len = remanente; //Si en cambio, lo que tengo que leer es mayor a lo que queda de este sector00288 //voy a tener que ir cargando del disco (o del cache) otros sectores hasta completar00289 //el total00290 00291 //Poner en el buffer del llamante lo que lei del bloque actual00292 //memcpy( (buf + desplaz), (bloque + aux->offset_rel), aux_len);00293 00294 //Corregir el offset00295 lseek(fd, aux_len, SEEK_CUR );00296 desplaz = desplaz + aux_len;00297 len = len - aux_len;00298 remanente = SECTOR_SIZE - aux->offset_rel;00299 }00300 00301 #ifdef RUNSTATS00302 if ( getvar("debugread") == 2 ) {00303 long long int counterfinal;
00001 /* floppy.c */00002 00003 /*00004 * Revision: 22 Marzo 200400005 * + Se migra la funcion (Floppy) llamada desde la rutina de atencion a interrupcion (intFloppy) a este archivo, 00006 * con lo que se hace la variable "continuar" (de ahora en mas llamada "floppy_continuar") como local al00007 * archivo (static).00008 *00009 * + Se ajustan los valores de inicializacion del floppy (comandos CONFIGURE y SPECIFY) de la siguiente forma:00010 * Se habilita la fifo (propia del 82077a) con un tama#o de 10 bytes.00011 * Step rate interval: 4mseg00012 * Head Unload time : 16mseg00013 * Head Load time : 16mseg00014 * 00015 * + Proxima revision:00016 * funciones de lectura y escritura00017 * conversion de sectores logicos a fisicos (LBA)00018 * ----------------------------------------------------------------00019 *00020 * Revision: 24 Marzo 200400021 *00022 * + Se crea la funcion dump_status_register() con fin de poder dumpear y debugear el estado de los registros00023 * de estado (ST0 a ST3)00024 *00025 * + En la funcion read_sector se reemplaza la verificacion de todos los bits del ST0 por una macro "COMMAND_OK"00026 * (definida en include/drivers/floppy.h) que solo chequea el valor del IC de este registro de estado.00027 * Esto es ya que cuando estamos en la condicion de sector=18 el valor de "H" devuelto por ST0 cambia (de 0 a 100028 * por ejemplo), pienso que se debera que nos indica la proxima posicion de la cabeza para leer el proximo00029 * sector (el sector numero 18 es el ultimo sector de un track en una cara).00030 * Con este cambio se observa que pueden ejecutarse sin problemas todos los archivos de disco.00031 */00032 00033 00034 00035 #include <drivers/floppy.h>00036 #include <fs/blockcache.h>00037 #include <routix/device.h>00038 #include <routix/atomic.h>00039 #include <routix/8259.h>00040 #include <routix/kstdio.h>00041 #include <routix/timer.h>00042 00043 #define OK 000044 00045 int block(void);00046 int floppy_get_result(void);
00047 int leer_sec(byte cara, byte cilindro, byte sector , byte *buffer);00048 void dump_states(void);00049 void dump_status_register(int numero, byte valor);00050 00051 void leer_escribir_new();00052 void init_floppy_block_new(void);00053 void recalibrate_block_new(void);00054 void motor_on_new_finish(timer_t *timer);00055 void seek_block_new();00056 void read_sector_block_new();00057 void error();00058 void error_por_timeout();00059 00060 00061 00062 00063 // Puntero a funcion para manejar las interrupciones00064 static void (*func)(void) = NULL;00065 00066 // Solicitud en proceso00067 static block_cache_t *actual_request = NULL;00068 #define ACTUAL_REQUEST (actual_request)00069 00070 //Contiene el estado del motor (0=apagado , 1=encendido)00071 static byte motor_status=MOTOR_OFF;00072 static byte floppy_calibrado=0;00073 static byte seek_status=0;00074 static byte rw_status=0;00075 00076 // Timer para encendido y apagado de motor00077 static timer_t *timer_motor = NULL;00078 // Timer de deteccion de TIMEOUTs00079 static timer_t *timer_timeout = NULL;00080 00081 // Array con los valores de estados y pcn en la fase de resultado.00082 // Valores validos: 0-255 ; ERR_TIMEOUT ; NO_VALIDO00083 static int status[12]; 00084 00085 #define ST0 status[0]00086 #define ST1 status[1]00087 #define ST2 status[2]00088 #define ST3 status[3]00089 00090 00091 // En esta direccion (640K - 1024) se ubica el bloque para transferencias DMA. La ubicacion de este bloque no puede00092 // quedar entre dos paginas (el tamaño de estas paginas es de 64K) es decir tiene que estar totalmente contenido dentro00093 // del mismo bloque.00094 addr_t dma_address=0xA0000 - 1024;00095 00096 00097 void motor_on()00098 {00099 outportb(DOR, DMA_INT | MOTA);00100 motor_status=1;00101 dword demora=0x3ffff; 00102 while (demora--); //Esperar por el regimen permanente del motor
00161 void set_dor(word valor)00162 {00163 outportb(DOR, valor);00164 }00165 00166 00167 00169 // Envia un byte al registro de datos del controlador //00171 int floppy_sendbyte(byte valor)00172 {00173 unsigned char msr;00174 int timeout;00175 00176 for (timeout=SENDBYTE_TIMEOUT; timeout; timeout--) {00177 00178 // Leemos primero el MSR (Main Status Register)00179 msr = inportb(MSR);00180 00181 // Si RQM=1 y DIO=0 ( Host puede escribir info)00182 if ( (msr & RQM) && ~(msr & DIO) ) {00183 outportb(DATA, valor);00184 break;00185 }00186 00187 }00188 00189 // Si timeout llego a 0 no se pudo completar la escritura00190 return timeout;00191 }00192 00193 00195 // Lee un byte del registro de datos del controlador //00197 int floppy_get_result()00198 {00199 unsigned char msr;00200 byte posicion=0;00201 int timeout;00202 00203 for (timeout=GETBYTE_TIMEOUT; timeout; timeout--) 00204 {00205 // Leemos primero el MSR (Main Status Register)00206 msr = inportb(MSR) & (RQM | CMDBUSY | DIO);00207 00208 // Si RQM=1, DIO=1 y CMD_BUSY=1 ( Host puede leer info)00209 if ( msr == (RQM | CMDBUSY | DIO) )00210 {00211 status[posicion++]=inportb(DATA);00212 continue;00213 }00214 //Si el controller dejo de estar ocupado (CMD_BUSY=0), no hay mas resultados que levantar, salida Ok00215 else if ( msr == RQM )00216 {00217 status[posicion]=NO_VALIDO;00218 return OK;00219 }00220 }00221 00222 // Si timeout llego a 0 no se pudo completar la escritura00223 return ERR_TIMEOUT;
00283 } 00284 00285 if ( ! floppy_sendbyte(0xC1) ) { // Seteamos SRT=4mseg (0xC0) y HUT=16mseg (0x01)00286 return ERR_TIMEOUT;00287 }00288 00289 if ( ! floppy_sendbyte(0x8<<1) ) { // Seteamos HLT=16mseg (0x08 << 1) y ND (Ahora esta en NON-DMA !!)00290 return ERR_TIMEOUT;00291 }00292 00293 //kprintf("Inicializacion Oks !!\n");00294 00295 return OK; 00296 }00297 00298 00299 00300 00301 // Inicializacion del floppy00302 void init_floppy_new()00303 {00304 00305 // Reset00306 outportb(DOR, 0);00307 00308 outportb(DOR, 0x0C);00309 00310 // Programamos la velocidad de datos (500 Kbps para 1.44MB)00311 outportb(CCR, DR_500KBPS);00312 00313 //Esperamos la interrupcion, le indicamos la nueva rutina de atencion de interrupcion00314 //block();00315 func=init_floppy_block_new;00316 }00317 00318 00319 void init_floppy_block_new(void)00320 { 00321 int i;00322 00323 // Reseteamos a func a su valor default00324 func=NULL;00325 00326 // Recolectamos el estado de las interrupciones de los 4 drives00327 // que soporta el controlador00328 for (i=0; i<4; i++) {00329 00330 if ( ! floppy_sendbyte(SENSE_INTERRUPT_STATUS) ) {00331 //return ERR_TIMEOUT;00332 }00333 00334 00335 if (floppy_get_result()==ERR_TIMEOUT) {00336 //return ERR_TIMEOUT;00337 }00338 00339 }
00340 00341 00342 // Ahora reconfiguramos el controlador00343 if ( ! floppy_sendbyte(CONFIGURE_0) ) {00344 //return ERR_TIMEOUT;00345 }00346 if ( ! floppy_sendbyte(CONFIGURE_1) ) {00347 //return ERR_TIMEOUT;00348 }00349 00350 // COMENTARIO: me quedan dudas respecto a estos valores ya que00351 // CONF_EIS: habilitamos los seeks automaticos (implicitos)00352 // CONF_EFIFO: deshabilitamos los fifos !! (modo 8272a)00353 // CONF_POLL: deshabilitamos el polling, esto esta oks00354 //if ( ! floppy_sendbyte(CONF_EIS|CONF_EFIFO|CONF_POLL) ) {}00355 if ( ! floppy_sendbyte( (CONF_EIS|CONF_POLL) | 0xa) ) { // 10 bytes de threshold00356 //return ERR_TIMEOUT;00357 }00358 if ( ! floppy_sendbyte(0) ) {00359 //return ERR_TIMEOUT;00360 }00361 00362 // Seguimos con SPECIFY00363 if ( ! floppy_sendbyte(SPECIFY) ) {00364 //return ERR_TIMEOUT;00365 } 00366 00367 if ( ! floppy_sendbyte(0xC1) ) { // Seteamos SRT=4mseg (0xC0) y HUT=16mseg (0x01)00368 //return ERR_TIMEOUT;00369 }00370 00371 if ( ! floppy_sendbyte(0x8<<1) ) { // Seteamos HLT=16mseg (0x08 << 1) y ND (Ahora esta en NON-DMA !!)00372 //return ERR_TIMEOUT;00373 }00374 00375 // Si fue como resultado de un pedido de lectura o escritura00376 if ( ACTUAL_REQUEST != NULL )00377 leer_escribir_new();00378 00379 }00380 00381 00383 // Recalibra la posicion de la cabeza a la pista 000385 #define PCN status[1]00386 00387 int recalibrate()00388 {00389 00390 // Reseteamos el flag00391 floppy_calibrado=0;00392 00393 // Habilita el motor A y el Drive 000394 motor_on();00395 00396 // Ejecutamos un RECALIBRATE 00397 if ( ! floppy_sendbyte(RECALIBRATE) ) { 00398 return ERR_TIMEOUT;00399 }
00456 kprintf("Envio de recalibrate_new !!\n");00457 00458 return OK;00459 00460 }00461 00462 void recalibrate_block_new(void)00463 {00464 // Reseteamos a func a su valor default00465 func=NULL;00466 00467 // Eliminamos el timer de timeout00468 clean_timer(timer_timeout);00469 00470 // El timer ya no existe00471 timer_timeout = NULL;00472 00473 if ( getvar("debugfloppy")==1 )00474 kprintf("Recalibrado ok !!\n");00475 00476 // Sensamos el estado de interrupcion00477 if ( ! floppy_sendbyte(SENSE_INTERRUPT_STATUS) ) {00478 //return ERR_TIMEOUT;00479 error();00480 return;00481 }00482 00483 // Obtener resultados de ejecucion 00484 if (floppy_get_result()==ERR_TIMEOUT) {00485 //return ERR_TIMEOUT;00486 error();00487 return;00488 }00489 00490 // Analizamos el resultado del comando 00491 if ( ! COMMAND_OK ) {00492 dump_states();00493 //return ERR_NEED_RESET;00494 error();00495 return;00496 }00497 00498 // Pudimos calibrarlo sin problemas00499 floppy_calibrado=1;00500 00501 // Si fue como resultado de un pedido de lectura o escritura00502 if ( ACTUAL_REQUEST != NULL )00503 leer_escribir_new();00504 00505 //return OK;00506 }00507 00508 00509 byte read_sector_id (void)00510 {00511 // Habilita el motor A y el Drive 000512 motor_on();00513 00514 // Ejecutamos un Read sector id
00794 // Eliminamos el timer de timeout00795 clean_timer(timer_timeout);00796 00797 // El timer ya no existe00798 timer_timeout = NULL; 00799 00800 // Obtener resultados de ejecucion 00801 if (floppy_get_result()==ERR_TIMEOUT) {00802 //return ERR_TIMEOUT;00803 error();00804 return;00805 }00806 00807 // Analizamos el resultado del comando 00808 if ( ! COMMAND_OK ) {00809 dump_states();00810 //return ERR_NEED_RESET;00811 error();00812 return;00813 }00814 00815 // Pudimos calibrarlo sin problemas00816 rw_status=1;00817 00818 // Si fue como resultado de un pedido de lectura o escritura00819 if ( ACTUAL_REQUEST != NULL )00820 leer_escribir_new();00821 00822 } 00823 00824 00825 char read_msr()00826 {00827 unsigned char msr;00828 00829 msr = inportb(MSR);00830 00831 return msr;00832 }00833 00835 // block: bloquea la ejecucion del proceso que la llama hasta recibir una interrupcion de floppy00837 00838 static byte floppy_continuar; // variable candado de block00839 00840 int block(void)00841 {00842 // Deshabilitamos las interrupciones 00843 disable_irq(6);00844 00845 floppy_continuar=0;00846 00847 // TEMPORAL00848 dword timeout=0xffffffff; //Este timeout es momentaneo (sera reemplazado por un timer). Puede que en maquinas con00849 //micro de mas de 600Mhz haya que aumentarlo... 00850 // Las habilitamos nuevamente00851 enable_irq(6); 00852 00853 // Esperamos que aparezca la interrupcion
00854 while (!floppy_continuar) {00855 timeout--;00856 if (!timeout) //En caso de no haber llegado la IRQ6, salir con ERR_TIMEOUT00857 return ERR_TIMEOUT;00858 }00859 00860 return OK;00861 }00862 00863 00864 00866 // Inicializa el controlador DMA, tanto para leer como para escribir (segun comando)00868 00869 void init_floppy_DMA ( byte comando )00870 {00871 outportb( DMA_MASK, 0x06 );00872 // Resetear el FlipFlop del DMA para que reconozca al primer byte enviado como un LSB00873 outportb ( 0xC, 0);00874 00875 // Enviar comando (DMA_READ, DMA_WRITE)00876 outportb( DMA_CMD, comando );00877 00878 // Seteo de la direccion. No debera estar por encima de los 16MB00879 outportb ( DMA_CH2_ADDR, (byte) dma_address);00880 outportb ( DMA_CH2_ADDR, (byte) (dma_address >> 8) );00881 outportb ( DMA_CH2_PAGE, (byte) (dma_address >> 16) );00882 00883 outportb ( DMA_CH2_COUNT, (byte) ( (BLOQUE_SIZE-1) & 0xff) );00884 outportb ( DMA_CH2_COUNT, (byte) ( (BLOQUE_SIZE-1) >> 8) );00885 outportb ( DMA_MASK, 2);00886 } 00887 00888 00889 00890 00892 // Acepta como operacion READ_SECTOR y WRITE_SECTOR (quiza pueda aceptar format).00893 // Es la encargada de convertir un sector logico (o LBA) en el sector fisico (CHS) correspondiente.00894 // Inicializa el controlador de floppy y enciende el motor. En caso de time outs, intenta hasta MAX_TRYS00896 00897 int leer_escribir(byte operacion, dword sector_logico, byte *buffer)00898 {00899 byte intentos=0;00900 byte cara, cilindro, sector;00901 00902 00903 intentar_nuevamente: 00904 intentos++;00905 if (intentos > MAX_FLOPPY_TRYS ) {00906 //kprintf("No se puede leer el sector %d(superados %d intentos)\n", sector_logico, MAX_FLOPPY_TRYS);00907 return ERR_MAX_TRYS;00908 }
00909 00910 00911 if (motor_status==0) {00912 init_floppy();00913 motor_on();00914 }00915 00916 00917 if (floppy_calibrado==0)00918 if ( recalibrate()!=OK )00919 goto intentar_nuevamente;00920 00921 00922 //Calcular el sector fisico (CHS) a traves del sector logico (LBA)00923 cara = (sector_logico / SECTORES_POR_PISTA) % CARAS_POR_PISTA;00924 cilindro = (sector_logico / SECTORES_POR_PISTA) /CARAS_POR_PISTA;00925 sector = (sector_logico % SECTORES_POR_PISTA) + 1;00926 00927 // Esto no deberia ser necesario ya que configuramos el controlador con EIS (Implied Seek) 00928 if ( seek(cara, cilindro) != OK) {00929 goto intentar_nuevamente;00930 }00931 00932 if (operacion == READ_SECTOR) {00933 if ( leer_sec(cara, cilindro, sector, buffer)!=OK )00934 goto intentar_nuevamente;00935 return OK;00936 }00937 00938 //No implementado00939 if (operacion == WRITE_SECTOR)00940 return OK;00941 else return -1;00942 } 00943 00944 00945 // Nueva funcion00946 //00947 // El proceso de lectura implica los siguientes pasos:00948 //00949 // 1. Encender el motor (si fuese necesario)00950 //00951 // a. Setear el bit del motor correspondiente en el registro DOR00952 // b. Esperar 500 mseg aproximadamente de modo que se estabilice00953 // la velocidad de giro del motor00954 //00955 // 2. Recalibracion (si fuese necesario)00956 //00957 // a. Enviar comando00958 // b. Esperar interrupcion00959 //00960 // 3. Seek en la cabeza, cilindro y sector correspondiente00961 //00962 // a. Enviar comando00963 // b. Esperar interrupcion
00964 //00965 // 4. Lectura del sector00966 //00967 // a. Enviar comando00968 // b. Esperar interrupcion00969 //00970 // Bien, el problema ppal es que si uso el modelo tipo:00971 //00972 // * envio comando00973 // * cambio el puntero de interrupcion de floppy00974 //00975 // se desestructura el codigo y pierdo las variables locales.00976 //00977 // Por ejemplo, en la funcion de lectura al llamar a la funcion de SEEK, 00978 // se pierde la memoria de la funcion previa (lectura).00979 //00981 //00982 // La primer solucion propuesta es la siguiente:00983 //00984 // Se guardará como un puntero la funcion base de ejecucion, en el ejemplo anterior sera00985 // la de lectura, en el proceso de lectura se realizara:00986 //00987 // 1. El motor esta encendido ?00988 // NO: ejecutar motor_on()00989 //00990 // Si no estaba encendido, motor_on se ejecutara y devolver el control al kernel mientras espera00991 // que se venza el timer de 500 mseg, la funcion de retorno de motor_on() establecera la variable00992 // global motor_status como encendida y llamara nuevamente a la funcion de lectura, quien ahora00993 // realizara los siguientes pasos:00994 //00995 // 1. El motor esta encendido ?00996 // SI: seguimos00997 // 00998 // 2. Esta Recalibrado ?00999 // NO: llamamos a recalibrate01000 //01001 // y el proceso continua de la misma forma01002 //01004 01005 //int leer_escribir_new(byte operacion, dword sector_logico, byte *buffer)01006 // Toma todos los argumentos necesarios de la variable global ACTUAL_REQUEST01007 void leer_escribir_new()01008 {01009 byte cara, cilindro, sector;01010 int cmdret;01011 01012 01013 if ( getvar("debugfloppy")==1 )01014 kprintf("leer_escribir_new: motor=%d calibracion=%d\n",motor_status,floppy_calibrado); 01015 01016 // Motor apagado ?01017 if (motor_status==MOTOR_OFF) {01018 if ( getvar("debugfloppy")==1 )
01072 return;01073 }01074 }01075 01076 if ( DRIVER_REQUEST_OPERATION(ACTUAL_REQUEST) == IOREAD ) {01077 01078 if ( getvar("debugfloppy")==1 )01079 kprintf("leer_escribir_new: Leyendo\n");01080 01081 // Todavia no fue hecha la lectura01082 if ( ! rw_status ) {01083 01084 // Comando enviado oks?01085 if ( (cmdret=read_sector_new(cara, cilindro, sector)) == OK )01086 return;01087 01088 // Retornamos el error01089 else {01090 error();01091 return;01092 }01093 01094 01095 }01096 01097 // Hicimos la lectura en un paso previo, veamos que resulto01098 else {01099 //kprintf("leer_escribir_new: Fase de lectura terminada, copiando al buffer\n");01100 01101 word i;01102 01103 byte *dma_block = (byte *) dma_address;01104 byte *buffer = DRIVER_REQUEST_BUFFER(ACTUAL_REQUEST);01105 01106 for(i=0 ; i < BLOQUE_SIZE ; i++) 01107 *buffer++ = *dma_block++;01108 01109 // Oks, ya hicimos todo, resta colocar ok en el retorno del buffer block, 01110 // marcarlo como hecho, despertar a la tarea que genero todo esto y01111 // colocar un timer de apagado de motor01112 floppy_calibrado=0;01113 seek_status=0;01114 rw_status=0;01115 01116 // Bloque leido correctamente01117 DRIVER_REQUEST_RETURN(ACTUAL_REQUEST) = 1;01118 01119 }01120 01121 }01122 01123 //No implementado01124 else if ( DRIVER_REQUEST_OPERATION(ACTUAL_REQUEST) == IOWRITE ) {
01125 //kprintf("WRITE: operacion no permitida\n");01126 }01127 01128 // Indicamos al cache que terminamos nuestra operatoria01129 endrequest(ACTUAL_REQUEST);01130 01131 // Liberamos al floppy01132 ACTUAL_REQUEST=NULL;01133 01134 // En algunos segundos apagamos el motor01135 timer_motor = create_timer(USECONDS_TO_TICKS(TIEMPO_APAGADO_MOTOR_POR_INACTIVIDAD), NULL, motor_off_new, NULL);01136 01137 return;01138 01139 }01140 01141 // Esta funcion es llamada si se vencio el tiempo de espera de interrupcion01142 void error_por_timeout()01143 {01144 01145 // El timer ya no existe01146 timer_timeout = NULL; 01147 01148 // llamamos a la rutina de error01149 error();01150 01151 }01152 01153 // Algun error, debemos actualizar el bloque indicando dicho error01154 void error()01155 {01156 01157 // Oks, hubo un error, resta colocarlo en el retorno del buffer block,01158 // marcarlo como hecho, despertar a la tarea que genero todo esto y01159 // colocar un timer de apagado de motor01160 DRIVER_REQUEST_RETURN(ACTUAL_REQUEST) = -1;01161 01162 floppy_calibrado=0;01163 seek_status=0;01164 rw_status=0;01165 01166 // Liberamos al floppy01167 ACTUAL_REQUEST=NULL;01168 01169 // Indicamos al cache que terminamos nuestra operatoria01170 endrequest(ACTUAL_REQUEST);01171 01172 // En algunos segundos apagamos el motor01173 timer_motor = create_timer(USECONDS_TO_TICKS(TIEMPO_APAGADO_MOTOR_POR_INACTIVIDAD), NULL, motor_off_new, NULL);01174 01175 }01176
00063 puts("Kmalloc Inicializando...\n");00064 kprintf("Memoria fisica %d Megabytes\n",memoria);00065 00066 inicializacion_kmalloc(memoria, KERNEL_END );00067 puts("Kmalloc Inicializacion completa\n");00068 00069 /* Mapea linealmente el resto de la memoria fisica (a partir de los 4Mb en adelante) */00070 init_all_memory(memoria);00071 00072 00073 puts("Inicializando interrupciones\n");00074 inicializarInterrupciones();00075 00076 puts("Inicializando 8254\n");00077 init_8254();00078 00079 puts("Inicializando Rejoj\n");00080 init_time();00081 00082 // Inicialización del cache de bloques00083 start_block_cache(memoria);00084 00085 // Ubicar un directorio de páginas para las tareas en modo kernel00086 USER_PDT = kmalloc_page();00087 copy_page ((void *)USER_PDT, (void *)KERNEL_PDT);00088 unsigned long *puntero = (unsigned long *)USER_PDT;00089 int i;00090 // A los 2 primeros GB de memoria los pongo como Supervisor Only00091 for (i=0 ; i<PAGINA_SIZE ; i++)00092 puntero[i] = puntero[i] & 0xfffffffb;00093 00094 00095 // Inicialización del scheduler00096 puts("Inicializando el scheduler\n");00097 start_scheduler();00098 00099 /*00100 extern dev_fat_t dev_fat[1]; //Estructura para dispositivos con fs FAT00101 dev_fat[0].boot_leido = FALSE; 00102 dev_fat[0].fat_levantada = FALSE;00103 floppy_cache_init();00104 */00105 00106 kprintf("Task Struct Size: %d\n", sizeof(task_struct_t));00107 00108 // Habilitamos solo teclado, timertick y floppy00109 enable_irq(0);00110 enable_irq(1);00111 enable_irq(6);00112 00113 // Inicializar variables USER-KERNEL (moemntaneas) definidas en sysmisc.c00114 init_var();00115 00116 00117
00056 }00057 00058 switch (*++p) {00059 00060 case 'c':00061 car=va_arg(argumentos, int); 00062 putchar( (char) car);00063 break;00064 00065 case 'x':00066 i = va_arg(argumentos, unsigned int );00067 printn(i,16);00068 break;00069 00070 00071 case 'd':00072 i = va_arg(argumentos, int);00073 if (i > (0xffffffff/2) ) {00074 putchar('-');00075 printn(~i+1,10);00076 break;00077 }00078 printn(i,10);00079 break;00080 00081 case 'u':00082 i = va_arg(argumentos, unsigned int);00083 printn(i,10);00084 break;00085 00086 case 'o':00087 i = va_arg(argumentos, unsigned int);00088 printn(i,8);00089 break; 00090 00091 case 's':00092 d = va_arg(argumentos, char *);00093 puts(d);00094 break;00095 00096 00097 // Tenemos el caso de querer imprimir un long long (8 bytes = 64bits)00098 // para ello tenemos que pensar que dado un número, por ej.:00099 // 2217 = 0x08A9 = 10001010100100100 // supongamos a modo de análisis que tenemos que imprimir estos 2 bytes y00101 // que nuestra capacidad de manejo (en registros) es de 1 byte.00102 // El MSB= 0x08 = 800103 // y LSB=0xA9 = 16900104 // mientras que deberíamos leer:00105 // MSB = 22 = 0x16 00106 // LSB = 17 = 0x1100107 //00108 // MSB = 0x08 --> 0x16 => dif = 0x16 - 0x08 = 0x0e00109 // LSB = 0xa9 --> 0x11 => dif = 0x11 - 0xa9 = neg 00110 //
00111 // Otro ejemplo:00112 // 3598 = 0x0E0E = 11100000111000113 //00114 // MSB = 0x0E --> 0x35 => dif = 0x35 - 0x0e = 0x2700115 // LSB = 0x0E --> 0x98 => dif = 0x98 - 0x0e = 0x8a00116 // 00117 // Por ahora no encuentro una forma sencilla de imprimir números long long00118 // en decimal, realizamos la implementación en hexa.00119 // 00120 case 'l':00121 00122 if ( *++p != 'l' )00123 break;00124 00125 switch ( *++p ) {00126 00127 case 'x':00128 lli = va_arg(argumentos, long long int);00129 00130 // Si los 4 bytes más significativos no son nulos el debemos agregar00131 // ceros en el menos significativo00132 if ( lli>>32 )00133 padding=8;00134 00135 // Si los 4 bytes más significativos son nulos no importa el padding, 00136 // pero lo colocamos en 1 para que cuando sea 0 imprima por lo menos00137 // ese caracter 00138 else00139 padding=1;00140 00141 PRINTn( (unsigned int) (lli>>32), HEXADECIMAL, 0);00142 PRINTn( (unsigned int) lli, HEXADECIMAL, padding);00143 break;00144 00145 }00146 00147 break;00148 00149 00150 00151 default:00152 putchar(*p);00153 break;00154 00155 }00156 00157 }00158 00159 va_end(argumentos);00160 }
00170 * pid = sys_waitpid(child->pid,&value,WNOHANG);00171 * child = aux; // Agregado 200172 * }00173 * \todo Implementarlo más limpiamente00174 */00175 #define _LIST_FOREACH(var,header,list) \00176 for(var=HEADER_NEXT(header); var!=NULL; )00177 00178 /* \brief Permite insertar un nodo antes de otro nodo determinado00179 * es importante que nodebefore sea un nodo válido, esto implica que00180 * no se permite que nodebefore sea NULL por ejemplo.00181 */00182 #define LIST_INSERT_BEFORE(header,list,nodebefore,node) \00183 LIST_PREVIOUS(list,node)=LIST_PREVIOUS(list,nodebefore); \00184 LIST_NEXT(list,node)=nodebefore; \00185 if ( HEADER_NEXT(header)==nodebefore ) \00186 HEADER_NEXT(header)=node; \00187 else \00188 LIST_NEXT(list,LIST_PREVIOUS(list,nodebefore))=node; \00189 LIST_PREVIOUS(list,nodebefore)=node;00190 00191 00192 00193 00202 #define LIST_FOREACH_REVERSE(var,header,list) \00203 for(var=HEADER_PREVIOUS(header); var!=NULL; var=LIST_PREVIOUS(list,var))00204 00205 00206 /* Permite buscar un nodo de la lista por un valor de00207 * un atributo particular.00208 * Recibe como argumentos la lista, la variable donde00209 * devuelve un puntero al nodo encontrado o NULL en caso00210 * de busqueda sin resultados, el siguiente argumento es00211 * el nombre de la variable a buscar (definida en la 00212 * estructura y por ultimo el valor buscado.00213 * ej:00214 * task_struct_t *nodobuscado;00215 * LIST_FIND(runqueue,nodobuscado,priority,3)00216 */00217 #define LIST_FIND(header,list,var,name,value) \00218 for(var=HEADER_NEXT(header); var!=NULL && var->name!=value; var=LIST_NEXT(list,var))00219 00220 00227 #define LIST_ATTACH(dest, src, type) \
00001 /* misc.h */00002 00003 #ifndef __SYSTEM00004 #include "routix/system.h"00005 #endif00006 00007 00008 // Setea el valor de una variable visible desde el modo Kernel y User00009 int setvar(char *nombre, int valor);00010 // Devuelve el valor de una variable visible desde el modo Kernel y User00011 int getvar(char *nombre);00012 00013 // Wrappers que transforman las direccion de nombre del User al Kernel space00014 inline int sys_setvar(char *nombre, int valor);00015 inline int sys_getvar(char *nombre);00016 00017 int find_var(char *nombre);00018 int find_empty_var(void);00019 00020 void sys_read_debug(int sector);00021 00022
00123 else { // Si la entrada existe, esa pagina puede ya estar asignada00124 00125 tabla = (pt_t *) (dir->entry[indice.dir_index] & 0xfffff000);00126 if (tabla->entry[indice.tabla_index]) {00127 if ( getvar("pagedebug")==1 )00128 kprintf("Kmapmem: Pagina ya en uso\n");00129 return ERR_DIR_BUSY;00130 }00131 }00132 00133 tabla->entry[indice.tabla_index]= (pte_t) make_pte ( fisica , atributo);00134 00135 return OK;00136 }00137 00144 int kunmapmem (addr_t logica, addr_t directorio)00145 {00146 if ( logica & 0xfff) {00147 if ( getvar("pagedebug")==1 )00148 kprintf("KunmapMem: la direccion debe estar alineada a 4KB\n");00149 return ERR_NO_ALIGN;00150 }00151 00152 00153 page_index_t indice;00154 indice = get_page_index ( logica );00155 00156 pd_t *dir = (pd_t *) directorio;00157 00158 pt_t *tabla = (pt_t *) (dir->entry[indice.dir_index] & 0xfffff000);00159 00160 00161 if ( !tabla ) { //Si la entrada correspondiente a "logica" esta vacia00162 if ( getvar("pagedebug")==1 )00163 kprintf("Debug Mode - KunmapMem: la tabla esta vacia\n");00164 return ERR_DIR_EMPTY;00165 }00166 00167 //Liberar la entrada correspondiente00168 tabla->entry[indice.tabla_index] = 0;00169 00170 00171 //Ahora verifico si todas las entradas de la tabla estan vacias. Si es asi la libero. Con que haya una00172 //ocupada, no puedo hacerlo00173 word i;00174 for ( i=0 ; i < PAGINAS_POR_TABLA ; i++)00175 if ( tabla->entry[i] )00176 return OK;00177 00178 if (getvar("pagedebug")==1)00179 kprintf("Kunmapmem: liberando tabla en: 0x%x\n", dir->entry[indice.dir_index] & 0xfffff000);
00001 00006 #include <routix/system.h>00007 #include <routix/segm.h>00008 #include <routix/paging.h>00009 #include <routix/kalloc.h>00010 #include <routix/task.h>00011 #include <routix/debug.h>00012 #include <routix/kstdio.h>00013 #include <error.h>00014 #include <routix/syscalls.h>00015 #include <sys/list.h>00016 #include <string.h>00017 #include <routix/signal.h>00018 00019 void idle_task(void);00020 void tarea_init(void); //Tarea INIT00021 00022 // Puntero a la lista enlazada de tareas00023 task_struct_t *tareas_inicio=NULL;00024 00025 // Puntero a la tarea actual00026 task_struct_t *actual=NULL;00027 00028 // Cantidad de tareas activas00029 extern volatile int tareas_activas;00030 00031 00032 task_struct_t *init_task, *pre_init_task;00033 00034 void start_scheduler(void)00035 {00036 /* Por ahora usamos la memoria fisica directamente, total esta mapeada00037 * hasta que reubiquemos el codigo y datos de las tareas a una dirección00038 * virtual particular */00039 00040 // Inicializo el tss con valores nulos00041 inicializarTss(&tss, 0, 0, 0, 0, 0 ); 00042 00043 // Tarea fundamental del sistema ! INIT_TASK corre cuando no hay nada para correr00044 addr_t INIT_stack = kmalloc_page();00045 00046 00047 init_task=init_new_task(DESC_CODE, DESC_DATA, (dword) &tarea_init, INIT_stack + 4096, 0x202, "init", 50);00048 pre_init_task = init_task;00049 00050 00051 init_task->pid = 1;00052 // Init es padre e hijo (santisima trinidad)... para evitar problemas al hacer init->padre00053 init_task->padre = init_task;00054 // LIST_INIT(init_task->zombie_header);00055 00056 // Aloco la estructura de señales de init y la incializo (cuidado !!! esto NO ANDA !!!!!!!)00057 // TASK_SIGNALS_ALLOC (init_task);
00001 00006 #include <routix/system.h>00007 #include <routix/task.h>00008 #include <signal.h>00009 #include <routix/signal.h>00010 #include <sys/types.h>00011 #include <routix/kstdio.h>00012 #include <error.h>00013 #include <routix/debug.h>00014 #include <routix/syscalls.h> 00015 00016 void exec_sig_default (task_struct_t *task, int signo);00017 void sig_kill(task_struct_t *task);00018 00151 // Variables que contienen cuantos malloc y cuantos frees realizó el kernel para alocar estructuras de00152 // señales (sólo con propósitos de debug)00153 unsigned int task_signals_alloc = 0;00154 unsigned int task_signals_free = 0;00155 00156 00157 00163 int check_sigpending(task_struct_t *task, int save_context)00164 {00165 // No verificar ningún tipo de señales para init (al menos por ahora)00166 if (actual==pre_init_task)00167 return -1;00168 00169 if (getvar("pepe")==1)00170 return 0;00171 00172 // Si no hay señales pendientes... me voy00173 if (TASK_SIGPENDING(task)==0) {00174 return 0;00175 }00176 00177 int signo = 0;00178 unsigned long sig = TASK_SIGPENDING(task);00179 00180 while (signo < SIGMAX) {00181 // Si la señal está pendiente y no está inhibida (o enmascarada) ...00182 if ( (sig & 1) && !IS_MASKED(task,signo)) {00183 exec_sigpending(task, signo, save_context);00184 TASK_SIGNAL_COUNT(task, signo)--;00185 if (TASK_SIGNAL_COUNT(task, signo)<1)00186 // Borro el bit que indica que la señal está pendiente00187 TASK_SIGPENDING(task) = TASK_SIGPENDING(task) ^ (1<<signo);00188 // Me voy avisando que encontre al menos una señal pendiente00189 return 1;00190 }00191 // No importa la máscara para SIGKILL00192 else if ( (sig & 1) && ((signo==SIGKILL) || (signo==SIGSTOP)) )
00193 sig_kill(actual);00194 sig = sig >> 1;00195 signo++;00196 }00197 00198 return 0;00199 00200 /*00201 // Barrer todas las posibles señales00202 for (signo=0 ; (sig & 1)==0 ; signo++)00203 sig = sig >> 1;00204 00205 exec_sigpending(task, signo, save_context);00206 task->sigpending = task->sigpending ^ (1<<signo);00207 00208 // Si llegué al final y no hay más pendientes00209 if (signo > SIGMAX) {00210 kprintf("No hay más pendientes ???\n");00211 return 0;00212 }00213 00214 00215 // Retorno al scheduler, diciendo que había la menos una señal pendiente00216 return 1;00217 */00218 }00219 00220 00221 00229 void exec_sigpending(task_struct_t *task, int signo, int save_context)00230 {00231 if (TASK_SIGNAL_HANDLER(task, signo) == SIG_DFL) {00232 exec_sig_default(task, signo);00233 return;00234 }00235 00236 // Debo inhibir la posibilidad que me conmute la tarea, ya que si lo hace con el stack desbalanceado00237 // puede pasar cualquier cosa00238 cli();00239 00240 // Obtener el contexto de la tarea00241 struct int_regs_ext *context = GET_CONTEXT(task);00242 00243 unsigned long *user_ptr = (unsigned long *) context->esp;00244 // Obtener la dirección física del stack de la tarea00245 user_ptr = convertir_direccion( user_ptr, actual->cr3_backup);00246 00247 // Sólo debo "pushear" el EIP de la tarea y los registros para la primer señal pendiente00248 // verificando con first_signal00249 if (save_context == 1) {00250 // kprintf("EXEC: signo first: %d\n", signo);00251 *(--user_ptr) = context->eip;00252 // kprintf("Pusheando al stack el EIP: 0x%x\n", context->eip);00253 context->esp -= 4;
00060 char *str_to_lower (char *str)00061 {00062 char *str_ret = str;00063 00064 for ( ; *str ; str++)00065 *str = (char) tolower(*str);00066 return str_ret;00067 } 00068 00069 00070 int tolower(int c)00071 {00072 if ( (c >= 'A') && (c <= 'Z') )00073 c+=32;00074 return c;00075 }00076 00077 int toupper(int c)00078 {00079 if ( (c >= 'a') && (c <= 'z') )00080 c-=32;00081 return c;00082 }00083 00084 00085 int memcmp( const void *s1, const void *s2, dword n)00086 {00087 byte *s22 = (byte *) s2;00088 byte *s11 = (byte *) s1;00089 00090 while (n--) {00091 if ( *s11 != *s22 )00092 return (int) (s11-s22);00093 s22++;s11++;00094 }00095 00096 return 0;00097 } 00098 00099 /*00100 void memcpy( void *dest, const void *src, dword n)00101 {00102 byte *dest1 = (char *)dest;00103 byte *src1 = (char *)src;00104 00105 while (n--)00106 *dest1++ = *src1++;00107 00108 return dest;00109 } 00110 */00111 00112 // Decidi escribir esta funcion de este modo al ver que el tiempo de ejecucion era entre 2 y 10 veces menor que00113 // el clasico codigo en C, sin sacrificar ningun tipo de comprobaciones. Influye tambien que esta funcion puede ser00114 // usada, entre otras cosas, para copiar paginas completas como ser el caso del directorio de paginas el cual sera00115 // duplicado por cada nueva tarea creada.00116 00117 void *memcpy( void *dest, const void *src, dword n)
00001 00008 #include "routix/system.h"00009 #include "routix/paging.h"00010 #include "routix/segm.h"00011 #include "routix/debug.h"00012 #include "routix/syscalls.h"00013 #include "sys/syscalls.h"00014 #include "error.h"00015 #include "routix/kalloc.h"00016 #include <routix/kstdio.h>00017 #ifndef __TASK00018 #include "routix/task.h"00019 #endif00020 00021 00022 //Nuevas00023 00024 extern task_struct_t *actual;00025 00026 00027 00028 // Vector de funciones de llamadas al sistema (grupo Timer)00029 int (*syscall_mem[MAX_SYSCALLS]) (void) = {00030 (int (*) (void)) sys_malloc_page,00031 (int (*) (void)) sys_free_page,00032 (int (*) (void)) sys_free_mem00033 };00034 00035 00036 00037 /* SYS_MALLOC_PAGE: Esta llamada tiene por función brindarle una pagina a un proceso de usuario, dandole la direccion00038 * virtual que le corresponde. Esta funcion se encarga también, de actualizar la task_Struct con los nuevos valores.00039 */00040 void *sys_malloc_page(void)00041 {00042 //Verificar que la tarea actual no sobrepase la cantidad máxima de páginas utilizadas00043 word npages_usadas = actual->num_code + actual->num_data;00044 00045 if ( npages_usadas >= MAX_PAGINAS_POR_TAREA )00046 return NULL; //No más memoria para esta tarea00047 00048 //Debo devolver la dirección mapeada en el espacio virtual de la tarea. Desde la dir virtual TASK_DATA me desplazo00049 //num_data paginas00050 addr_t dir_virtual = TASK_DATA + (actual->num_data * PAGINA_SIZE);00051 00052 struct user_page *mem;00053 00054 //Posicionarse en el ultimo nodo00055 for (mem=actual->mdata; mem->next ; mem=mem->next);00056 00057 //Pedir memoria al Memory Manager00058 mem->next = umalloc_page (PAGINA_ALLOC, dir_virtual, actual->cr3_backup); 00059
00060 //Si no hay disponible, nos vamos :-(00061 if (!mem->next)00062 return NULL;00063 00064 mem = mem->next;00065 /* 00066 if (kmapmem( mem->dir , dir_virtual, actual->cr3_backup , PAGE_PRES|PAGE_USER|PAGE_RW) != OK) {00067 kprintf("\nTEMP: KmapMem Retorno -1");00068 }00069 */ 00070 actual->num_data++;00071 00072 return (void *) dir_virtual;00073 } 00074 00075 00076 00077 /* Libera una pagina del User Space NO IMPLEMENTADA */00078 int sys_free_page(void *dir)00079 {00080 kprintf("SYS_FREE_PAGE\n");00081 while(1);00082 }00083 00084 00085 // Devuelve la cantidad de paginas libres (utilizada para propositos de depuración)00086 dword sys_free_mem (void)00087 {00088 return kmem_free();00089 }00090
00065 inline int sys_setvar(char *nombre, int valor)00066 {00067 return setvar(convertir_direccion (nombre , actual->cr3_backup), valor);00068 }00069 00070 inline int sys_getvar(char *nombre)00071 {00072 return getvar(convertir_direccion (nombre , actual->cr3_backup));00073 }00074 00075 00076 // Setea una variable en el array variables[] si no existe alli, y modifica su valor en caso de ya encontrarse00077 int setvar(char *nombre, int valor)00078 {00079 int i;00080 00081 if ( (i=find_var(nombre)) ==-1 ) { //Variable no esta definida. Agregarla00082 if ( (i = find_empty_var()) ==-1 ) {00083 if (getvar("vardebug")==1) // Imprimir info de debug ???00084 kprintf("SYS_SETVAR: no hay espacio libre\n");00085 return -1; //No hay un espacio libre00086 }00087 strncpy(variables[i].nombre, nombre, VAR_NAME_MAX);00088 variables[i].valor = valor;00089 if (getvar("vardebug")==1) // Imprimir info de debug ???00090 kprintf("SYS_SETVAR: no estaba definida... definiendola en indice: %d\n", i);00091 return 0;00092 }00093 00094 //Si llegó aquí, la variable esta definida en el indice "i", debo modificarla00095 if (getvar("vardebug")==1) // Imprimir info de debug ???00096 kprintf("SYS_SETVAR: estaba definida ... modificandola en indice: %d\n", i);00097 00098 variables[i].valor = valor;00099 return 0;00100 00101 }00102 00103 int getvar(char *nombre)00104 {00105 int i;00106 00107 if ( (i=find_var(nombre))!=-1) {00108 return variables[i].valor;00109 }00110 00111 return -1;00112 }
00113 00114 // Busca una variable en el array y retorna el indice en caso de exito, o -1 en caso de no encontrarse00115 int find_var (char *nombre)00116 {00117 int i=0;00118 for (i=0 ; i<VARIABLES_MAX ; i++)00119 if (strcmp(variables[i].nombre, nombre)==0)00120 return i;00121 00122 return -1;00123 }00124 00125 // Busca un espacio libre para definir una variable. Devuelve el indice en caso de encontrarlo o -1 en caso contrario00126 int find_empty_var (void)00127 {00128 int i=0;00129 for (i=0 ; i<VARIABLES_MAX ; i++)00130 if (variables[i].valor==-1)00131 return i;00132 return -1;00133 }00134 00135 /*00136 void sys_read_debug(int sector)00137 {00138 00139 kprintf("Leyendo sector %d\n",sector);00140 00141 buffer_block_t *buf = (buffer_block_t *) malloc(sizeof(buffer_block_t));00142 00143 byte *data = (byte *) malloc( 512 * sizeof(byte) );00144 00145 BUFFER_BLOCK_DEVICE(buf)=fd0;00146 BUFFER_BLOCK_PROCESO(buf)=actual;00147 BUFFER_BLOCK_SECTOR(buf)=sector;00148 BUFFER_BLOCK_OPERACION(buf)=IOREAD;00149 BUFFER_BLOCK_BUFFER(buf)=data;00150 00151 insertar_buffer_block(buf);00152 00153 kprintf("Voy a dormir un rato...\n");00154 dormir_task(actual);00155 00156 // Switcheamos de proceso mediante la macro _reschedule quien realiza una llamada00157 // a la interrupción 0x5100158 _reschedule();00159 00160 remover_buffer_block(buf);00161 00162 kprintf("Sector leido ok !!!\n");00163 00164 int i;00165 for(i=0; i<512; i++) {00166 kprintf("%c", data[i]);00167 }00168 00169 kprintf("\n");
00070 kprintf("VOIDO: valor de ESP+3: 0x%x\n", *(_esp+3));00071 kprintf("VOIDO: valor de ESP+4: 0x%x\n", *(_esp+4));00072 kprintf("VOIDO: valor de ESP+5: 0x%x\n", *(_esp+5));00073 kprintf("VOIDO: valor de ESP+6: 0x%x\n", *(_esp+6));00074 kprintf("VOIDO: valor de ESP+7: 0x%x\n", *(_esp+7));00075 kprintf("VOIDO: valor de ESP+8: 0x%x\n", *(_esp+8));00076 kprintf("VOIDO: valor de ESP+9: 0x%x\n", *(_esp+9));00077 kprintf("VOIDO: valor de ESP+10: 0x%x\n", *(_esp+10));00078 kprintf("VOIDO: valor de ESP+11: 0x%x\n", *(_esp+11));00079 kprintf("VOIDO: valor de ESP+12: 0x%x\n", *(_esp+12));00080 kprintf("VOIDO: valor de ESP+13: 0x%x\n", *(_esp+13));00081 kprintf("VOIDO: valor de ESP+14: 0x%x\n", *(_esp+14));00082 kprintf("VOIDO: valor de ESP+15: 0x%x\n", *(_esp+15));00083 kprintf("VOIDO: valor de ESP+16: 0x%x\n", *(_esp+16));00084 kprintf("VOIDO: valor de ESP+17: 0x%x\n", *(_esp+17));00085 00086 unsigned long *p = (unsigned long *) *(_esp+9);00087 p = convertir_direccion (p, actual->cr3_backup);00088 00089 00090 return OK;00091 } 00092 00095 struct int_regs00096 {00097 dword ebx;00098 dword ecx;00099 dword edx;00100 dword edi;00101 dword esi;00102 dword ebp;00103 dword eip;00104 dword cs;00105 dword eflags;00106 dword esp;00107 dword ss;00108 };00109 00110 00112 int sys_fork (void)00113 {00114 cli();00115 00116 struct task_struct_t *new_task;00117 // Punteros al contexto de la tarea (en el stack de modo kernel)00118 struct int_regs *new, *current;00119 struct int_regs_ext *new_ext;00120 word i;00121 00122 // init_new_task devuelve la tarea creada como TASK_STOPPED, la mantendremos así hasta el final 00123 new_task = init_new_task (0,0,0,0,0, actual->descripcion, actual->prioridad);00124 00125 current = (struct int_regs *) ((actual->esp0 & 0xfffff000) + PAGINA_SIZE - sizeof(struct int_regs));00126 new = (struct int_regs *) ((new_task->esp0 & 0xfffff000) + PAGINA_SIZE - sizeof(struct int_regs));00127
00179 }00180 00181 // Pido lugar para las páginas de stack y las copio00182 src = actual->mstack;00183 for( i=0 ; i < new_task->num_stack ; i++) {00184 if (i==0)00185 dest = umalloc_page ( src->flags, src->vdir, new_task->cr3 );00186 else {00187 dest->next = umalloc_page ( src->flags, src->vdir, new_task->cr3 );00188 dest = dest->next;00189 }00190 if (!dest) { //liberar00191 actual->err_no = ENOMEM;00192 return -1;00193 }00194 if (i==0) //Apuntar el mdata al comienzo de la lista00195 new_task->mstack = dest;00196 00197 copy_page ( (void *)dest->dir, (void *)src->dir);00198 src = src->next;00199 }00200 00201 TASK_SIGNALS_ALLOC(new_task);00202 memcpy(TASK_SIGNALS(new_task), TASK_SIGNALS(actual), sizeof(struct task_signals));00203 00204 new_task->pid = get_new_pid();00205 TASK_PARENT(new_task) = actual;00206 new_task->padre = actual;00207 00208 00209 // Inicializo el header de la lista de hijos00210 LIST_INIT (new_task->childs);00211 // Me agrego en la lista de hijos de mi padre00212 LIST_ADD_TAIL(actual->childs, brothers, new_task);00213 00214 // Inicializo el header de la lista de hijos zombies00215 // LIST_INIT (new_task->zombie_header);00216 00217 // Poner en eax del hijo un 0, para que tome 0 como el retorno de fork00218 new_ext->eax = 0;00219 00220 // Despertamos la nueva tarea, recordemos que init_new_task crea las tareas en estado TASK_STOPPED00221 despertar_task( new_task );00222 sti();00223 00224 return (new_task->pid);00225 }00226 00227 00234 int sys_exec (char *nombre)00235 {00236 // kprintf("Esta llamada al sistema no se encuentra disponible, por favor, utilizar execve\n");00237 //}
00397 }00398 00399 //Levantar .DATA00400 lseek(fd, sec_data.s_scnptr,SEEK_SET);00401 for( i=0, mem=new_task->mdata ; i < paginas_data ; i++, mem=mem->next ) {00402 bytes_leidos = read(fd, (void *)mem->dir, PAGINA_SIZE);00403 if (bytes_leidos < 0) { //error de algo.... liberar memoria00404 actual->err_no = EIO;00405 close(fd);00406 return -1;00407 }00408 };00409 00410 // En esa página extra que pedimos para datos, vamos a ubicar una llamada a EXIT00411 // y los argumentos de main y variables de entorno que recibe la tarea00412 // Me ubico en la ultima pagina (la cual usare para exit, argv, etc, etc)00413 00414 for( mem=new_task->mdata ; mem->next ; mem=mem->next);00415 00416 unsigned char *ptr_exit = (unsigned char *) mem->dir;00417 00418 // Ubicamos en el wrapper el codigo de libreria routstd de llamada EXIT:00419 *ptr_exit = 0xb8; // Codigo de operacion: "mov eax, "00420 *(unsigned long *)(ptr_exit+1) = SYS_PROCESS | SYS_EXIT; 00421 *(ptr_exit+5) = 0xbb; // "mov ebx, "00422 *(unsigned long *)(ptr_exit+6) = 0; // parametro que recibe la función EXIT... 0 en este caso00423 *(ptr_exit+10) = 0xcd; // int00424 *(ptr_exit+11) = 0x50; // 0x5000425 00426 // Hardcodeo la llamada al sistema sys_signal_check, la cuál se ejecutará luego de cada handler de seña00427 *(ptr_exit+12) = 0xb8; // Codigo de operacion: "mov eax, "00428 *(unsigned long *)(ptr_exit+13) = SYS_SIGNALS | SYS_SIGNAL_CHECK; 00429 *(ptr_exit+17) = 0xcd; // int00430 *(ptr_exit+18) = 0x50; // 0x5000431 *(ptr_exit+19) = 0x5e; // pop esi // Con estos popeos recupero los registros de propósito general00432 *(ptr_exit+20) = 0x5f; // pop edi // que fueron pusheados antes de ejecutar un handler de señal00433 *(ptr_exit+21) = 0x5a; // pop edx00434 *(ptr_exit+22) = 0x59; // pop ecx00435 *(ptr_exit+23) = 0x5b; // pop ebx00436 *(ptr_exit+24) = 0x58; // pop eax00437 *(ptr_exit+25) = 0xc3; // retn00438 00439 // Aloco la estructura que almacena info de señales00440 TASK_SIGNALS_ALLOC(new_task);00441 memset (TASK_SIGNALS(new_task), 0, sizeof(struct task_signals));
00442 TASK_SIGADDR(new_task) = GET_OFFSET(ptr_exit + 12) + mem->vdir;00443 00444 // new_task->sigcheck_addr = GET_OFFSET(ptr_exit + 12) + mem->vdir;00445 00446 // Argumentos y demas facturetas 00447 unsigned int argc = 0;00448 char **argv, **envp;00449 00450 argv = NULL;00451 envp = NULL;00452 00453 // Mando al fondo del Stack a envp00454 unsigned long *qwe = (unsigned long *) (new_task->mstack->dir + 4096 - 4);00455 *(qwe--) = (unsigned long) envp;00456 *(qwe--) = (unsigned long) argv;00457 *(qwe--) = (unsigned long) argc;00458 *qwe = (unsigned long) mem->vdir;00459 00460 if (actual==pre_init_task) { // Si el proceso es init, caso particular ya que init no tiene formato de tarea00461 TASK_PARENT(new_task) = actual;00462 new_task->pid = get_new_pid();00463 new_task->padre = pre_init_task;00464 // A los procesos que "executa init" le inicializo la lista de hijos, ya que no la van a reemplazar00465 // a init00466 LIST_INIT (new_task->childs);00467 // Agregarse como hijo de init00468 LIST_ADD (actual->childs, brothers, new_task);00469 }00470 else { // Si es cualquier otro proceso, el nuevo proceso debe heredar su pid y ppid00471 TASK_PARENT(new_task) = TASK_PARENT(actual);00472 new_task->pid = actual->pid;00473 new_task->padre = actual->padre;00474 // El nuevo proceso se hace cargo de todos los hijos00475 // new_task->zombie_header = actual->zombie_header;00476 //LIST_REPLACE(brothers, actual, new_task);00477 // START_TICKS_COUNT();00478 LIST_DEL(actual->padre->childs, brothers, actual);00479 LIST_ADD_TAIL(actual->padre->childs, brothers, new_task);00480 // END_TICKS_COUNT();00481 // PRINT_TICKS_COUNT();00482 }00483 00484 close(fd);00485 00486 // Despertamos la nueva tarea, recordemos que init_new_task crea las tareas en estado TASK_STOPPED00487 despertar_task( new_task );00488 00489 #ifdef RUNSTATS00490 if ( getvar("debugexec") == 2 ) {
00491 long long int counterfinal;00492 rdtscl(counterfinal);00493 kprintf("0x%llx ",counterfinal-counter);00494 } 00495 #endif00496 00497 // Recordemos que el proceso que ejecuto el exec debe terminar (excepto que sea el init)00498 if (actual->pid!=1) {00499 00500 cli();00501 sys_exit_mm(); // libero la memoria00502 remover_task (actual); // lo saco de la lista de tareas del scheduler00503 kfree_page ((addr_t)actual); // libero el task_struct00504 sti();00505 _reschedule(); 00506 }00507 00508 00509 return OK;00510 }00511 00512 00513 00514 00516 void sys_perror (char *str)00517 {00518 str = convertir_direccion( str , actual->cr3_backup);00519 perror(str);00520 }00521 00526 int sys_renice (word pid, word prioridad)00527 {00528 // kprintf("TEMP SYS_RENICE. PID: %d\tPRI: %d\n", pid, prioridad);00529 task_struct_t *tmp;00530 if ( (tmp = encontrar_proceso_por_pid(pid))==NULL ) {00531 actual->err_no = ESRCH;00532 return -1;00533 }00534 // Esto es solo a modo de prueba ya que en routix por ahora el TASK_STOPPED cumple las veces de TASK_INTERRUMIPLE00535 if (prioridad < 1)00536 dormir_task(tmp);00537 tmp->prioridad = prioridad; 00538 return OK;00539 }00540 00542 inline pid_t sys_get_pid (void)00543 {00544 return actual->pid;00545 }00546 00548 inline pid_t sys_get_ppid (void)00549 {00550 return TASK_PPID(actual);00551 }00552
00554 // Llamada al sistema Exit y funciones relacionadas00555 //00557 00559 int zombie_queue_len = 0;00560 00561 00567 /*00568 * \relates sys_exit_notify00569 * \relates sys_exit_mm00570 * \relates sys_wait00571 */00572 void sys_exit (int valor)00573 {00574 cli();00575 actual->retorno = valor; 00576 00577 sys_exit_mm();00578 sys_exit_notify();00579 sys_exit_fs();00580 dormir_task(actual);00581 actual->estado = TASK_ZOMBIE;00582 sti();00583 _reschedule();00584 } 00585 00588 int sys_exit_mm(void)00589 {00590 // Libero la memoria utilizada por los handlers de señales (antes esto lo ejcutaba al final de esta función00591 // y a veces me cagaba todo.... averiguar porque !!!00592 TASK_SIGNALS_FREE(actual);00593 00594 00595 struct user_page *aux, *tmp;00596 // Liberar las paginas de código00597 for (aux=tmp=actual->mcode ; aux && tmp ; aux=tmp) {00598 tmp=aux->next;00599 // Desmapear la direccion fisica de la logica00600 kunmapmem(aux->vdir, actual->cr3_backup);00601 // Si Esta habilitada la variable de entorno "__exit", imprimir info00602 if (getvar("__exit")==1)00603 kprintf("Liberando Codigo dir: 0x%x\tvdir: 0x%x\n", aux->dir,aux->vdir);00604 ufree_page(aux);00605 }00606 // Liberar de esta tarea las paginas de datos00607 for (aux=tmp=actual->mdata ; aux && tmp ; aux=tmp) {00608 tmp=aux->next;00609 kunmapmem(aux->vdir, actual->cr3_backup);00610 if (getvar("__exit")==1)00611 kprintf("Liberando Datos dir: 0x%x\tvdir: 0x%x\n", aux->dir,aux->vdir);00612 ufree_page(aux);00613 }00614 // Liberar de esta tarea las paginas de codig00615 for (aux=tmp=actual->mstack ; aux && tmp ; aux=tmp) {00616 tmp=aux->next;00617 kunmapmem(aux->vdir, actual->cr3_backup);00618 if (getvar("__exit")==1)
00619 kprintf("Liberando Stack dir: 0x%x\tvdir: 0x%x\n", aux->dir,aux->vdir);00620 ufree_page(aux);00621 }00622 // Desmapear del directorio la pagina usada para los wrappers (de exit por ej)00623 kunmapmem (TASK_TEXT - PAGINA_SIZE, actual->cr3_backup);00624 00625 00626 // Libero el directorio de páginas utilizado por la tarea00627 kfree_page (actual->cr3_backup);00628 return 0;00629 }00630 00635 int sys_exit_notify (void)00636 {00637 // Aumento la cuenta que lleva el sistema sobre zombies (proposito de debug)00638 zombie_queue_len++;00639 00640 int value;00641 pid_t pid;00642 task_struct_t *child, *aux;00643 // Tomar la condición de salida de mis hijos zombies00644 _LIST_FOREACH(child, actual->childs, brothers) {00645 aux = LIST_NEXT(brothers, child);00646 00647 // Si el proceso está zombie, tomo su condidción de salida y lo libero00648 if (child->estado == TASK_ZOMBIE) {00649 if (getvar("exit")==1)00650 kprintf("Esperando al hijo: %d\n", child->pid);00651 // Liberar el primer hijo que se encuentre00652 pid = sys_waitpid(-1,&value,WNOHANG);00653 00654 if (getvar("exit")==1)00655 kprintf("EXIT_NOTIFY: termino hijo %d con: %d\n", pid, value);00656 }00657 // Si está en otro estado, se lo paso a init00658 else {00659 LIST_DEL(actual->childs, brothers, child);00660 LIST_ADD_TAIL(init_task->childs, brothers, child);00661 child->padre = init_task;00662 }00663 00664 child = aux;00665 }00666 // Envio la señal SIGCHLD al padre00667 _sys_kill(actual->padre, SIGCHLD);00668 00669 // Despertar a init 00670 wakeup_init();00671 return 0;00672 } 00673
00676 int sys_exit_fs ()00677 {00678 int i;00679 for (i=0 ; i < MAX_FILES_POR_TAREA ; i++)00680 if (actual->open_files[i]) {00681 free(actual->open_files[i]);00682 file_free++;00683 }00684 00685 return 0;00686 }00687 00688 00694 pid_t sys_waitpid (pid_t pid, int *status, int options)00695 {00696 _cli();00697 status = convertir_direccion( status , actual->cr3_backup);00698 00699 struct task_struct_t *child;00700 00701 int zombie_found = 0;00702 00703 while (1) {00704 // Recorrer la lista de procesos hijos, en busca de zombies00705 LIST_FOREACH (child, actual->childs, brothers) {00706 // Si el hijo no es zombie, seguir buscando00707 if (TASK_STATE(child)!=TASK_ZOMBIE)00708 continue;00709 // Buscar cualquier hijo (pid=-1) o alguno en particular (pid>1)? 00710 if (pid>1 && pid!=TASK_PPID(child))00711 break;00712 zombie_found = 1;00713 break;00714 }00715 if (zombie_found==1) 00716 break;00717 if (getvar("__wait")==1)00718 kprintf("SYS_WAIT: Mi PID es: %d\tNo encontre hijo zombie\n", actual->pid);00719 00720 // Si es no bloqueante, debo irme con -1 al no haberlo encontrado00721 if (options==WNOHANG) { 00722 if (getvar("__wait")==1)00723 kprintf("SYS_WAIT: Me voy por el WNOHANG....\n");00724 actual->err_no = ECHILD;00725 _sti();00726 return -1;00727 }00728 _sti();00729 _reschedule(); // espero un rato00730 _cli();00731 }00732 00733 // Mientras que aux queda apuntando al nodo anterior, tmp lo hace al nodo en cuestión00734 pid_t child_pid = TASK_PID(child);
00901 }00902 }00903 00904 //Verificar que posea una seccion de codigo, una de datos inicializados y sin inicializar00905 if (verificacion != (COFF32_TEXT | COFF32_DATA | COFF32_BSS) ) {00906 close(fd);00907 actual->err_no = ENOEXEC;00908 return -1;00909 }00910 00911 //Verificar que haya memoria suficiente 00912 if (kmem_free() < ((actual->open_files[fd]->size / PAGINA_SIZE)+8) ) {00913 actual->err_no = ENOMEM;00914 close(fd);00915 return -1;00916 }00917 00918 // Aca voy a guardar la cantidad de paginas requeridas por cada segmento00919 word paginas_texto, paginas_datos;00920 00921 paginas_texto = sec_text.s_size / PAGINA_SIZE;00922 if ( sec_text.s_size % PAGINA_SIZE ) {00923 paginas_texto++;00924 }00925 //Tamaño en bytes del .DATA + .BSS00926 int size_datos = sec_data.s_size + sec_bss.s_size;00927 00928 //Cantidad de paginas requeridas por .DATA + .BSS00929 paginas_datos = size_datos / PAGINA_SIZE;00930 if ( size_datos % PAGINA_SIZE )00931 paginas_datos++;00932 00933 paginas_datos++; // Uso una pagina de datos para (argc, **argv, **env) y llamada implicita a EXIT00934 00935 //Cantidad de paginas requeridas solo por .DATA00936 int paginas_data = sec_data.s_size / PAGINA_SIZE;00937 if ( sec_data.s_size % PAGINA_SIZE )00938 paginas_data++;00939 00940 task_struct_t *new_task;00941 struct user_page *mem;00942 00943 // Poner el nombre de archivo como descripcion00944 char nuevo_nombre[13];00945 tomar_nombre_tarea(nombre, nuevo_nombre);00946 00947 // init_new_task devuelve la tarea creada como TASK_STOPPED, la mantendremos así hasta el final 00948 new_task = init_new_task(DESC_CODE_USUARIO, DESC_DATA_USUARIO, TASK_TEXT, TASK_STACK + 4096 - 16, 0x202,\00949 nuevo_nombre, START_PRIORITY);00950 00951 if (!new_task) { //liberar00952 return -1;00953 }00954
01004 01005 // Me ubico en la ultima pagina (la cual usare para exit, argv, etc, etc)01006 for( mem=new_task->mdata ; mem->next ; mem=mem->next);01007 01008 unsigned char *ptr_exit = (unsigned char *) mem->dir;01009 01010 // Ubicamos en el wrapper el codigo de libreria routstd de llamada EXIT:01011 *ptr_exit = 0xb8; // Codigo de operacion: "mov eax, "01012 *(unsigned long *)(ptr_exit+1) = SYS_PROCESS | SYS_EXIT; 01013 *(ptr_exit+5) = 0xbb; // "mov ebx, "01014 *(unsigned long *)(ptr_exit+6) = 0; // parametro que recibe la función EXIT... 0 en este caso01015 *(ptr_exit+10) = 0xcd; // int01016 *(ptr_exit+11) = 0x50; // 0x5001017 01018 // Hardcodeo la llamada al sistema sys_signal_check, la cuál se ejecutará luego de cada handler de seña01019 *(ptr_exit+12) = 0xb8; // Codigo de operacion: "mov eax, "01020 *(unsigned long *)(ptr_exit+13) = SYS_SIGNALS | SYS_SIGNAL_CHECK; 01021 *(ptr_exit+17) = 0xcd; // int01022 *(ptr_exit+18) = 0x50; // 0x5001023 *(ptr_exit+19) = 0x5e; // pop esi // Con estos popeos recupero los registros de propósito general01024 *(ptr_exit+20) = 0x5f; // pop edi // que fueron pusheados antes de ejecutar un handler de señal01025 *(ptr_exit+21) = 0x5a; // pop edx01026 *(ptr_exit+22) = 0x59; // pop ecx01027 *(ptr_exit+23) = 0x5b; // pop ebx01028 *(ptr_exit+24) = 0x58; // pop eax01029 *(ptr_exit+25) = 0xc3; // retn01030 01031 TASK_SIGNALS_ALLOC(new_task);01032 memset (TASK_SIGNALS(new_task), 0, sizeof(struct task_signals));01033 TASK_SIGADDR(new_task) = GET_OFFSET(ptr_exit + 12) + mem->vdir;01034 01035 #define EXIT_CALL_LEN 1201036 #define SIGNAL_CHECK_LEN 1401037 01038 // Traspaso de argumentos y variables de entorno01039 arg_p = convertir_direccion(arg_p, actual->cr3_backup);01040 env_p = convertir_direccion(env_p, actual->cr3_backup);01041 01042 char **argv, **envp; 01043 // Cantidad de argumentos, más el nombre del ejecutable01044 int argc = count_elements (arg_p) + 1;01045 // Cantidad de variables de entorno seteadas01046 int envc = count_elements (env_p);01047 01048 char *aux_dest;01049 argv = (char **) ptr_exit + EXIT_CALL_LEN + SIGNAL_CHECK_LEN;01050 aux_dest = (char *)(argv + argc + 1); // Empiezo a poner los strings despues del vector de punteros argv01051 strcpy(aux_dest, nuevo_nombre);
01052 int z;01053 01054 for (z=0 ; z<argc ; z++) {01055 argv[z] = GET_OFFSET(aux_dest) + (char *) mem->vdir;01056 aux_dest += strlen(aux_dest) + 1;01057 strcpy(aux_dest, convertir_direccion(arg_p[z], actual->cr3_backup));01058 }01059 // Coloco el NULL como ultimo elemento de vector de punteros01060 argv[z+2] = NULL;01061 01062 // Alinear a 4 bytes01063 envp = (char **) ALINEAR_4(aux_dest + strlen(aux_dest) + 4);01064 01065 aux_dest = (char *) (envp + envc + 2);01066 for (z=0 ; z<envc ; z++) {01067 strcpy(aux_dest, convertir_direccion(env_p[z], actual->cr3_backup));01068 envp[z] = GET_OFFSET(aux_dest) + (char *) mem->vdir;01069 aux_dest += strlen(aux_dest) + 1;01070 }01071 // Coloco el NULL como ultimo elemento de vector de punteros01072 envp[z+1] = NULL;01073 // TEMPORAL (no recuerdo porque le puse temporal !!!)01074 argv = (char **)(GET_OFFSET(argv) + (char *) mem->vdir);01075 envp = (char **)(GET_OFFSET(envp) + (char *) mem->vdir);01076 01077 // Mando al fondo del Stack a envp, argv, argc y el RET a sys_exit01078 unsigned long *qwe = (unsigned long *) (new_task->mstack->dir + 4096 - 4);01079 *(qwe--) = (unsigned long) envp;01080 *(qwe--) = (unsigned long) argv;01081 *(qwe--) = (unsigned long) argc;01082 *qwe = (unsigned long) mem->vdir;01083 /*01084 // El nuevo proceso debe heredar el PID y el PPID del llamante, excepto que sea INIT01085 if (actual->pid==1) { // Si el proceso es init, caso particular ya que init no tiene formato de tarea01086 TASK_PARENT(new_task) = actual;01087 TASK_PPID(new_task) = actual->pid;01088 new_task->pid = get_new_pid();01089 new_task->padre = init_task;01090 }01091 else { // Si es cualquier otro proceso, el nuevo proceso debe heredar su pid y ppid01092 TASK_PARENT(new_task) = TASK_PARENT(actual);01093 new_task->pid = actual->pid;01094 new_task->padre = actual->padre;01095 }01096 */01097 if (actual==pre_init_task) { // Si el proceso es init, caso particular ya que init no tiene formato de tarea
01098 TASK_PARENT(new_task) = actual;01099 new_task->pid = get_new_pid();01100 new_task->padre = pre_init_task;01101 // A los procesos que "executa init" le inicializo la lista de hijos, ya que no la van a reemplazar01102 // a init01103 LIST_INIT (new_task->childs);01104 // Agregarse como hijo de init01105 LIST_ADD (actual->childs, brothers, new_task);01106 }01107 else { // Si es cualquier otro proceso, el nuevo proceso debe heredar su pid y ppid01108 TASK_PARENT(new_task) = TASK_PARENT(actual);01109 new_task->pid = actual->pid;01110 new_task->padre = actual->padre;01111 // El nuevo proceso se hace cargo de todos los hijos01112 // new_task->zombie_header = actual->zombie_header;01113 //LIST_REPLACE(brothers, actual, new_task);01114 // START_TICKS_COUNT();01115 LIST_DEL(actual->padre->childs, brothers, actual);01116 LIST_ADD_TAIL(actual->padre->childs, brothers, new_task);01117 // END_TICKS_COUNT();01118 // PRINT_TICKS_COUNT();01119 }01120 01121 01122 close(fd);01123 01124 // Despertamos la nueva tarea, recordemos que init_new_task crea las tareas en estado TASK_STOPPED01125 despertar_task( new_task );01126 01127 // Recordemos que el proceso que ejecuto el exec debe terminar (excepto que sea el init)01128 if (actual->pid!=1) {01129 cli();01130 sys_exit_mm(); // libero la memoria01131 remover_task (actual); // lo saco de la lista de tareas del scheduler01132 kfree_page ((addr_t)actual); // libero el task_struct01133 sti();01134 _reschedule(); 01135 }01136 return OK;01137 }01138 01140 inline int count_elements (char **vector)01141 {01142 int i;01143 for ( i=0 ; vector[i]!=NULL && i<10 && vector; i++);01144 return i;01145 }
00078 TASK_SIGPENDING(proceso) = TASK_SIGPENDING(proceso) | (1 << signo);00079 // Aqui "encolo" las señales pendientes de un mismo tipo00080 TASK_SIGNAL_COUNT(proceso, signo)++;00081 }00082 return 0;00083 }00084 00085 00092 void *sys_signal (int signo, void (*func)()) 00093 {00094 void (*old_func)() = TASK_SIGNAL_HANDLER(actual, signo);00095 // Verificar si la función "func" pertenece al espacio de direcciones del usuario00096 TASK_SIGNAL_HANDLER(actual, signo) = func;00097 if (getvar("__sig")==1)00098 kprintf("Direccion de la funcion especificada: 0x%x\n", func);00099 return old_func;00100 }00101 00104 void sys_signal_correct(void)00105 {00106 //kprintf("SYS_SIGNAL_CORRECT");00107 00108 struct int_regs_ext *contexto = GET_CONTEXT(actual);00109 00110 contexto->esp += 4;00111 00112 //__debug();00113 }00114 00115 00121 void sys_signal_check (void)00122 {00123 struct int_regs_ext *context = GET_CONTEXT(actual);00124 00125 // Porque es esto ??? Bla bla... no estoy seguro...00126 context->esp += 4;00127 00128 // Obtener la dirección física del stack de la tarea00129 unsigned long *user_ptr = (unsigned long *) convertir_direccion( context->esp, actual->cr3_backup); 00130 00131 // Obtengo el valor de sigmask valido antes de ejecutar este handler que acaba de finalizar00132 TASK_SIGMASK(actual) = *(user_ptr++);00133 00134 // Si no hay otras selañes pendientes... reacomodo el stack y retorno00135 if (check_sigpending(actual, 0)<1) {00136 context->esp += 4;00137 // kprintf("SYS_SIGNAL_CHECK: No hay otras pendientes\n");00138 }00139 // Si hay otra señal pendiente, pongo el EIP del contexto para que apunte al nuevo handler00140 else {00141 // kprintf("SYS_SIGNAL_CHECK: HAY !!! pendientes\n");
00064 return OK;00065 }00066 00067 int sys_usleep(int usegundos)00068 {00069 timer_t *timer;00070 00071 // Creamos el timer00072 if ( (timer=create_timer(USECONDS_TO_TICKS(usegundos), actual, despertar, NULL))==NULL ) {00073 return -1;00074 }00075 00076 // Dormimos la tarea actual00077 dormir_task(actual);00078 00079 // Switcheamos de proceso mediante la macro _reschedule quien realiza una llamada00080 // a la interrupción 0x5100081 _reschedule();00082 00083 // Volvemos ! el timer se venció. Este es nuestro punto de retorno.00084 // todavía estamos en modo kernel en la llamada al sistema00085 00086 return OK;00087 }00088 00089 00090 00091 int sys_proc_dump(void)00092 {00093 task_struct_t *tmp;00094 00095 char *estados[] = { "TASK_RUNNING", "TASK_STOPPED", "TASK_SLEEPING", "TASK_ININTERRUMPIBLE", "TASK_ZOMBIE", \00096 "TASK_CLEAN" };00097 00098 kprintf("pid ppid descripcion estado pri cuenta cpu sigpending sigmask\n");00099 for (tmp = tareas_inicio; tmp != NULL ; tmp=tmp->proxima ) {00100 00101 kprintf("%d\t%d\t%s", tmp->pid, tmp->padre->pid, tmp->descripcion);00102 kprintf(" %s %d %d %d",estados[tmp->estado], tmp->prioridad,tmp->cuenta,tmp->tiempo_cpu);00103 kprintf(" 0x%x\t\t0x%x\n", TASK_SIGPENDING(tmp), TASK_SIGMASK(tmp));00104 00105 }00106 00107 return 0;00108 }00109 00110 // Sys Proc Dump Verbose00111 int sys_proc_dump_v(int pid)00112 {00113 task_struct_t *tmp;00114 //Ubicar el pid en la lista de procesos00115
00056 #define SYS_SIGNALS_MAX 5 // Cantidad de llamadas en el grupo SIGNALS. Debe ser siempre el ultimo valor00057 00058 00059 // Funciones de grupo TIMER00060 #define SYS_SLEEP 000061 #define SYS_PROC_DUMP 100062 #define SYS_KILLL 2 //KILL no va aquí (debo crear un grupo SYS_SIGNAL o lo ubico en SYS_MEM ?)00063 #define SYS_USLEEP 300064 #define SYS_PROC_DUMP_V 400065 #define SYS_TIMER_DUMP 500066 #define SYS_TIMER_MAX 6 // Cantidad de llamadas en el grupo TIMER. Debe ser siempre el ultimo valor00067 00068 // Funciones de grupo MEM00069 #define SYS_MALLOC_PAGE 000070 #define SYS_FREE_PAGE 100071 #define SYS_FREE_MEM 200072 #define SYS_MEM_MAX 3 // Cantidad de llamadas en el grupo MEM. Debe ser siempre el ultimo valor00073 00074 // Funciones de grupo MISC00075 #define SYS_SETVAR 000076 #define SYS_GETVAR 100077 #define SYS_READ_DEBUG 2 00078 #define SYS_MISC_MAX 3 // Cantidad de llamadas en el grupo MEM. 00079 00080 void syscall (void);00081 int sys_no_existe (dword numero);00082 00083 #endif
00178 if (init_task->estado!=TASK_RUNNING) {00179 init_task->estado=TASK_RUNNING;00180 tareas_activas++;00181 }00182 sti();00183 }00184 00185 00186 task_struct_t *init_new_task(word cs, word ds, dword eip, dword esp, dword eflags, char *descripcion, word prioridad)00187 {00188 task_struct_t *nueva;00189 dword esp0;00190 00191 // Alocamos una página completa (para el descriptor de tarea y el stack en modo kernel).00192 if ( (nueva = (task_struct_t *) kmalloc_page()) == NULL ) {00193 return NULL;00194 }00195 00196 //Provisorio, debe ser más performante !!!00197 memset(nueva, 0, PAGINA_SIZE);00198 00199 // Selector de Datos (DPL=0) y esp0 al final de la página00200 esp0 = (dword) nueva + 4092;00201 00202 // Crear un directorio de paginas y alojar su dir en cr300203 if ( (nueva->cr3 = (addr_t) make_pdt()) == NULL ) {00204 kfree_page((addr_t)nueva);00205 return NULL;00206 } 00207 00208 nueva->cr3_backup = nueva->cr3;00209 00210 // Esto debe hacerse sólo en la llamada a fork (o en exec si el que llama es init)00211 // nueva->pid = get_new_pid();00212 strcpy( nueva->descripcion, descripcion);00213 00214 // Ponemos la tarea a correr00215 //nueva->estado = TASK_RUNNING;00216 // Stoppeada hasta no terminar todo el proceso de creacion, la debe despertar otra funcion00217 // como por ejemplo sys_exec()00218 nueva->estado = TASK_STOPPED;00219 00220 nueva->prioridad = prioridad;00221 00222 nueva->err_no = 0;00223 00224 // Cantidad de ticks que tiene permitido usar la cpu en forma continua00225 nueva->cuenta = prioridad;00226 nueva->tiempo_cpu = 0;00227 00228 // Inicializamos la estructura que contiene el contexto de hardware de la tarea00229 nueva->esp0 = (dword) inicializar_task((dword *)esp0,cs,ds,eip,esp,eflags); //el ultimo 0 es ebp (provisorio)00230 00231 word j;
00163 task_signals_free++; 00164 #define TASK_SIGNALS_INIT(task) memset(TASK_SIGNALS(task), 0, sizeof(struct task_signals))00165 00166 #define TASK_SIGNAL_COUNT(task, signo) ((TASK_SIGNALS(task)->sigcount)[signo])00167 00168 struct std_header {00169 struct std_header *next;00170 struct std_header *prev;00171 }; 00172 00173 00174 typedef struct task_struct_t00175 {00176 dword esp0;00177 dword cr3;00178 dword cr3_backup;00179 pid_t pid;00180 struct task_struct_t *proxima;00181 char descripcion[MAX_DESCRIPTION];00182 byte estado;00183 word prioridad;00184 word cuenta;00185 dword tiempo_cpu;00186 struct file *open_files [MAX_FILES_POR_TAREA]; //definido en file.h00187 word num_code, num_data, num_stack; //Cantidad de paginas de Codigo, Datos y stack00188 int err_no;00189 struct user_page *mcode;00190 struct user_page *mdata;00191 struct user_page *mstack;00192 struct task_struct_t *padre;00193 int retorno;00194 // LIST_NEW(struct zombie_queue) zombie_header;00195 LIST_DATA(task_struct_t) io_pending; // Este proceso puede pertenecer a una lista00196 // de entrada/salida, estando pendiente (mientras00197 // se lee o escribe de disco por medio de disco)00198 // ver "blockcache.c".00199 LIST_NEW(struct task_struct_t) childs; // Header de una lista con los hijosl proceso00200 LIST_DATA(task_struct_t) brothers; // Nodo de la lista que contiene a todos sus "bros"00201 00202 struct task_signals *_signals;00203 /*00204 struct sigaction signals[SIGMAX];00205 unsigned long sigpending; //Señales pendientes00206 addr_t sigcheck_addr; //Aca guardo la dir del hardcode de la llamada a SYS_SIGNALS | SYS_SIGNAL_CHECK 00207 sigset_t sigmask;00208 */00209
00267 00268 00269 // Direcciones logicas de los segmentos del espacio de usuario00270 #define TASK_TEXT 0x8000000000271 #define TASK_DATA 0x8800000000272 #define TASK_STACK 0x9000000000273 00274 // Ubicacion logica de una pagina de kernel que poseera permisos de usuario para contener algunos wrappers 00275 // (como por ejemplo, una llamada a Syscall EXIT cuando termina el main de una tarea).00276 // EL pedido de la pagina, y la asignacion del código de EXIT está realizado en Kmain.c00277 #define EXIT_TASK (TASK_TEXT - PAGINA_SIZE)00278 00279 // Estructura utilizada para almacenar una cola de procesos zombies (Siempre hay un primer nodo)00280 struct zombie_queue00281 {00282 pid_t ppid; // padre del proceso zombiee00283 task_struct_t *task_struct; // task struct del proceso00284 LIST_DATA (zombie_queue) zombie_queue; 00285 };00286 00287 00288 #define GET_CONTEXT(task) ((struct int_regs_ext *) ((task->esp0 & 0xfffff000) + PAGINA_SIZE - sizeof(struct int_regs_ext)))00289 00290 // Definiciones para debuggear memory leaks00291 00292 extern int num_mallocs, num_frees;00293 extern int num_alloc_signals, num_free_signals;00294 00295 #endif
00183 return(caracter); 00184 }00185 00186 // Sino agregamos un nodo a la lista de eventos y esperamos00187 00188 // Alocamos un nuevo nodo en memoria00189 nuevo = (event_t *) malloc( sizeof(event_t) );00190 00191 // Error al intentar alocar memoria (setear errno)00192 if ( nuevo == NULL ) {00193 return -1;00194 }00195 00196 // Dispositivo TECLADO00197 nuevo->dispositivo = TECLADO;00198 nuevo->proceso=actual;00199 00200 // Finalmente lo agregamos a la lista de eventos00201 insertar_evento(nuevo);00202 00203 // y nos ponemos a dormir00204 dormir_task(actual);00205 00206 // Switcheamos de proceso mediante la macro _reschedule quien realiza una llamada00207 // a la interrupción 0x5100208 _reschedule();00209 00210 // Volvimos ! Hay por lo menos una tecla en el buffer, eliminamos el nodo de la lista de eventos00211 remover_evento(nuevo);00212 00213 // Liberamos el espacio00214 free(nuevo);00215 00216 // Devolvemos finalmente la tecla ingresada00217 __asm__ __volatile__("cli");00218 --ptrbufferTeclado;00219 caracter = *ptrbufferTeclado;00220 __asm__ __volatile__("sti");00221 00222 return(caracter);00223 }00224 */00225 char *gets(char *s)00226 {00227 unsigned char valor;00228 char *string=s;00229 char flag=1;00230 00231 while ( flag ) {00232 valor = getchar();00233 00234 if ( valor == '\b' ) {00235 if ( string != s ) {00236 string--;00237 putchar(valor);00238 }00239 } 00240 else if ( valor == '\0' ) { flag--; }00241
00114 00115 // Correcion por año bisiesto00116 if ( (mes > FEBRERO) && es_bisiesto(1900+anio) ) dias++;00117 00118 return dias;00119 }00120 00121 00122 00123 // Calculo del tod00124 // el tod representa la cantidad de segundos transcurridos desde un momento hasta el00125 // 1 de enero de 1970.00126 // Para entender la forma de calcularlo veamos un ejemplo:00127 // queremos calcular el tod de la fecha: 25/09/1979 07:49:1500128 //00129 // bien, primero que todo:00130 //00131 // 1979 - 1970 = 9 años transcurridos00132 // 70-71 -- 71-72 -- 72-73 -- 73-74 -- 74-75 -- 75-76 -- 76-77 -- 77-78 -- 78-7900133 // o sea que la cantidad de segundos hasta el inicio del 79 son:00134 //00135 // tod += 9 * 365 * 24 * 60 * 6000136 //00137 // bien, no olvidemos los años bisiestos, que desde el 70 al 78 son 1972 - 197600138 // con lo que ajustando el tod anterior tenemos:00139 //00140 // tod += 2 * 1 * 24 * 60 * 60 dos días de ajuste00141 //00142 // Bien, ahora debemos calcular la cantidad de segundos desde el Enero de 1979 hasta Septiembre de 197900143 // tod += dias_por_mes[ENERO] * 24 * 60 * 60 + dias_por_mes[FEB] * 24 * 60 * 60 + ... + dias_por_mes[AGOS] * 24 * 60 * 6000144 // como antes, si el año es bisiesto y SI PASAMOS POR FEBRERO debemos corregir:00145 // tod += 1 * 24 * 60 * 6000146 //00147 // Ahora, la cantidad de segundos desde el 1ero de Septiembre al 25 de Septiembre00148 // tod += 24 * 24 * 60 * 6000149 // 00150 //00151 // Ahora la cantidad de segundos desde las 00:00 del 25 de Septiembre a las 07:00 del mismo00152 // tod += 7 * 60 * 6000153 //00154 // Ahora la cantidad de segundos desde los 0 minutos hasta los 49 minutos00155 // tod += 49 * 60;00156 //00157 // Finalmente sumamos la cantidad de segundos:00158 // tod += 1500159 //00160 // y obtuvimos el tod buscado.00161 time_t mktime(struct tm *tm)00162 {00163 int tmp;00164 time_t tod=0;
00053 int tm_min; /* minutes after the hour - [0, 59] */00054 int tm_hour; /* hour since midnight - [0, 23] */00055 int tm_mday; /* day of the month - [1, 31] */00056 int tm_mon; /* months since January - [0, 11] */00057 int tm_year; /* years since 1900 */00058 int tm_wday; /* days since Sunday - [0, 6] */00059 int tm_yday; /* days since January 1 - [0, 365] */00060 int tm_isdst; /* flag for alternate daylight savings time */00061 };00062 00063 typedef int time_t;00064 00065 // Definimos los tipos00066 struct tm *localtime(const time_t *clock);00067 time_t mktime(struct tm *tm);00068 int day_of_year(int anio, int mes, int dia);00069 char *asctime(const struct tm *tm);00070 00071 inline void actualizar_reloj();00072 int es_bisiesto(int year);00073
00001 /* timer.c */00002 00003 /* Manejo de la interrupcion de timertick y timers */00004 00005 #include <routix/system.h>00006 #include <routix/8254.h>00007 #include <routix/8259.h>00008 #include <routix/debug.h>00009 #include <routix/task.h>00010 #include <routix/kalloc.h>00011 #include <routix/time.h>00012 #include <routix/sched.h>00013 #include <routix/atomic.h>00014 #include <routix/timer.h>00015 #include <sys/list.h>00016 00017 // ojo que los jiffies podrian tener un overflow !00018 // a los 497 dias de estar corriendo00019 dword jiffies=0;00020 00021 // Puntero al inicio de la lista de timers00022 volatile timer_t *timer_inicio=NULL;00023 00024 // Lista de timers00025 LIST_NEW(timer_t) timer_list = LIST_INITIALIZER;00026 00027 void actualizar_timers(void);00028 inline static int insert_timer(timer_t *nuevo);00029 inline static int remove_timer(timer_t *timer);00030 00031 00032 void timertick_handler()00033 {00034 00035 endOfInterrupt();00036 00037 // Incrementamos el contador de jiffies00038 jiffies++;00039 00040 // Actualizamos el reloj00041 if ( ! (jiffies % FINTERRUPCION) ) actualizar_reloj(jiffies);00042 00043 // Revisamos los timers00044 actualizar_timers();00045 00046 // Chequeamos si el floppy tiene algo pendiente, en realidad colocarlo00047 // aca tiene un bajo rendimiento, solo para pruebas00048 floppy_procesar_buffer();00049 00050 // Verificamos si hay actualmente una tarea corriendo antes de modificar00051 // sus estadisticas00052 if ( actual ) {00053 // Actualizamos la cuenta del proceso actual00054 actual->cuenta -= 1;00055 actual->tiempo_cpu++;00056
00057 // Si se venció la cuenta, la reestablecemos y pasamos el control al scheduler00058 if ( actual->cuenta <= 0 ) {00059 // Reiniciamos la cuenta00060 actual->cuenta = actual->prioridad;00061 scheduler();00062 __asm__ ("nop");00063 }00064 }00065 00066 // Si no la hubiese, devolvemos el control al scheduler00067 else {00068 scheduler();00069 }00070 00071 }00072 00073 /*00074 void actualizar_timers(void)00075 {00076 timer_t *tmp;00077 00078 // Chequeamos si hay timers activos00079 if ( timer_inicio == NULL ) {00080 return;00081 } 00082 00083 for ( tmp=timer_inicio; tmp != NULL; tmp = tmp->proximo ) {00084 00085 // Debe estar pendiente (ver timer.h) y haber vencido su cuenta00086 if ( (tmp->estado==PENDIENTE) && (--(tmp->ticks) <= 0) ) {00087 00088 // Ejecutamos la funcion establecida00089 (*(tmp->func))(tmp);00090 00091 // Seteamos su estado como finalizado00092 tmp->estado=FINALIZADO;00093 }00094 00095 }00096 00097 }00098 */00099 00100 00101 void actualizar_timers(void)00102 {00103 timer_t *tmp;00104 timer_t *toclean;00105 00106 // Recorremos la lista procesando los timers vencidos hasta encontrar uno más grande00107 for(tmp=HEADER_NEXT(timer_list); tmp ;) {00108 00109 if ( TIMER_TICKS(tmp) > jiffies )00110 break;00111 00112 // Llamamos a la función seteada en el timer00113 (*(TIMER_FUNCTION(tmp)))(tmp);00114
00175 00176 timer_t *tmp;00177 00178 if ( nuevo == NULL ) { return 0; }00179 00180 // Buscamos el primer nodo que posea un tiempo mayor al tiempo del nodo nuevo00181 // de manera que la lista siempre este ordenada de menor a mayor00182 00183 // Si la lista está vacia lo insertamos al ppio00184 //if ( LIST_EMPTY(timer_list) ) {00185 // LIST_ADD(timer_list,timer_list,nuevo);00186 //}00187 00188 // Sino buscamos en el listado de timers00189 //else {00190 00191 LIST_FOREACH(tmp,timer_list,timer_list) {00192 00193 // Dejamos ">=" como criterio así la búsqueda concluye lo antes posible, ya que no importa00194 // si queda al ppio o final de un grupo de nodos con el mismo tiempo.00195 if ( TIMER_TICKS(tmp) >= TIMER_TICKS(nuevo) )00196 break;00197 }00198 00199 // Si no encontramos ningún nodo con tiempo mayor colocamos nuestro nuevo nodo al final de la lista00200 if ( tmp==NULL ) {00201 LIST_ADD_TAIL(timer_list,timer_list,nuevo);00202 }00203 00204 // lo insertamos antes de nuestro nodo encontrado00205 else {00206 LIST_INSERT_BEFORE(timer_list,timer_list,tmp,nuevo);00207 }00208 00209 //}00210 00211 // Habilitamos Interrupciones00212 FINISH_ATOMIC_OPERATION;00213 00214 return 1;00215 }00216 00217 /*00218 int remover_timer(timer_t *timer)00219 {00220 cli();00221 00222 timer_t *tmp;00223 00224 // Es el primer timer ?00225 if ( timer == timer_inicio ) {00226 timer_inicio = timer->proximo;
00227 sti();00228 return 0;00229 }00230 00231 // Buscamos nuestro timer entonces00232 for ( tmp=timer_inicio; (tmp->proximo != timer) && (tmp != NULL) ; tmp = tmp->proximo ) ;00233 00234 // Si no encontramos el timer devolvemos error00235 if ( tmp == NULL ) {00236 // Lo tengo que reemplazar por la constante correcta según la definición de errno.h00237 sti();00238 return -1;00239 }00240 00241 else {00242 tmp->proximo = timer->proximo;00243 }00244 00245 // Lo tengo que reemplazar por la constante correcta según la definición de errno.h00246 sti();00247 return 0;00248 00249 }00250 00251 */00252 00253 inline static int remove_timer(timer_t *timer)00254 {00255 // Deshabilitamos Interrupciones00256 START_ATOMIC_OPERATION;00257 00258 timer_t *tmp;00259 00260 // Este bloque de cod es opcional y permite que se realice un chequeo adicional00261 // a la hora de borrar un timer, verificando que exista en la lista00262 #ifdef SECURE_TIMER_OPER00263 // Buscamos si realmente existe el timer, para evitar problemas de inconsistencias00264 LIST_FOREACH(tmp,timer_list,timer_list) {00265 if ( tmp==timer )00266 break;00267 }00268 00269 // EL timer no existe en la lista !!00270 if ( tmp==NULL ) {00271 FINISH_ATOMIC_OPERATION;00272 return -1; 00273 }00274 00275 #endif00276 00277 // Lo eliminamos00278 LIST_DEL(timer_list,timer_list,timer);00279 00280 // Habilitamos Interrupciones00281 FINISH_ATOMIC_OPERATION;