13
ESO/SOL. Tema 4. Llamadas al Sistema Juan Carlos Perez y Sergio Saez

04 Llamadas Al Sistema

Embed Size (px)

DESCRIPTION

tema 4 asignatura eso

Citation preview

Page 1: 04 Llamadas Al Sistema

ESO/SOL. Tema 4. Llamadas al Sistema Juan Carlos Perez y Sergio Saez

Page 2: 04 Llamadas Al Sistema

Table of Contents

Tema 4. Llamadas al Sistema............................................................................................................................14.1 Introducción.......................................................................................................................................14.2 Estructura de un proceso en Linux.....................................................................................................14.3 Interfaz con el sistema operativo.......................................................................................................2

4.3.1 Interfaz ofrecida por el núcleo...........................................................................................24.3.2 Interfaz ofrecida al programador (API).............................................................................3

4.4 Ejemplo de invocación.......................................................................................................................44.5 Biblioteca de llamadas al sistema......................................................................................................5

4.5.1 La biblioteca de llamadas glibc.........................................................................................64.5.2 Código de retorno..............................................................................................................7

4.6 Entrando en el núcleo.........................................................................................................................74.6.1 Dentro de system_call........................................................................................................84.6.2 Recogida de parámetros.....................................................................................................9

4.7 Ejecución de la llamada al sistema....................................................................................................94.8 Saliendo del núcleo: ret_from_syscall.............................................................................................104.9 Resumen...........................................................................................................................................10

ESO/SOL. Tema 4. Llamadas al Sistema

i

Page 3: 04 Llamadas Al Sistema

Tema 4. Llamadas al Sistema

Juan Carlos Pérez y Sergio Sáez Juan Carlos Pérez (Inglés)

Sergio Sá[email protected]@disca.upv.es

4.1 Introducción

Estudio detallado del mecanismo mediante el cual Linux implementa las llamadas al sistema en unaarquitectura i386.

• Un sistema operativo está definido por los servicios que ofrece en relación con el acceso aentrada/salida, procesos, manejo de memoria, envio de señales, tiempo, etc.

• A los servicios del S.O. se accede a través de las llamadas al sistema: open, read, fork,mmap, kill, time, etc.

• La llamada al sistema la invoca un proceso de usuario (o mejor dicho un proceso en modo usuario) yes servida por el núcleo.

• Una llamada al sistema implica pasar o saltar del código del usuario al código del núcleo. Estesalto conlleva un cambio en el modo del funcionamiento del procesador. El procesador debe pasar demodo usuario (acceso restringido a los recursos) a modo supervisor o privilegiado (recordemos queno tiene nada que ver con el superusario root).

4.2 Estructura de un proceso en Linux

• Un proceso en Linux, aunque tiene un modelo de memoria plano, está estructurado en dos zonas dememoria:

♦ La zona del usuario, normalmente los tres primeros gigabytes de las direcciones lógicas.♦ La zona del núcleo, que ocupa el cuarto gigabyte.

• La zona del núcleo de Linux está "mapeada" en todos los procesos del sistema.• Esta estructura coincide con los dos modos del procesador que utiliza Linux:

♦ El código de la zona del núcleo se ejecuta siempre en modo supervisor.♦ El código presente en la zona del usuario se ejecuta en modo usuario.

Tema 4. Llamadas al Sistema 1

Page 4: 04 Llamadas Al Sistema

4.3 Interfaz con el sistema operativo

• Se pueden distinguir dos puntos de vista en la interfaz ofrecida por las llamadas al sistema:

1. La interfaz ofrecida por el núcleo del sistema operativo.

◊ Es una interfaz definida a nivel de lenguaje ensamblador.◊ Depende directamente del "hardware" sobre el cual se está ejecutando el S.O.:

Registros del procesador, cómo se cambia de modo y se salta del código de usuarioal código del núcleo (jump, call, trap, int ...), etc.

2. La interfaz ofrecida al programador o usuario (API).

MSDOS

Documentada a nivel de ensamblador: Interrupción a la que hay que llamar,valores de los registros que hay que cargar y valores de retorno.

UNIX

Funciones estándar en lenguaje C. P.ej. int kill (pid_t pid, int sig);

4.3.1 Interfaz ofrecida por el núcleo

