Para poder entender cómo se comportan los procesos en un sistema operativo, y con la excusa de hacer un trabajo voluntario de clase, decidí emplear parte de mi tiempo en adentrarme en xv6. Antes de incorporar absolutamente nada, necesité descargar todas las herramientas necesarias para lanzar xv6 en Windows (10) desde la terminal. Necesitarás descargar xv6, hazlo aquí.
Modificaciones sobre xv6
Primer contacto: hello y shutdown
En el directorio raíz en el que se encuentra xv6 se aprecia una ristra de archivos que confundirán al ojo inexperto, donde me incluyo. Existen opciones más jerárquicas en la red, es cuestión de buscar bien.
Inicialmente opté por crear un programa llamado hello el cual sería muy básico y se encargaría de imprimir por pantalla un mensaje cualquiera (haciendo uso de la llamada al sistema hello). Fueron necesarias ciertas modificaciones en los archivos syscall.h/.c, user.h, usys.S y sysproc.c así como incorporar un hello.c.
Para incorporar el proceso shutdown necesitamos a su vez crear la llamada al sistema halt. En definitiva, se requieren modificaciones en Makefile así como en los ficheros anteriores. Añadiremos también halt.c y shutdown.c.
(¡OJO! El asterisco (*) y (...) no deben interpretarse como parte del código).
- syscall.h
// System call numbers
#define SYS_fork 1
#define SYS_exit 2
(...)
#define SYS_close 21
#define SYS_halt 22 *
#define SYS_hello 23 *
- syscall.c
extern int sys_chdir(void);
extern int sys_close(void);
extern int sys_dup(void);
(...)
extern int sys_uptime(void);
extern int sys_halt(void); *
extern int sys_hello(void); *
static int (*syscalls[])(void) = {
[SYS_fork] sys_fork,
[SYS_exit] sys_exit,
(...)
[SYS_close] sys_close,
[SYS_halt] sys_halt, *
[SYS_hello] sys_hello *
};
- usys.s
SYSCALL(fork)
SYSCALL(exit)
SYSCALL(wait)
(...)
SYSCALL(uptime)
SYSCALL(halt) *
SYSCALL(hello) *
- user.h
- sysproc.c
(...)
int
sys_hello(void)
{
cprintf("###Proceso de Alejandro Pariente###\n");
return 0;
}
int
sys_halt(void)
{
outb(0xf4, 0x00);
return 0;
}
- Makefile
(...)
UPROGS=\
_cat\
_echo\
(...)
_shutdown\ *
_hello\ *
(...)
QEMUOPTS = -drive file=fs.img,index=1,media=disk,format(...)
-device isa-debug-exit,iobase=0xf4,iosize=0x04 *
(...)
EXTRA=\
(...)
shutdown.c\ *
hello.c\ *
printf.c umalloc.c\
(...)
- hello.c
#include "types.h"
#include "user.h"
int main(void)
{
hello();
<---- podemos incorporar una llamada a sleep(n) para dejar al proceso durmiendo un tiempo antes de su salida exit()
exit(); (*)
}
- halt.c
int
sys_halt(void)
{
outb(0xf4, 0x00);
return 0;
}
- shutdown.c
#include "types.h"
#include "user.h"
int main(void){
halt();
exit();
}
Con estos cambios y añadidos ya es posible lanzar los procesos hello y shutdown. El primero simplemente muestra un mensaje en la terminal, termina su ejecución y desaparece. El segundo provoca el cierre del sistema.
Todo lo que necesité para implementar el shutdown: "Adding New System Calls to xv6".
Viendo el estado de los procesos | Process Status (ps)
Para poder implementar esta llamada al sistema necesité hacerme con el código de andear@github, que reorganiza el código por módulos que lo hacen más comprensible. Los cambios con respecto al xv6 'más virgen' se encuentran principalmente en el Makefile.
Según he podido deducir, Makefile es el primer fichero del que nuestra terminal hace uso al ordenarle la instrucción 'make'. Cumpliría con la función de hacer un pequeño 'tour' por toda la carpeta que contiene nuestro xv6.
Sirve como guía para generar otros muchos ficheros que son necesarios para la ejecución de todo el contexto de la versión de xv6 que tengamos... ergo es en ahí donde se expone toda esta nueva jerarquía por los directorios ----> include; tools; user; kernel. Los tres últimos cuentan con su propio makefile.mk.
Se necesitaron cambios en syscall.h/.c, user.h, usys.S, sysproc.c, sysfunc.h, makefile.mk, proc.c y defs.h. Además, es necesario incorporar los archivos ProcessInfo.h y ps.c.
Es importante saber que, aunque sólo se hagan cambios en esos ficheros para añadir 'ps', el resto de ficheros no son idénticos al 'xv6 virgen'; presentan discrepancias.
- syscall.h
- syscall.c
(...)
[SYS_getprocs] sys_getprocs
- user.h
// system calls
int fork(void);
int exit(void) __attribute__((noreturn));
(...)
int getprocs(struct ProcessInfo* processInfoTable); *
int halt(void);
int hello(void);
- usys.s
SYSCALL(fork)
SYSCALL(exit)
(...)
SYSCALL(getprocs) *
SYSCALL(halt)
SYSCALL(hello)
- sysproc.c
- sysfunc.h
- makefile.mk
USER_PROGS := \
cat\
echo\
(...)
ps\ *
shutdown\
hello\
- proc.c
#include "ProcessInfo.h" *
(...)
int
getprocs(struct ProcessInfo* processInfoTable){
struct proc *p;
int count = 0;
int i;
for (i = 0, p = ptable.proc; p < &ptable.proc[NPROC] && i < NPROC; i++,p++)
{
if(p->state == UNUSED)
{
continue;
}
count++;
processInfoTable[i].pid = p->pid;
if(i == 0){
processInfoTable[i].ppid = 0;
}
else{
processInfoTable[i].ppid = p->parent->pid;
}
processInfoTable[i].state = p->state;
processInfoTable[i].sz = p->sz;
for (int j = 0; j < 16; j++)
{
processInfoTable[i].name[j] = p->name[j];
}
}
p = NULL;
return count;
}
- defs.h
// proc.c
(...)
void yield(void);
int getprocs(struct ProcessInfo*); *
- ProcessInfo.h
#ifndef _PROCESSINFO_H_
#define _PROCESSINFO_H_
#include "types.h"
struct ProcessInfo{
int pid;
int ppid;
int state;
uint sz;
char name[16];
};
#endif
- ps.c
#include "ProcessInfo.h"
#include "types.h"
#include "stat.h"
#include "user.h"
#include "param.h"
int main(void)
{
enum procstate { DESHUSO, EMBRION, BLOQUEADO, PREPARADO, ACTIVO, ZOMBIE };
static char *states[] = {
[DESHUSO] "DESHUSO ",
[EMBRION] "EMBRION ",
[BLOQUEADO] "BLOQUEADO",
[PREPARADO] "PREPARADO",
[ACTIVO] "ACTIVO ",
[ZOMBIE] "ZOMBIE "
};
printf(1, "\n PID\tESTADO\t\tMEM\tPROC\n");
^
|
---- id, estado, tamaño de memoria asignada y nombre del proceso i-ésimo
printf(1,"\n");
struct ProcessInfo processInfoTable[NPROC];
int numbers = getprocs(processInfoTable);
int lineNumber;
for (int i = 0; i < numbers; i++)
{
lineNumber = i + 1;
printf(1, "%d %d\t%s\t%d\t%s",
lineNumber,
processInfoTable[i].pid,
states[processInfoTable[i].state],
processInfoTable[i].sz,
processInfoTable[i].name);
printf(1, "\n");
}
exit();
}
Breve demostración de lo implementado
1. Lanzamiento de xv6 en Windows 10 desde terminal
2. Ejecución de 'hello', 'ps', 'sh' y 'kill'
Como mencioné en un principio, el proceso hello únicamente muestra un mensaje por pantalla. Posteriormente lanzo 'ps' para ver qué procesos existen en ese instante en la máquina. Para comprobar que funciona correctamente, lanzo varias veces el proceso 'sh', lo cual se corrobora en el siguiente 'ps'. Por último, acabo (kill) con 2 de ellos y vuelvo a confirmarlo mediante el comando 'ps'.
3. Shutdown y cierre del sistema xv6
Con esto quedan demostradas todas las funcionalidades anteriormente descritas. Para hacerte con los archivos relevantes a esta entrada visita el apartado 'Descargas' de este blog.
No hay comentarios:
Publicar un comentario