Argument Passing
La primera asignación de los programas de usuario es lograr pasar los argumentos de consola a los procesos al momento de mandarlos a ejecutar.
Last updated
Was this helpful?
La primera asignación de los programas de usuario es lograr pasar los argumentos de consola a los procesos al momento de mandarlos a ejecutar.
Last updated
Was this helpful?
En los programas que maneja PintOS, los argumentos que se le pasan al programa son argc y argv. Estos argumentos se deben de guardar en inicio del stack del proceso, en el caso de PintOS el stack empieza en PHYS_BASE
. El argument passing debe de cumplir el convenio de llamadas a funciones de Intel 80x86:
Caller hace push de los argumentos con un orden inverso (derecha a izquierda).
Caller hace push a la dirección de retorno al tope del stack.
El Callee ejecuta su función.
El Callee guarda su resultado de retorno, si tiene, en EAX (a0).
El Callee hace pop a la dirección de retorno y regresa a esa instrucción.
El Caller saca los argumentos del stack.
Para entender como pasar los argumentos a un proceso, es importante saber como los procesos son cargados por el Sistema Operativo.
El thread padre llama a la función process_execute(char *file_name)
. En este función se crea el nuevo thread con el nombre de file_name
y se configura para que ejecute la función de start_process(char* file_name)
y le envía al thread como parámetro a file_name
El nuevo thread empieza a ejecutar start_process(char* file_name)
donde se empieza a cargar el proceso con la función load(char *file_name)
En la función load(char *file_name)
se abre el archivo y se carga a memoria, si esta acción es exitosa, entonces se empieza a cargar los argumentos al stack con la función setup_stack (void **esp, char* file_name)
En la función de setup_stack (void **esp, char* file_name),
asume que los argumentos estan en el file_name y luego hace un push de cada argumento al stack y los punteros que apuntan a los argumentos.
Si todo el proceso de load se realizó de manera exitosa, se empieza a correr el proceso, de lo contrario, se llama a la función thread_exit()
para destruir el proceso.
Este es el comportamiento deseado, pero no es el comportamiento original que tiene la carga de procesos al iniciar la asignación.
La primera tarea para pasar los argumentos es ir a la función process_execute(char* file_name)
. En esta función se crea el thread que maneja al nuevo proceso con el nombre del proceso, pero el filename no solo incluye el nombre sino también incluye la lista de argumentos que se le pasaron en consola o en el syscall de exec.
Entonces es necesario separar el nombre del archivo de sus argumentos. Para lograr esto se utiliza la función strtok_r(char* str, char* delim, char** save_ptr).
Y al conseguir el nombre con este tokenizer ya se crea el thread con el token del nombre y se le envía una copia de los parámetros al nuevo thread.
fn_copy
contiene una copia del file_name
En este momento, el control de la creación del proceso queda en manos del thread que se acaba de crear.
El nuevo thread empieza la creación del nuevo proceso en la función start_process(char* filename)
. En la creación del nuevo thread file_name
ya no contiene el nombre del archivo sino contiene la lista de parámetros del proceso. Para cargar el proceso se llama a la función load (const char* file_name, void (*eip) (void), void **esp)
En la función de load, se le pasaron la lista de argumentos en el parámetro defile_name.
En está función se abre el archivo y se carga el código a memoria, y por último se crea el stack.
En la configuración del stack se deben de pasar los argumentos al proceso y es el paso final antes de empezar a ejecutar el proceso. Está configuración se realiza en la función setup_stack (void **esp, char *args, char *name)
.
Indicar que el stack pointer (**esp
) apunta a PHYS_BASE
(la dirección base de la memoria).
Escribir los argumentos del proceso con un orden inverso (derecha a izquierda).
Escribir el nombre del proceso.
Alinear la memoria, respecto a 4 bytes, con 0.
Escribir de la dirección de argv
Escribir la dirección de cada argumento del proceso.
Escribir la dirección de argv[0]
Escribir argc
(la cantidad de argumentos más el nombre)
Escribir la dirección de retorno (para estos procesos es 0).
Se voltean los argumentos y se crea un arreglo para guardar las direcciones de cada arreglo. Por cada argumento en la lista de argumentos inversos se debe de bajar el stack pointer. Se baja el stack por el tamaño del arg más 1 porque hay que tomar en cuenta el NULL Terminator de un string.
Se debe de alinear la memoria porque al ingresar los argumentos o el nombre, el stack pudo quedar desalineado a la memoria, en lugar de apuntar al inicio de un word queda apuntando a otra parte de un word.
La operación para alinear la memoria es el módulo 4 del stack pointer para saber a que parte está apuntando. Se utiliza módulo 4 porque la memoria esta compuesta por words de 4 bytes.
La memoria se alinea con 0