Una llamada al sistema es cuando un programa llama a un servicio del Kernel del sistema operativo. Generalmente estos servicios se encuentran disponibles como instrucciones de lenguaje ensamblador pero algunas veces también pueden llamarse (sin tener conocimiento de ello) desde lenguajes de alto nivel.
Kernel o núcleo
Este se considera el principal componente de los sistemas operativos sirve de interfaz entre aplicaciones y el proceso de los datos de bajo nivel hecho por el hardware del equipo. Entre sus funciones están administrar los recursos del sistema como memoria , uso de CPU por las aplicaciones o acceso a interfaces de entrada y salida del equipo.
Ejemplos de esto son:
Kernel o núcleo
Este se considera el principal componente de los sistemas operativos sirve de interfaz entre aplicaciones y el proceso de los datos de bajo nivel hecho por el hardware del equipo. Entre sus funciones están administrar los recursos del sistema como memoria , uso de CPU por las aplicaciones o acceso a interfaces de entrada y salida del equipo.
Ejemplos de esto son:
- Exit: Para terminar la ejecución de un programa.
- Fork: Para crear hilos de ejecución.
- Time: Para ver la hora actual.
- Open: Para abrir archivos
- Close: para cerrar archivos
Como puede observarse estas son funciones muy utilizadas en cualquier lenguaje de programación pero sin darnos cuenta estas son administradas por el núcleo del sistema operativo.
Llamadas al sistema en linux
Conceptos útiles:
El espacio kernel:
En este espacio residen todas las subrutinas que forman parte del
kernel tales como módulos o drivers y cualquier otra que tenga que ver
con la interfaz kernel.
El espacio de usuario:
En este espacio se encuentran los programas que utiliza el usuario
final, tales como shell y cualquiera de los programas que he estado
programando de ejemplos en ensamblador.
[imagen obtenida de: http://www.rootkitanalytics.com/images/KernelWorking1.png]
Existen diferentes implementaciones en el mundo Linux en los procesadores x86 más antiguos se utilizaban interrupciones para migrar de un espacio de usuario a un espacio Kernel ( lo cuál es un objetivo intermedio de las llamadas al sistema).
En Linux en las llamadas al sistema se utiliza el registro "%eax" para identificar la llamada al sistema que se quiere invocar, la cual se especifica en una biblioteca de Ansi C. Para empezar la llamada al sistema se invoca al vector de interrupción de Linux (0x80) que contiene las direcciones de memoria de los controladores de interrupciones. Cuando una llamada al sistema es generada, el sistema operativo salva el estado de ejecución del proceso y empieza la ejecución del controlador de llamadas al sistema en el vector de interrupciones.
Una vez esto el controla de llamadas al sistema provoca la ejecución de la función "system_call", esta función controla todas las llamadas al sistema, y toma como parámetro el valor contenido en el registro %eax para identificar la llamada en especifico. Una vez realizadas una serio de verificaciones se invoca la llamada real al sistema utilizando la funcion "system_call_table" y el indice en "%eax". Una vez que el kernel termina su ejecución invoca ala función "system_call_exit" y se llama a las transiciones de resumen "resume_userspace" nuevamente para regresar al espacio de usuario para regresar a la aplicación del usuario.
[imagen obtenida de. http://www.ibm.com/developerworks/ssa/linux/library/l-system-calls/figure1.gif]
Por último en la página que esta abajo encontré la lista de llamadas al sistema implementadas en el Kernel de Linux, que utilicé como guía para mi ejemplo de este tema.
Ejemplo de llamada al sistema en ensamblador x86
Parametros
En la tabla de llamadas al sistema de linux se puede ver que en a partir de la tercera columna se encabezan nombres de registros y en las celdas tipos de variables. Esto es por que estos registros se utilizan como parámetros para las funciones de llamadas al sistema, el primer parámetro va en el registro "%ebx" el segundo va en "%ecx", el tercero en "%edx" y el cuatro en "%esi", el quinto en "%edi" y el sexto y ultimo en "%ebp".
Hilos usando llamadas al sistema
Como ejemplo de una llamada al sistema decidí implementar la llamada al sistema numero dos del kernel de linux con el que cuento, esta de lo que se encarga es de partir nuestro proceso en dos y crear una copia exacta del mismo que corre de manera independiente del proceso del cual fue creado.
.LC0: .string "A: %d\n" .LC1: .string "B: %d\n" .LC2: .string "%d\n" .globl A ;Esta función solo sirve para imprimir el resultado A: ;con el nombre del thread que lo manda. pushq %rbp movq %rsp, %rbp movq 16(%rsp), %rsi movq $.LC0, %rdi movl $0, %eax call printf leave ret .globl B ;Esta hace lo mismo con el threadB B: pushq %rbp movq %rsp, %rbp movq 16(%rsp), %rsi movq $.LC1, %rdi movl $0, %eax call printf leave ret .globl threads threads: pushq %rbp ;Recuperamos la base y el top de la función en el stack de memoria movq %rsp, %rbp subq $8, %rsp movq $50, 16(%rsp) movq $2, %rax ;Aquí es donde se define el parámetro al kernel de la función del sistema a la cual queremos llamar int $0x80 ;Aquí se hace la interrupción y la llamada a system_call cmpq $0, %rax ;El parametro de la llamada se devuelve en el registro %eax jz .threadA ;Si el valor es cero significa que se esta en el proceso padre cmpq $0, %rax ; En caso contrario se esta en un proceso hijo jg .threadB cmpq $0, %rax jl .threadB .threadA: ; estas funciones solo aumentan y disminuyen el valor en 16(%rsp), despues salen y terminan su ejecución addq $1, 16(%rsp) ; aquí se aumenta pushq 16(%rsp) call A popq %rax cmpq $100, 16(%rsp) jge .fin cmpq $2000, 16(%rsp) jl .threadA .threadB: subq $1, 16(%rsp) ; aqui se disminuye pushq 16(%rsp) call B popq %rax cmpq $0, 16(%rsp) jle .fin cmpq $0, 16(%rsp) jg .threadB .fin: movq $1, %rax int $0x80
Como puede observarse cada impresión en la terminal se hace de forma asíncrona entre las dos funciones, ademas de que a pesar de que parece que comparten la misma dirección de memoria como contador, eso no es verdad pues como vemos la misma variable tiene valores diferentes para cada thread, lo cual nos dice que el kernel nos asigna un nuevo stack de memoria exclusivo para el nuevo thread.

8 pts lab integrados
ResponderBorrar