• El núcleo de Linux, en la arquitectura i386, utiliza la instrucción int 0x80 como puerta de entradaen el núcleo. Recuérdese que el manejador para la interrupción software se instala en la funcióntrap_init()[arch/i386/kernel/traps.c#L916]. El número de la interrupción software a utilizar esta definidopor la constante SYSCALL_VECTOR[include/asm−i386/hw_irq.h#L19].

PARÁMETROS DE ENTRADA

Código del servicio requerido en el registro eax. En el ficheroinclude/asm−i386/unistd.h aparecen listadas todas las llamadas al sistemaque ofrece Linux con su correspondiente número. Contiene también laimplementación de algunas llamadas al sistema de uso interno del propio Linux.Si la llamada al sistema espera algún parámetro, Linux los pasa en los siguientesregistros del propio procesador. %ebx arg 1%ecx arg 2%edx arg 3%esi arg 4%edi arg 5

VALORES DE SALIDA

En el registro eax se devuelve el código de retorno.

ESO/SOL. Tema 4. Llamadas al Sistema

4.3 Interfaz con el sistema operativo 2

Page 5: 04 Llamadas Al Sistema

4.3.2 Interfaz ofrecida al programador (API)

• Todas las implementaciones de UNIX disponen de unas bibliotecas de usuario que esconden laimplementación concreta de las llamadas al sistema (la interfaz real ofrecida por el S.O.) y ofrecen alprogramador una interfaz C que presenta las siguientes ventajas:

♦ Facilidad de uso al acceder desde un lenguaje de alto nivel.♦ Portabilidad entre arquitecturas: Linux−sparc, Linux−i386, etc.♦ Portabilidad entre diferentes versiones de UNIX: estándar POSIX. El estándar POSIX se

define sólo a nivel de interfaz, no a nivel de implementación real. De hecho, hasy sistemasno−UNIX, como Windows NT, que ofrecen una interfaz POSIX.

• Todas las llamadas al sistema se encuentran documentadas en la sección 2 del manual en línea deUNIX: # man 2 kill

# man 2 kill KILL(2) Linux Programmer's Manual KILL(2)

NAME kill − send signal to a process

SYNOPSIS #include <sys/types.h> #include <signal.h>

int kill(pid_t pid, int sig);

DESCRIPTION The kill system call can be used to send any signal to any pro− cess group or process.

If pid is positive, then signal sig is sent to pid.

If pid equals 0, then sig is sent to every process in the process group of the current process.

If pid equals −1, then sig is sent to every process except for process 1 (init), but see below.

If pid is less than −1, then sig is sent to every process in the process group −pid. If sig is 0, then no signal is sent, but error checking is still performed.

RETURN VALUE On success, zero is returned. On error, −1 is returned, and errno is set appropriately.

ERRORS EINVAL An invalid signal was specified.

ESRCH The pid or process group does not exist. Note that an existing process might be a zombie, a process which already committed termination, but has not yet been wait()ed for.

EPERM The process does not have permission to send the signal to any of the receiving processes. For a process to have

ESO/SOL. Tema 4. Llamadas al Sistema

4.3.2 Interfaz ofrecida al programador (API) 3

Page 6: 04 Llamadas Al Sistema

permission to send a signal to process pid it must either have root privileges, or the real or effective user ID of the sending process must equal the real or saved set−user− ID of the receiving process. In the case of SIGCONT it suffices when the sending and receiving processes belong to the same session....

• En adelante utilizaremos la llamada al sistema kill(2) como ejemplo.

4.4 Ejemplo de invocación

Ejemplo de invocación de la llamada al sistema int nice(int);

• Detalle de los anillos de prioridad:

En la figura se observa la utilización de los anillos o niveles de prioridad de la arquitectura i386 porparte del núcleo de Linux.

• Detalle del mapa de memoria de un proceso:

ESO/SOL. Tema 4. Llamadas al Sistema

4.4 Ejemplo de invocación 4

Page 7: 04 Llamadas Al Sistema

En la figura se muestra la utilización del espacio de direcciones por parte de un proceso de usuario.El código del usuario puede utilizar las direcciones lógicas desde 0 a 3GB y el núcleo desde 3GB a4GB.

4.5 Biblioteca de llamadas al sistema

• La biblioteca que contiene todas las llamadas al sistema es la libc.• Esta biblioteca se encarga de ocultar los detalles de la interfaz de las llamadas al sistema del núcleo

en forma de funciones en C. Dichas funciones trasladan los parámetros que reciben, normalmente atraves de la pila, en los registros del procesador apropiados, invocan al S.O., recogen el código deretorno (asignándolo típicamente a la variable errno), etc. En algunos casos concretos, las llamadasal sistema que ofrece la biblioteca no se corresponden con las que ofrece el núcleo. Existen casos en los que dos llamadas al sistema de la biblioteca coinciden con la misma llamada alsistema "real" (P.Ej. waitpid y wait4 invocan ambas a la llamada al sistema wait4). Incluso algunas supuestas llamadas al sistema ofrecidas por la biblioteca son implementadascompletamente por ella misma, con lo que el núcleo del S.O. no llega a invocarse. Con estos mecanismos, la biblioteca es capaz de ofrecer una interfaz estándar, p. ej. POSIX, aunquelas llamadas al sistema que ofrece el núcleo del S.O. no coincidan exactamente con dicho estándar. En esta asignatura nos centraremos únicamente en las llamadas al sistema "reales".

• Inicialmente, en Linux, la libc la mantenían Linus Torvalds et al.. Actualmente, se utiliza labiblioteca de GNU (glibc).

• El código de la biblioteca NO pertenece al núcleo del S.O., sino que está en el espacio de direccionesdel usuario. Por lo tanto, el código de las bibliotecas se ejecuta todavía en modo usuario.

ESO/SOL. Tema 4. Llamadas al Sistema

4.5 Biblioteca de llamadas al sistema 5

Page 8: 04 Llamadas Al Sistema

4.5.1 La biblioteca de llamadas glibc

• Existen dos versiones de esta biblioteca con idéntica funcionalidad:

♦ La que se enlaza de forma estática (libc.a), y está incluida en el propio programaejecutable del usuario.

♦ La de enlace dinámico (libc.so) que se incorpora al proceso de usuario sólo cuando esnecesario, y su código es compartido por todos los procesos que la utilizan.

• La biblioteca GNU C es realmente compleja pues los mismos fuentes se pueden compilar sobremultitud de sistemas UNIX y sobre diferentes arquitecturas.

• El código de la llamada al sistema está declarado en una macro que se evalúa en tiempo decompilación, dependiendo su valor del S.O. y la arquitectura para la cual se estén compilando lasfunciones. O sea, el código en ensamblador que realiza la llamada al sistema no existe en un ficherosino que se crea y compila a partir de unos ficheros de especificaciones del S.O. y la arquitectura dedestino.

• El resultado de compilar los fuentes de la libc es son los ficheros libc.a y libc.so, versiónestática y dinámica de la biblioteca respectivamente. Dentro de los ficheros de biblioteca estánempaquetados, como si de un fichero arj o zip se tratara, los bloques de código máquina de todas lasfunciones.

• La orden ar(1) se utiliza para trabajar con bibliotecas (crear, listar, añadir y borrar los ficheros quecontienen las funciones).

• Con la siguiente orden podemos mostrar todos los ficheros contenidos en la biblioteca:

# ar t /usr/lib/libc.a

init−first.olibc−start.oset−init.osysdep.oversion.ocheck_fds.o...

con la opción x en lugar de t se puede extraer un fichero de la biblioteca. Para extraer el ficherokill.o, que contiene la llamada al sistema kill(2) la orden sería:# ar x /usr/lib/libc.a kill.o

• Con la utilidad objdump(1) se puede desensamblar cualquier código objeto o ejecutable. Elsiguiente fragmento de código representa la llamada al sistema kill.

# objdump −d kill.o

kill.o: file format elf32−i386

Disassembly of section .text:

00000000 <__kill>: 0: 89 da mov %ebx,%edx 2: 8b 4c 24 08 mov 0x8(%esp,1),%ecx 6: 8b 5c 24 04 mov 0x4(%esp,1),%ebx

ESO/SOL. Tema 4. Llamadas al Sistema

4.5.1 La biblioteca de llamadas glibc 6

Page 9: 04 Llamadas Al Sistema

a: b8 25 00 00 00 mov $0x25,%eax f: cd 80 int $0x80 11: 89 d3 mov %edx,%ebx 13: 3d 01 f0 ff ff cmp $0xfffff001,%eax 18: 0f 83 fc ff ff ff jae 1a <__kill+0x1a> 1e: c3 ret

• Con la utilidad strace(1) se puede ver cómo los programas hacen uso de las llamadas al sistema.Acepta como parámetro el nombre de un ejecutable y lo ejecuta volcando en la salida estándar deerror todas las llamadas al sistema que éste realiza con sus respectivos parámetros.

# strace ls

execve("/bin/ls", ["ls"], %[/* 34 vars */]) = 0uname({sys="Linux", node="viver", ...}) = 0brk(0) = 0x8053448old_mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, −1, 0) = 0x40017000...open("/lib/i586/libc.so.6", O_RDONLY) = 3read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\264\313"..., 1024) = 1024...open(".", O_RDONLY|O_NONBLOCK|0x10000) = 3...write(1, "11−introduccion\t\t21−arranque\t."..., 81) = 81..._exit(0)

