Llamadas al sistema.docx

32
 1.3 I nter f ace s  con el sistema operativo [SILB94] 1.3.1 Las llamadas al sistema Ya se ha comentado que el sistema operativo es u na interfaz que oculta las peculiaridades del hardware. Para ello ofrece una serie de servicios que constituyen una máquina virtual más fácil de usar que el hardware   básico. Estos servicios se solicitan mediante llamadas al sistema. La forma en que se realiza una llamada al sistema consiste en colocar una serie de parámetros en un lugar específico (como los registros del  procesador), para después ejecutar una instrucción del lenguaje máquina del procesador denominada trap (en castellano, trampa). La ejecución de esta instrucción máquina hace que el hardware guarde el contador de  programa y la palabra de estado del procesador (PSW, Processor Status Word) en un lugar seguro de la memoria, cargándose un nuevo contador de programa y una nueva PSW. Este nuevo contador de programa contiene una dirección de memoria donde reside una parte (un programa) del sistema operativo, el cual se encarga de llevar a cabo el servicio solicitado. Cuando el sistema operativo finaliza el servicio, coloca un código de estado en un registro para indicar si hubo éxito o fracaso, y ejecuta una instrucción retur n fr om trap , esta instrucción provoca que el hardware  restituya el contador de programa y la PSW del programa que realizó la llamada al sistema, prosiguiéndose así su ejecución.  Normalmente los lenguajes de alto nivel  tienen una (o varias) rutinas de  biblioteca por cada llamada al sistema. Dentro de estos procedimientos se aísla el código (normalmente en ensamblador) correspondiente a la carga de registros con parámetros, a la instrucción trap, y a obtener el código de estado a partir de un registro. La finalidad de estos

Transcript of Llamadas al sistema.docx

1.3 Interfaces con el sistema operativo [SILB94]

1.3.1 Las llamadas al sistemaYa se ha comentado que el sistema operativo es una interfaz que oculta las peculiaridades del hardware. Para ello ofrece una serie de servicios que constituyen una mquina virtual ms fcil de usar que el hardware bsico. Estos servicios se solicitan mediante llamadas al sistema.La forma en que se realiza una llamada al sistema consiste en colocar una serie de parmetros en un lugar especfico (como los registros del procesador), para despus ejecutar una instruccin del lenguaje mquina del procesador denominada trap (en castellano, trampa). La ejecucin de esta instruccin mquina hace que el hardware guarde el contador de programa y la palabra de estado del procesador (PSW, Processor Status Word) en un lugar seguro de la memoria, cargndose un nuevo contador de programa y una nueva PSW. Este nuevo contador de programa contiene una direccin de memoria donde reside una parte (un programa) del sistema operativo, el cual se encarga de llevar a cabo el servicio solicitado. Cuando el sistema operativo finaliza el servicio, coloca un cdigo de estado en un registro para indicar si hubo xito o fracaso, y ejecuta una instruccin return from trap, esta instruccin provoca que el hardware restituya el contador de programa y la PSW del programa que realiz la llamada al sistema, prosiguindose as su ejecucin.Normalmente los lenguajes de alto nivel tienen una (o varias) rutinas de biblioteca por cada llamada al sistema. Dentro de estos procedimientos se asla el cdigo (normalmente en ensamblador) correspondiente a la carga de registros con parmetros, a la instruccin trap, y a obtener el cdigo de estado a partir de un registro. La finalidad de estos procedimientos de biblioteca es ocultar los detalles de la llamada al sistema, ofreciendo una interfaz de llamada al procedimiento. Como una llamada al sistema depende del hardware (por ejemplo, del tipo de registros del procesador), la utilizacin de rutinas de biblioteca hace el cdigo portable.El nmero y tipo de llamadas al sistema vara de un sistema operativo a otro. Existen, por lo general, llamadas al sistema para ejecutar ficheros que contienen programas, pedir ms memoria dinmica para un programa, realizar labores de E/S (como la lectura de un carcter de un terminal), crear un directorio, etc. Ejemplos de rutinas de biblioteca que realizan llamadas al sistema en un entorno del sistema operativo C-UNIX son: read, write, malloc, exec, etc. .1.3.2 El intrprete de rdenesCuando un usuario se conecta a un ordenador se inicia un intrprete de rdenes (en entornos UNIX llamados shells). El intrprete de rdenes es un programa que muestra un indicador (prompt) formado por algunos caracteres, que pueden incluir el directorio de trabajo (un posible indicador en MS DOS es C:\>), que indica al usuario que es posible introducir una orden. El usuario escribir una orden , por ejemplo C:\> copy fich fich2 y pulsar la tecla return. En un entorno UNIX, o MS DOS, la primera palabra es el nombre de un fichero que contiene un programa, siendo el resto de la lnea una serie de argumentos, separados por espacios, que toma dicho programa. Una excepcin a esto son las rdenes internas, que el intrprete implementa como rutinas suyas, y que no tienen, por tanto, un programa asociado guardado en disco. El intrprete de rdenes realiza entonces una o varias llamadas al sistema para ejecutar dicho programa. Cuando el programa finalice (al realizar una llamada al sistema exit) el control vuelve al programa que lo lanz (el intrprete de rdenes), mostrando ste otra vez el indicador, y repitindose el ciclo.As pues, el intrprete de rdenes es un programa que sirve de interfaz entre el sistema operativo y un usuario, utilizndolo este ltimo para ejecutar programas. A diferencia de un programador, un "usuario final" realiza todas las llamadas al sistema indirectamente, a travs de las llamadas al sistema de los programas que ejecuta. En muchos sistemas se ha optado por sustituir el intrprete de rdenes por un programa que utiliza ventanas. En estos programas aparecen iconos que el usuario puede seleccionar mediante un ratn. Cuando el usuario selecciona un icono que representa un programa se realizan llamadas al sistema para ejecutar un fichero, asociado al icono, que contiene el programa. Por lo tanto, se sustituye la interfaz del usuario para ejecutar programas, pero la interfaz con el sistema operativo no cambia.Es el momento de hacer una aclaracin. Existen una serie de programas muy importantes (como traductores, editores de texto, ensambladores, enlazadores e intrpretes de rdenes) que ayudan al programador a realizar sus programas, y que vienen en el lote con cualquier sistema operativo. Estos programas, que forman parte de los programas de sistema o software de sistemas, utilizan llamadas al sistema, pero no son parte del sistema operativo. El sistema operativo es el cdigo que acepta llamadas al sistema, y realiza un procesamiento para satisfacer dichas llamadas. El sistema operativo es el programa de sistema ms importante.

