Documentos de Académico
Documentos de Profesional
Documentos de Cultura
5.1 Introduccin
Vamos 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.
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
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.
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)
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.
arch/i386/kernel/traps.c [995-996,1009-1017,1032,1037-1040]
995 void __init trap_init(void)
996 {
...
1009
set_trap_gate(0,÷_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
fstream.h
# ar t /usr/lib/libc.a
init-first.o
libc-start.o
set-init.o
sysdep.o
version.o
check_fds.o
...
# ar x /usr/lib/libc.a setpriority.o
# objdump -d setpriority.o
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
arch/i386/kernel/entry.S [240-261]
#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
#246 Se verifica si el proceso padre est monitorizando este proceso, en cuyo caso se ejecuta
#247 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, <0 =>
BUG */
...
47 };
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 [syscall_call] Se llama a la funcin que sirve la llamada pedida y se guarda
#252
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).
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 = 0x38
kernel/sys.c [244-263,295-296]
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))
arch/i386/kernel/entry.S [314-322]
314 syscall_exit_work:
315 testb
$(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SI
NGLESTEP), %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 [syscall_exit_work] Se comprueba se est monitorizando a la tarea
#316 invocante. Si es as, se continua con la ejecucin, en caso contrario se salta a
work_pending [arch/i386/kernel/entry.S#L265]
#317 Si el proceso estaba siendo monitorizado, se invoca a do_syscall_trace y
#322 despus se continua con el trabajo pendiente, si lo hubiera, en
resume_userspace.
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 [work_resched] Se comprueba si la hace falta invocar al planificador antes de
#267
volver al espacio de usuario. Si no se pasa a work_notifysig
[arch/i386/kernel/entry.S#L280] para ver si hay seales pendientes.
#270 Tras ejecutar el planificador se comprueba si la tarea actual (posiblemente una distinta)
tiene trabajo pendiente, repitiendose el proceso desde work_resched (#266).
5.10 Resumen
1. 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)