sábado, 29 de febrero de 2020

Implementar la llamada al sistema 'ps' en xv6 | Sistemas Operativos @US

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

// system calls
int fork(void);
int exit(void) __attribute__((noreturn));
(...)
int uptime(void);
int halt(void); *
int hello(void); *

  • 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();      (*)
}

(*) A diferencia de otros sistemas operativos, en xv6 es necesario hacer una llamada a exit() para liberar los recursos de procesamiento asociados al proceso pertinente. De lo contrario, este no podrá finalizar su ejecución normalmente y la consola nos lo indicará mediante un error.

trap error exit xv6

  • 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

(...)
#define SYS_getprocs número

  • 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

#include "ProcessInfo.h" *
(...)
int 
sys_getprocs(void){
  struct ProcessInfo *processInfoTable;
  if(argptr(0,(char**)&processInfoTable,sizeof(struct ProcessInfo) * NPROC) < 0) {
    return -1;
  }
  return getprocs(processInfoTable);

}

  • sysfunc.h

// System call handlers
int sys_chdir(void);
int sys_close(void);
(...)
int sys_getprocs(void); *
int sys_hello(void);
int sys_halt(void);

  • 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


xv6 Windows 10

2. Ejecución de 'hello', 'ps', 'sh' y 'kill'


prueba estado procesos

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


lanzamiento shutdown

cierre 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