Tema 5. Llamadas al SistemaJuan Carlos Prez y Sergio Sez Sergio SezJuan Carlos [email protected]@disca.upv.es

5.1 IntroduccinVamos a llevar a cabo el estudio detallado del mecanismo mediante el cual Linux implementa las llamadas al sistema en una arquitectura x86. Un sistema operativo est definido por los servicios que ofrece en relacin con el acceso a entrada/salida, procesos, manejo de memoria, envo de seales, tiempo, etc. A los servicios del S.O. se accede a travs 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) y es servida por el ncleo (tpicamente el mismo proceso en modo ncleo). Una llamada al sistema implica pasar o saltar del cdigo del usuario al cdigo del ncleo. Este salto conlleva un cambio en el modo del funcionamiento del procesador. El procesador debe pasar de modo usuario (acceso restringido a los recursos) a modo supervisor o privilegiado (recordemos que no tiene nada que ver con el superusario root).

5.2 Estructura de un proceso en Linux Un proceso en Linux, aunque tiene un modelo de memoria plano, est estructurado en dos zonas de memoria: La zona del usuario, normalmente los tres primeros gigabytes de las direcciones lgicas. La zona del ncleo, que ocupa el cuarto gigabyte. La zona del ncleo de Linux est "mapeada" en todos los procesos del sistema. Esta estructura coincide con los dos modos del procesador que utiliza Linux: El cdigo de la zona del ncleo se ejecuta siempre en modo supervisor. El cdigo presente en la zona del usuario se ejecuta en modo usuario.

5.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 ncleo 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, cmo se cambia de modo y se salta del cdigo de usuario al cdigo del ncleo (jump, call, trap, int, sysenter ...), etc. 2. La interfaz ofrecida al programador o usuario (API). MSDOS Documentada a nivel de ensamblador: Interrupcin a la que hay que llamar, valores de los registros que hay que cargar y valores de retorno. UNIX Funciones estndar en lenguaje C. P.ej. int setpriority (int which, int who, int prio);