4.5.2 Código de retorno

• En la arquitectrua i386, Linux devuelve el código de retorno de la llamada al sistema en el registroeax.

• Cuando la llamada no ha tenido éxito, el valor devuelto es negativo. Si es negativo, la bibliotecacopia dicho valor sobre una variable global llamada errno y devuelve −1 como valor de retorno dela función Aun así, algunas llamadas realizadas con éxito pueden devolver un valor negativo (p.ej.lseek). Actualmente, el rango de errores que puede devolver una llamada al sistema se encuentraentre −1 y −4095 (0xfffff001). La biblioteca debe ser capaz de determinar cuándo el valordevuelto es un error y tratarlo de forma adecuada. . La rutina syscall_error es la encargada dehacerlo.

• La variable errno contiene el código de error de la última llamada que falló. Una llamada que serealice con éxito no modifica errno. Más información con:# man 3 errno.

• La variable errno está declarada en la propia biblioteca.

4.6 Entrando en el núcleo

• La int 0x80 produce un salto a la zona de código del sistema operativo. Concretamente se salta ala función system_call[arch/i386/kernel/entry.S#L187]. La interrupción 0x80 se asocia con la funciónsystem_call al inicializar Linux en la línea arch/i386/kernel/traps.c#L943 de lafunción trap_init() invocada desde la función start_kernel[init/main.c#L545].

• En el proceso de salto ...

ESO/SOL. Tema 4. Llamadas al Sistema

4.5.2 Código de retorno 7

Page 10: 04 Llamadas Al Sistema

♦ El procesador pasa de modo usuario ("ring level" 3 en la arquitectura i386) a modosupervisor ("ring level" 0).

♦ Se cambia el puntero depila para que apunte a lapila del núcleo delproceso y se guardan endicha pila algunsoregistros (SS, ESP,EFLAGS, CS, EIP). En laarquitectura i386 cadanivel de privilegio tieneun puntero de piladistinto por motivos deseguridad.

Evidentemente la instrucción int necesita muchos ciclos de reloj para completarse.

4.6.1 Dentro de system_call

1. Primero guarda el código de la llamada al sistema en la pila del núcleo(#195[arch/i386/kernel/entry.S#L195]).

2. Lo primero es guardar todos los registros del procesador con la macroSAVE_ALL[arch/i386/kernel/entry.S#L85] (#196[arch/i386/kernel/entry.S#L196]).

3. Se pone el puntero al PCB de la tarea actual en el registro ebx (#197[arch/i386/kernel/entry.S#L197]).4. Se verifica si el proceso padre está monitorizando este proceso (#198[arch/i386/kernel/entry.S#L198]), en

cuyo caso se le informa de que su hijo está realizando una llamada al sistema, antes y después deservirla (#232[arch/i386/kernel/entry.S#L232]).

5. Se comprueba que el número de llamada pedido es válido (o si la llamada no está implementada). Sino es válido se retorna inmediatamente con el código de error ENOSYS(#243[arch/i386/kernel/entry.S#L243]).

6. ...

6. Se llama a la función que sirve la llamada pedida: 202 call *SYMBOL_NAME(sys_call_table)(,%eax,4)

sys_call_table[arch/i386/kernel/entry.S#L397] es un vector de direcciones de salto que contiene lasdirecciones de la funciones que sirven cada una de las llamadas al sistema. Cada dirección ocupa4 bytes.

7. Finalmente se pone en el registro eax el código de retorno, y se ejecuta la rutinaret_from_sys_call[arch/i386/kernel/entry.S#L204].

Realmente se almacena el valor del registro eax en la posición que ocupa dicho registro en la piladel núcleo (EAX[arch/i386/kernel/entry.S#L55](%esp)). Cuando se saquen todos los registros dela pila con la macro RESTORE_ALL[arch/i386/kernel/entry.S#L100], el registro se establecerá al valor deretorno, que es lo que espera la biblioteca libc.

ESO/SOL. Tema 4. Llamadas al Sistema

4.6.1 Dentro de system_call 8

Page 11: 04 Llamadas Al Sistema

4.6.2 Recogida de parámetros

• En C normalmente se utiliza la pila para pasar parámetros entre funciones. Antes de realizar lallamada a una función se insertan en la pila todos los parámetros luego se invoca a la función y éstalee los parámetros de la pila.

Estructura de la pila en el i386 al llamar a una función. • En nuestro caso, la macro SAVE_ALL[arch/i386/kernel/entry.S#L85] guarda los registros (que es donde se

reciben los parámetros de las llamadas) en la pila (push), por lo que si luego llamamos a unafunción C, ésta se comportará como si otra función en C le hubiera pasado los parámetros.

El orden en el que se insertan en la pila es importante. Los últimos en insertarse en la pila sonlos primeros parámetros en la declaración de la función en C.

4.7 Ejecución de la llamada al sistema

• Siguiendo con el ejemplo de la llamada kill, cuando se ejecute la instrucción 202 call *SYMBOL_NAME(sys_call_table)(,%eax,4)se llamará a la función C cuya dirección se encuentra en la entrada37 (include/asm−i386/unistd.h#L44) de la tablasys_call_table[arch/i386/kernel/entry.S#L436]. Recordemos que en el código de la biblioteca libc seasigno el valor 0x25(37) al registro eax antes de ejecutar la instrucción int 0x80:

...a: b8 25 00 00 00 mov $0x25,%eaxf: cd 80 int $0x80

ESO/SOL. Tema 4. Llamadas al Sistema

4.6.2 Recogida de parámetros 9

Page 12: 04 Llamadas Al Sistema

...

y que el tamaño de cada dirección es de 32 bits, o sea 4 bytes.• La función invocada es sys_kill(int pid, int sig)[kernel/signal.c#L978], que se encargará de

enviar la señal sig al proceso con pid pid.• Los parámetros que el usuario le paso a la función kill de la biblioteca libc han pasado por los

registros %ebx y %ecx, luego se han copiado en la pila del núcleo y ese valor de la pila, en ellenguaje C, se ve ahora como los parámetro pid y sig de la función sys_kill.

4.8 Saliendo del núcleo: ret_from_syscall

Al finalizar la ejecución de la llamada al sistema se ejecuta el código que se encuentra enret_from_sys_call[arch/i386/kernel/entry.S#L204]. Este código lleva a cabo algunas comprobaciones antes devolver a modo usuario.

• Durante la ejecución de la llamada al sistema el proceso ha podido cambiar de estado, y por lo tanto,haberse marcado la necesidad de planificar de nuevo, poniendo la variable need_resched a cierto. Aquí se comprueba si esta variable está a cierto y en caso afirmativo llamamos alplanificador[kernel/sched.c#L539] (#L206[arch/i386/kernel/entry.S#L206]).

• Se comprueba si el proceso activo tiene señales pendientes, en cuyo caso se le envían(#L208[arch/i386/kernel/entry.S#L208]).

• Finalmente se restauran todos los registros (incluido el %eax que contiene el código de retorno) y seretorna de la interrupción (int), reponiendo el modo del procesador y la pila a la que tiene acceso elproceso en modo usuario. Se puede observar que la ejecución más frecuente desystem_call[arch/i386/kernel/entry.S#L187] (la llamada es correcta, no está siendo monitorizada, no hayque replanificar y no hay señales pendientes) está optimizada para no tomar absolutamente ningúnsalto, a excepción de la propia llamada a la función.

Los procesadores actuales basan la mayor parte de sus prestaciones en la ejecución segmentada deinstrucciones, y las instrucciones de salto suelen forzar el vaciado de la unidad de ejecución si eldestino de los mimos no se predice correctamente. El código de Linux intenta minimizar el númerode saltos dentro del flujo de control más frecuente.

4.9 Resumen

1. La biblioteca mete en los registros del procesador los parámetros de la llamada.2. Se produce la interrupción software (trap) 0x80. El descriptor de interrupción. 0x80 apunta a la

rutina system_call.3. Se guardan el registro %eax. El resto de los registros con SAVE_ALL.4. Se verifica que el número de llamada al sistema corresponde con una llamada válida.5. Se salta a la función que implementa el servicio pedido:

call *SYMBOL_NAME(sys_call_table)(,%eax,4)

6. Si mientras se atendía la llamada al sistema se ha producido algún evento que requiera llamar alplanificador, se llama en este punto.

7. Si el proceso al que se va a retornar tiene señales pendientes, se le envían ahora.8. Se restauran los valores de los registros con RESTORE_ALL y se vuelve de la interrupción software.

ESO/SOL. Tema 4. Llamadas al Sistema

4.8 Saliendo del núcleo: ret_from_syscall 10

Page 13: 04 Llamadas Al Sistema

9. La biblioteca comprueba si la llamada ha producido algún error, y en este caso guarda en la variableerrno el valor de retorno.

ESO/SOL. Tema 4. Llamadas al Sistema

4.8 Saliendo del núcleo: ret_from_syscall 11