5.3.1 Interfaz ofrecida al programador (API) Todas las implementaciones de UNIX disponen de unas bibliotecas de usuario que esconden la implementacin concreta de las llamadas al sistema (la interfaz real ofrecida por el S.O.) y ofrecen al programador 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: estndar POSIX. El estndar POSIX se define slo a nivel de interfaz, no a nivel de implementacin real. De hecho, hay sistemas no-UNIX, como Windows NT, que ofrecen una interfaz POSIX. Todas las llamadas al sistema se encuentran documentadas en la seccin 2 del manual en lnea de UNIX: # man 2 setpriority GETPRIORITY(2) Linux Programmer's Manual GETPRIORITY(2) NAME getpriority, setpriority - get/set program scheduling priority SYNOPSIS #include #include int getpriority(int which, int who); int setpriority(int which, int who, int prio); DESCRIPTION The scheduling priority of the process, process group, or user, as indicated by which and who is obtained with the getpriority() call and set with the setpriority() call. The value which is one of PRIO_PROCESS, PRIO_PGRP, or PRIO_USER, and who is interpreted relative to which (a process identifier for PRIO_PROCESS, process group identifier for PRIO_PGRP, and a user ID for PRIO_USER). A zero value for who denotes (respectively) the calling process, the process group of the calling process, or the real user ID of the calling process. Prio is a value in the range -20 to 19 (but see the Notes below). The default priority is 0; lower priorities cause more favor- able scheduling. The getpriority() call returns the highest priority (lowest numerical value) enjoyed by any of the specified processes. The setpriority() call sets the priorities of all of the specified processes to the specified value. Only the superuser may lower priorities. RETURN VALUE Since getpriority() can legitimately return the value -1, it is necessary to clear the external variable errno prior to the call, then check it afterwards to determine if a -1 is an error or a legitimate value. The setpriority() call returns 0 if there is no error, or -1 if there is. ERRORS EINVAL which was not one of PRIO_PROCESS, PRIO_PGRP, or PRIO_USER. ESRCH No process was located using the which and who values specified. In addition to the errors indicated above, setpriority() may fail if: EPERM A process was located, but its effective user ID did not match either the effective or the real user ID of the caller, and was not privileged (on Linux: did not have the CAP_SYS_NICE capability). But see NOTES below. EACCES The caller attempted to lower a process priority, but did not have the required privilege (on Linux: did not have the CAP_SYS_NICE capability). Since Linux 2.6.12, this error only occurs if the caller attempts to set a process priority outside the range of the RLIMIT_NICE soft resource limit of the target process; see getrlimit(2) for details. CONFORMING TO SVr4, 4.4BSD (these function calls first appeared in 4.2BSD), POSIX.1-2001. NOTES A child created by fork(2) inherits its parent's nice value. The nice value is preserved across execve(2). ... SEE ALSO nice(1), fork(2), capabilities(7), renice(8) Linux 2002-09-20 GETPRIORITY(2)

5.3.2 Interfaz ofrecida por el ncleo La versin 2.6 del ncleo de Linux para la arquitectura x86 utiliza dos posibles puertas de entrada en el ncleo: int 0x80 Sistema original basado en una interrupcin software. sysenter Llamada al sistema rpida disponible desde el Pentium II. Para utilizar un mecanismo concreto, la librera libc y el ncleo tienen que estar de acuerdo en su disponibilidad. Para resolver el problema, el ncleo de Linux genera una pequea librera que se enlaza con los ejecutables al hacer execve (est alojada en una pgina de memoria fija), y que le ofrece a la librera libc la funcin __kernel_vsyscall. Dicha funcin utiliza el mejor mecanismo disponible. arch/i386/kernel/vsyscall-int80.S [10-17] 10 .text 11 .globl __kernel_vsyscall 12 .type __kernel_vsyscall,@function 13 __kernel_vsyscall: 14 .LSTART_vsyscall: 15 int $0x80 16 ret 17 .LEND_vsyscall:arch/i386/kernel/vsyscall-sysenter.S [10-22,30-39] 10 .text 11 .globl __kernel_vsyscall 12 .type __kernel_vsyscall,@function 13 __kernel_vsyscall: 14 .LSTART_vsyscall: 15 push %ecx 16 .Lpush_ecx: 17 push %edx 18 .Lpush_edx: 19 push %ebp 20 .Lenter_kernel: 21 movl %esp,%ebp 22 sysenter... 30 .globl SYSENTER_RETURN /* Symbol used by entry.S. */ 31 SYSENTER_RETURN: 32 pop %ebp 33 .Lpop_ebp: 34 pop %edx 35 .Lpop_edx: 36 pop %ecx 37 .Lpop_ecx: 38 ret 39 .LEND_vsyscall: Dependiendo del mecanismo de entrada utilizado, la salida del ncleo se realiza mediante iret o sysexit, respectivamente.

Estudiaremos la versin basada en la interrupcin software int 0x80, disponible en cualquier procesador de la arquitectura x86. Recurdese que el manejador para la interrupcin software se instala en la funcin trap_init() [arch/i386/kernel/traps.c#L995]. El nmero de la interrupcin software a utilizar esta definido por la constante SYSCALL_VECTOR [include/asm-i386/mach-default/irq_vectors.h#L25]. arch/i386/kernel/traps.c [995-996,1009-1017,1032,1037-1040] 995 void __init trap_init(void) 996 {...1009 set_trap_gate(0,&divide_error);1010 set_intr_gate(1,&debug);1011 set_intr_gate(2,&nmi);1012 set_system_intr_gate(3, &int3); /* int3-5 can be called from all */1013 set_system_gate(4,&overflow);1014 set_system_gate(5,&bounds);1015 set_trap_gate(6,&invalid_op);1016 set_trap_gate(7,&device_not_available);1017 set_task_gate(8,GDT_ENTRY_DOUBLEFAULT_TSS);...1032 set_system_gate(SYSCALL_VECTOR,&system_call);...1037 cpu_init();1038 1039 trap_init_hook();1040 }Parmetros de entrada Cdigo del servicio requerido en el registro %eax. En el fichero include/asm-i386/unistd.h aparecen listadas todas las llamadas al sistema que ofrece Linux con su correspondiente nmero. Si la llamada al sistema espera algn parmetro, debern estar en los siguientes registros del procesador. %ebx arg 1%ecx arg 2%edx arg 3%esi arg 4%edi arg 5Valor de salida En el registro %eax se devuelve el cdigo de retorno de la llamada al sistema.

5.4 Ejemplo de invocacinEjemplo de invocacin de la llamada al sistema int nice(int); Detalle de los anillos de prioridad: En la figura se observa la utilizacin de los anillos o niveles de prioridad de la arquitectura x86 por parte del ncleo de Linux.

Detalle del mapa de memoria de un proceso:

En la figura se muestra la utilizacin del espacio de direcciones por parte de un proceso de usuario. El cdigo del usuario puede utilizar las direcciones lgicas desde 0 a 3GB y el cdigo del ncleo est situado desde el 3GB al 4GB. Flujo simplificado de una llamada al sistema utilizando la entrada mediante int.

5.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 ncleo en forma de funciones en C. Dichas funciones trasladan los parmetros que reciben, normalmente a traves de la pila, en los registros del procesador apropiados, invocan al S.O., recogen el cdigo de retorno (asignndolo tpicamente a la variable errno), etc. En algunos casos concretos, las llamadas al sistema que ofrece la biblioteca no se corresponden con las que ofrece el ncleo. Existen casos en los que dos llamadas al sistema de la biblioteca coinciden con la misma llamada al sistema "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 implementadas completamente por ella misma, con lo que el ncleo del S.O. no llega a invocarse. Con estos mecanismos, la biblioteca es capaz de ofrecer una interfaz estndar, p. ej. POSIX, aunque las llamadas al sistema que ofrece el ncleo del S.O. no coincidan exactamente con dicho estndar. En esta asignatura nos centraremos nicamente en las llamadas al sistema "reales". Inicialmente, en Linux, la libc la mantenan Linus Torvalds et al.. Actualmente, se utiliza la biblioteca de GNU (glibc). El cdigo de la biblioteca NO pertenece al ncleo del S.O., sino que est en el espacio de direcciones del usuario. Por lo tanto, el cdigo de las bibliotecas se ejecuta todava en modo usuario.

5.5.1 La biblioteca de llamadas glibc Existen dos versiones de esta biblioteca con idntica funcionalidad: La que se enlaza de forma esttica (libc.a), y est incluida en el propio programa ejecutable del usuario. La de enlace dinmico (libc.so) que se incorpora al proceso de usuario slo cuando es necesario, y su cdigo es compartido por todos los procesos que la utilizan. La biblioteca GNU C es realmente compleja pues los mismos fuentes se pueden compilar sobre multitud de sistemas UNIX y sobre diferentes arquitecturas. El cdigo de la mayora de las llamadas al sistema se genera en tiempo de compilacin, dependiendo su valor del S.O. y la arquitectura para la cual se estn compilando las funciones. O sea, el cdigo en ensamblador que realiza la llamada al sistema no existe en un fichero sino que se crea y compila a partir de unos ficheros de especificaciones del S.O. y la arquitectura de destino.

El resultado de compilar los fuentes de la libc son los ficheros libc.a y libc.so, versin esttica y dinmica de la biblioteca respectivamente. Dentro de los ficheros de biblioteca estn empaquetados, como si de un fichero arj o zip se tratara, los bloques de cdigo mquina de todas las funciones. La orden ar(1) se utiliza para trabajar con bibliotecas (crear, listar, aadir y borrar los ficheros que contienen 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 opcin x en lugar de t se puede extraer un fichero de la biblioteca. Para extraer el fichero setpriority.o, que contiene la llamada al sistema setpriority(2) la orden sera:

# ar x /usr/lib/libc.a setpriority.o

Con la utilidad objdump(1) se puede desensamblar cualquier cdigo objeto o ejecutable. El siguiente fragmento de cdigo representa la llamada al sistema setpriority. # objdump -d setpriority.o setpriority.o: file format elf32-i386

Disassembly of section .text:

00000000 : 0: 53 push %ebx 1: 8b 54 24 10 mov 0x10(%esp),%edx 5: 8b 4c 24 0c mov 0xc(%esp),%ecx 9: 8b 5c 24 08 mov 0x8(%esp),%ebx d: b8 61 00 00 00 mov $0x61,%eax 12: cd 80 int $0x80 14: 5b pop %ebx 15: 3d 01 f0 ff ff cmp $0xfffff001,%eax 1a: 0f 83 fc ff ff ff jae 1c 20: c3 ret Se puede observar como la funcin de biblioteca coge los tres parmetros de la pila y los coloca en los registros %ebx, %ecx y %edx respectivamente. Igualmente, la funcin coloca en el registro %eax el cdigo [include/asm-i386/unistd.h#L105] de la llamada al sistema (setpriority 0x61 = 97) Con la utilidad strace(1) se puede ver cmo los programas hacen uso de las llamadas al sistema. Acepta como parmetro el nombre de un ejecutable y lo ejecuta volcando en la salida estndar de error todas las llamadas al sistema que ste realiza con sus respectivos parmetros. # 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)

5.5.2 Cdigo de retorno En la arquitectura x86, Linux devuelve el cdigo de retorno de la llamada al sistema en el registro %eax. Cuando la llamada no ha tenido xito, el valor devuelto es negativo. Si es negativo, la biblioteca copia dicho valor sobre una variable global llamada errno y devuelve -1 como valor de retorno de la funcin Aun as, algunas llamadas realizadas con xito pueden devolver un valor negativo. Actualmente, el rango de errores que puede devolver una llamada al sistema se encuentra entre -1 y -4095 (0xfffff001). La biblioteca debe ser capaz de determinar cundo el valor devuelto es un error y tratarlo de forma adecuada. . La rutina syscall_error es la encargada de hacerlo. La variable errno contiene el cdigo de error de la ltima llamada que fall. Una llamada que se realice con xito no modifica errno. Ms informacin con:# man 3 errno. La variable errno est declarada en la propia biblioteca.

5.6 Entrando en el ncleo La int 0x80 produce un salto a la zona de cdigo del sistema operativo. Concretamente se salta a la funcin system_call [arch/i386/kernel/entry.S#L240]. La interrupcin 0x80 se asocia con la funcin system_call al inicializar Linux en la lnea arch/i386/kernel/traps.c#L1032 de la funcin trap_init() invocada desde la funcin start_kernel. En el proceso de salto ... El procesador pasa de modo usuario ("priviledge level" 3 en la arquitectura x86) a modo supervisor ("priviledge level" 0). Se cambia el puntero de pila para que apunte a la pila del ncleo del proceso y se guardan en dicha pila algunos registros (SS, ESP, EFLAGS, CS, EIP). En la arquitectura x86 cada nivel de privilegio tiene un puntero de pila distinto por motivos de seguridad.

Evidentemente la instruccin int necesita muchos ciclos de reloj para completarse, de ah que se incorporar la instruccin sysenter para poder implementar una entrada rpida en el sistema.

5.6.1 Dentro de system_callLa implementacin se encuentra en el fichero entry.S arch/i386/kernel/entry.S [240-261] 240 # system call handler stub 241 ENTRY(system_call) 242 pushl %eax # save orig_eax 243 SAVE_ALL 244 GET_THREAD_INFO(%ebp) 245 # system call tracing in operation 246 testb $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),TI_flags(%ebp) 247 jnz syscall_trace_entry 248 cmpl $(nr_syscalls), %eax 249 jae syscall_badsys 250 syscall_call: 251 call *sys_call_table(,%eax,4) 252 movl %eax,EAX(%esp) # store the return value 253 syscall_exit: 254 cli # make sure we don't miss an interrupt 255 # setting need_resched or sigpending 256 # between sampling and the iret 257 movl TI_flags(%ebp), %ecx 258 testw $_TIF_ALLWORK_MASK, %cx # current->work 259 jne syscall_exit_work 260 restore_all: 261 RESTORE_ALL#242 [system_call] Primero guarda el cdigo de la llamada al sistema en la pila del ncleo.

#243 A continuacin es importante guardar todos los registros del procesador con la macro SAVE_ALL [arch/i386/kernel/entry.S#L84]. arch/i386/kernel/entry.S [84-98] 84 #define SAVE_ALL \ 85 cld; \ 86 pushl %es; \ 87 pushl %ds; \ 88 pushl %eax; \ 89 pushl %ebp; \ 90 pushl %edi; \ 91 pushl %esi; \ 92 pushl %edx; \ 93 pushl %ecx; \ 94 pushl %ebx; \ 95 movl $(__USER_DS), %edx; \ 96 movl %edx, %ds; \ 97 movl %edx, %es; 98

#244 Se pone el puntero a la estructura thread_info de la tarea actual en el registro %ebp usando la macro GET_THREAD_INFO [include/asm-i386/thread_info.h#L119].

#246#247 Se verifica si el proceso padre est monitorizando este proceso, en cuyo caso se ejecuta el cdigo syscall_trace_entry [arch/i386/kernel/entry.S#L302]. Para verificar algunas condiciones sobre el estado del proceso actual se accede al campo flags de su estructura thread_info a travs del registro %ebp y el offset TI_flags. Para acceder a los campos de la estructura struct thread_info [include/asm-i386/thread_info.h#L28] se utilizan desplazamientos fijos que se calculan en tiempo de compilacin mediante el fichero arch/i386/kernel/asm-offsets.c. arch/i386/kernel/asm-offsets.c [24-25,47-54,66] 24 void foo(void) 25 {... 47 OFFSET(TI_task, thread_info, task); 48 OFFSET(TI_exec_domain, thread_info, exec_domain); 49 OFFSET(TI_flags, thread_info, flags); 50 OFFSET(TI_status, thread_info, status); 51 OFFSET(TI_cpu, thread_info, cpu); 52 OFFSET(TI_preempt_count, thread_info, preempt_count); 53 OFFSET(TI_addr_limit, thread_info, addr_limit); 54 OFFSET(TI_restart_block, thread_info, restart_block);... 66 }include/asm-i386/thread_info.h [28-34,47] 28 struct thread_info { 29 struct task_struct *task; /* main task structure */ 30 struct exec_domain *exec_domain; /* execution domain */ 31 unsigned long flags; /* low level flags */ 32 unsigned long status; /* thread-synchronous flags */ 33 __u32 cpu; /* current CPU */ 34 __s32 preempt_count; /* 0 => preemptable, BUG */... 47 };El cdigo de syscall_trace_entry [arch/i386/kernel/entry.S#L247] informa al proceso padre de que su hijo est realizando una llamada al sistema, antes y despus de servirla, mediante la invocacin de la funcin do_syscall_trace. arch/i386/kernel/entry.S [300-311] 300 # perform syscall exit tracing 301 ALIGN 302 syscall_trace_entry: 303 movl $-ENOSYS,EAX(%esp) 304 movl %esp, %eax 305 xorl %edx,%edx 306 call do_syscall_trace 307 movl ORIG_EAX(%esp), %eax 308 cmpl $(nr_syscalls), %eax 309 jnae syscall_call 310 jmp syscall_exit 311

#248#249 Se comprueba que el nmero de llamada pedido es vlido (si est dentro del rango). Si no es vlido, se ejecuta el cdigo syscall_badsys [arch/i386/kernel/entry.S#L333] que retorna inmediatamente con el cdigo de error ENOSYS saltando a resume_userspace [arch/i386/kernel/entry.S#L167]. arch/i386/kernel/entry.S [167-175,333-335] 167 ENTRY(resume_userspace) 168 cli # make sure we don't miss an interrupt 169 # setting need_resched or sigpending 170 # between sampling and the iret 171 movl TI_flags(%ebp), %ecx 172 andl $_TIF_WORK_MASK, %ecx # is there any work to be done on 173 # int/exception return? 174 jne work_pending 175 jmp restore_all... 333 syscall_badsys: 334 movl $-ENOSYS,EAX(%esp) 335 jmp resume_userspace

#250#252 [syscall_call] Se llama a la funcin que sirve la llamada pedida y se guarda el cdigo de retorno en la posicin de la pila donde est situado el registro %eax. Se almacena el valor del registro %eax en la posicin que ocupa dicho registro en la pila del ncleo (EAX(%esp) [arch/i386/kernel/entry.S#L60]) para que al ejecutar el cdigo de la macro RESTORE_ALL [arch/i386/kernel/entry.S#L125] el valor se situe en el registro %eax antes de volver a espacio de usuario (que es lo que espera la biblioteca libc). Al comienzo del fichero entry.S se han definido constantes que permiten acceder a los registros almacenados en la pila utilizando el registro %esp arch/i386/kernel/entry.S [54-68] 54 EBX = 0x00 55 ECX = 0x04 56 EDX = 0x08 57 ESI = 0x0C 58 EDI = 0x10 59 EBP = 0x14 60 EAX = 0x18 61 DS = 0x1C 62 ES = 0x20 63 ORIG_EAX = 0x24 64 EIP = 0x28 65 CS = 0x2C 66 EFLAGS = 0x30 67 OLDESP = 0x34 68 OLDSS = 0x38sys_call_table [arch/i386/kernel/entry.S#L574] es un vector de direcciones de salto que contiene las direcciones de la funciones que sirven cada una de las llamadas al sistema. Se utiliza el registro %eax como ndice, teniendo en cuenta que cada direccin ocupa 4 bytes.

5.6.2 Recogida de parmetros En C normalmente se utiliza la pila para pasar parmetros entre funciones. Antes de realizar la llamada a una funcin se insertan en la pila todos los parmetros luego se invoca a la funcin y sta lee los parmetros de la pila. Estructura de la pila en el x86 al llamar a una funcin.

En nuestro caso, la macro SAVE_ALL [arch/i386/kernel/entry.S#L84] guarda los registros (que es donde se reciben los parmetros de las llamadas) en la pila (push), por lo que si luego llamamos a una funcin C, sta se comportar como si otra funcin en C le hubiera pasado los parmetros. El orden en el que se insertan en la pila es importante. Los ltimos en insertarse en la pila son los primeros parmetros en la declaracin de la funcin en C.

5.7 Ejecucin de la llamada al sistema Siguiendo con el ejemplo de la llamada setpriority, cuando se ejecute la instruccin 251 call *sys_call_table(,%eax,4)se llamar a la funcin C cuya direccin se encuentra en la entrada 97 [include/asm-i386/unistd.h#L105] de la tabla sys_call_table [arch/i386/kernel/entry.S#L436]. Recordemos que en el cdigo de la biblioteca libc se asigno el valor 0x61(97) al registro %eax antes de ejecutar la instruccin int 0x80: ... d: b8 61 00 00 00 mov $0x61,%eax 12: cd 80 int $0x80...y que el tamao de cada direccin es de 32 bits, o sea 4 bytes. La funcin invocada es sys_setpriority(int which, int who, int niceval) [kernel/sys.c#L244], que se encargar de cambiar la prioridad del proceso o processo indicados. kernel/sys.c [244-263,295-296] 244 asmlinkage long sys_setpriority(int which, int who, int niceval) 245 { 246 struct task_struct *g, *p; 247 struct user_struct *user; 248 int error = -EINVAL; 249 250 if (which > 2 || which < 0) 251 goto out; 252 253 /* normalize: avoid signed division (rounding problems) */ 254 error = -ESRCH; 255 if (niceval < -20) 256 niceval = -20; 257 if (niceval > 19) 258 niceval = 19; 259 260 read_lock(&tasklist_lock); 261 switch (which) { 262 case PRIO_PROCESS: 263 if (!who)... 295 return error; 296 } Los parmetros que el usuario le paso a la funcin setpriority de la biblioteca libc han pasado por los registros %ebx, %ecx y %edx, luego se han copiado en la pila del ncleo y ese valor de la pila, en el lenguaje C, se ve ahora como los parmetros which, who y niceval de la funcin sys_setpriority.

5.8 Saliendo del ncleo: syscall_exitAl finalizar la ejecucin de la llamada al sistema se ejecuta el cdigo que se encuentra en syscall_exit [arch/i386/kernel/entry.S#L253]. Este cdigo lleva a cabo algunas comprobaciones antes de volver a modo usuario. #254 [syscall_exit] Se deshabilitan las interrupciones.

#257#259 Durante la ejecucin de la llamada al sistema el proceso ha podido cambiar de estado, y por lo tanto, haberse marcado la necesidad de planificar de nuevo, atender seales, etc. Este cdigo comprueba si hay trabajo pendiente o se est monitorizando el proceso invocante, saltando a syscall_exit_work [arch/i386/kernel/entry.S#L314] si as fuera.

#260#261 [restore_all] Si no hay trabajo pendiente y la instruccin no est siendo monitorizada, la llamada al sistema termina ejecutando el cdigo de restore_all. Este cdigo consiste nicamente en la macro RESTORE_ALL [arch/i386/kernel/entry.S#L125]. Esta macro restaura los registros almacenados con SAVE_ALL, ignora el ndice de la llamada al sistema insertado en la pila y ejecuta la instruccin de retorno de interrupcin iret. arch/i386/kernel/entry.S [99-106,108-111,125-128] 99 #define RESTORE_INT_REGS \ 100 popl %ebx; \ 101 popl %ecx; \ 102 popl %edx; \ 103 popl %esi; \ 104 popl %edi; \ 105 popl %ebp; \ 106 popl %eax... 108 #define RESTORE_REGS \ 109 RESTORE_INT_REGS; \ 110 1: popl %ds; \ 111 2: popl %es; \... 125 #define RESTORE_ALL \ 126 RESTORE_REGS \ 127 addl $4, %esp; \ 128 1: iret; \Es importante destacar que al ejecutar la instruccin pop %eax en realidad se est almacenando el cdigo de retorno de la llamada al sistema en el registro %eax (colocado ah por las instrucciones del tipo mov %eax, EAX(%esp))

5.9 Ejecutando el trabajo pendiente antes de volverAl finalizar una llamada al sistema o una interrupcin hay que comprobar si existe trabajo pendiente antes de volver a modo usuario. Las condiciones que se deben comprobar en el campo flags de la tarea actual son: Si el proceso se estaba monitorizando Se debe invocar a la funcin do_syscall_trace(). Viene indicado en el campo flags por los indicadores: _TIF_SYSCALL_TRACE, _TIF_SYSCALL_AUDIT y/o _TIF_SINGLESTEP . Si hay peticiones de planificacin pendientes Se debe invocar a la funcin schedule(). Viene indicado en el campo flags por el indicador: _TIF_NEED_RESCHED. Si hay seales pendientes Se debe invocar a la funcin do_notify_resume(). Viene indicado en el campo flags por el indicador: _TIF_SIGPENDING.

Salir del ncleo al terminar una llamada al sistema comparte mucho cdigo con el retorno de una interrupcin. Diagrama de flujo del proceso de salida del ncleo.

Si el campo flags (accedido mediante el offset TI_flags) de la estructura thread_info de la tarea actual indica que hay trabajo pendiente antes de volver al espacio de usuario, entonces se ejecuta el cdigo de syscall_exit_work [arch/i386/kernel/entry.S#L314]. arch/i386/kernel/entry.S [314-322] 314 syscall_exit_work: 315 testb $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SINGLESTEP), %cl 316 jz work_pending 317 sti # could let do_syscall_trace() call 318 # schedule() instead 319 movl %esp, %eax 320 movl $1, %edx 321 call do_syscall_trace 322 jmp resume_userspace#315#316 [syscall_exit_work] Se comprueba se est monitorizando a la tarea invocante. Si es as, se continua con la ejecucin, en caso contrario se salta a work_pending [arch/i386/kernel/entry.S#L265]

#317#322 Si el proceso estaba siendo monitorizado, se invoca a do_syscall_trace y despus se continua con el trabajo pendiente, si lo hubiera, en resume_userspace.

Si el campo TI_flags indica que hay trabajo pendiente (independientemente de si se est monitorizando la tarea invocante o no) se acaba ejecutando el cdigo work_pending [arch/i386/kernel/entry.S#L265]. arch/i386/kernel/entry.S [265-288] 265 work_pending: 266 testb $_TIF_NEED_RESCHED, %cl 267 jz work_notifysig 268 work_resched: 269 call schedule 270 cli # make sure we don't miss an interrupt 271 # setting need_resched or sigpending 272 # between sampling and the iret 273 movl TI_flags(%ebp), %ecx 274 andl $_TIF_WORK_MASK, %ecx # is there any work to be done other 275 # than syscall tracing? 276 jz restore_all 277 testb $_TIF_NEED_RESCHED, %cl 278 jnz work_resched 279 280 work_notifysig: # deal with pending signals and 281 # notify-resume requests 282 testl $VM_MASK, EFLAGS(%esp) 283 movl %esp, %eax 284 jne work_notifysig_v86 # returning to kernel-space or 285 # vm86-space 286 xorl %edx, %edx 287 call do_notify_resume 288 jmp restore_all#266#267 [work_resched] Se comprueba si la hace falta invocar al planificador antes de volver al espacio de usuario. Si no se pasa a work_notifysig [arch/i386/kernel/entry.S#L280] para ver si hay seales pendientes.

#269 Se invoca a la funcin de planificacin. Posiblemente el proceso que salga de dicha funcin sea un proceso diferente al actual. Esto se tratar con el debido detalle en el tema 7 "Gestin de procesos".

#270Tras ejecutar el planificador se comprueba si la tarea actual (posiblemente una distinta) tiene trabajo pendiente, repitiendose el proceso desde work_resched (#266).

#280#287 [work_notifysig] Tras algunas comprobaciones, se invoca a la funcin do_notify_resume [arch/i386/kernel/signal.c#L642] que se encarga entre otras cosas de comprobar si el proceso tiene seales pendientes.

#288Finalmente se salta a restore_all donde se restauran todos los registros mediante la macro RESTORE_ALL.

Se puede observar que la ejecucin ms frecuente de system_call [arch/i386/kernel/entry.S#L241] (la llamada es correcta, no est siendo monitorizada y no hay trabajo pendiente) est optimizada para no tomar absolutamente ningn salto, a excepcin de la propia llamada a la funcin. Los procesadores actuales basan la mayor parte de sus prestaciones en la ejecucin segmentada de instrucciones, y las instrucciones de salto suelen forzar el vaciado de la unidad de ejecucin si la condicin de salto no se predice correctamente. El cdigo de Linux intenta minimizar el nmero de saltos dentro del flujo de control ms frecuente.

5.10 Resumen1. La biblioteca mete en los registros del procesador los parmetros de la llamada. 2. Se produce la interrupcin software (trap) 0x80. El descriptor de interrupcin. 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 nmero de llamada al sistema corresponde con una llamada vlida. 5. Se salta a la funcin que implementa el servicio pedido:call *sys_call_table(,%eax,4)

6. Si mientras se atenda la llamada al sistema se ha producido algn evento que requiera llamar al planificador, se llama en este punto. 7. Si el proceso al que se va a retornar tiene seales pendientes, se le envan ahora. 8. Se restauran los valores de los registros con RESTORE_ALL y se vuelve de la interrupcin software. 9. La biblioteca comprueba si la llamada ha producido algn error, y en este caso guarda en la variable errno el valor de retorno.