2012-07-26 8 views
15

Tengo entendido que, en general, el comportamiento no está definido si llamas a una función de señal segura no asíncrona desde un manejador de señal, pero he escuchado que linux te permite llamar a cualquier sistema de manera segura. ¿Es esto cierto? Además, el único comportamiento portátil para un manejador SIGSEGV es abortar o salir, pero entiendo que Linux realmente reanudará la ejecución si regresa, ¿cierto?¿Linux permite que cualquier llamada al sistema se realice desde manejadores de señal?

+1

¿Dónde has oído hablar de eso? – moooeeeep

+2

específicamente la respuesta a esta pregunta: http://stackoverflow.com/questions/2663456/write-a-signal-handler-to-catch-sigsegv –

+0

Me hubiera gustado haber agregado el término "llamada sin procesar del sistema" al título: -) –

Respuesta

5

Creo que se puede llamar a cualquier llamada al sistema desde un controlador de señal. Un verdadero syscall tiene un número en <asm/unistd.h> (o <asm/unistd_64.h>).

algunas funciones POSIX de la sección 2 de las páginas de manual se implementan a través de una llamada al sistema "multiplexado", por lo que no son "verdaderos llamadas al sistema" en mi sentido

Una llamada al sistema es una operación atómica desde el punto de vista de la aplicación; es casi como una instrucción de una sola máquina (desde el interior de la aplicación). Ver this answer.

Si su pregunta es: ¿puede un controlador SIGSEGV cambiar la asignación de direcciones defectuosas a través de mprotect o mmap? entonces creo que la respuesta es (al menos en x86-64 & x86-32 arquitecturas), como said here en una pregunta que usted citó, pero no lo intenté. He leído que hacerlo es bastante ineficiente (SIGSEGV el manejo no es muy rápido, y mprotect o mmap también es un poco lento). En particular, imitar de esta manera Hurd/Mach external pagers podría ser ineficiente.

+0

Las llamadas al sistema son algo atómicas, pero pueden devolver el control al código de usuario, ya sea desde el que se llamaron desde ellas o desde un manejador de señal. El comportamiento normal es que el manejador de señal haga algo, luego regrese, luego la llamada al sistema interrumpido devuelve EINTR. (y el glibc puede volver a llamarlo, creo que es lo que sucede.) Si hace una llamada al sistema desde dentro de un manejador de señal, supongo que el sistema operativo pone en cola otras señales hasta que termine. O, opcionalmente, hace algo extraño si llama a una llamada al sistema que no está en la lista de seguridad garantizada. –

+0

¡Solo lo creeré cuando vea un documento en el árbol escrito por el mismo Linus! :-) –

16

Según section 2 signal manual:

Ver señal (7) para obtener una lista de las funciones asíncrono de señal de seguridad que pueden ser llamados con seguridad desde el interior de un manejador de señales.

Y section 7 signals manual enumera las siguientes funciones y/o llamadas al sistema junto con una descripción bastante claro: funciones

asíncrono de señal seguras

A signal handler function must be very careful, since processing elsewhere may 
    be interrupted at some arbitrary point in the execution of the program. POSIX 
    has the concept of "safe function". If a signal interrupts the execution of 
    an unsafe function, and handler calls an unsafe function, then the behavior of 
    the program is undefined. 

    POSIX.1-2004 (also known as POSIX.1-2001 Technical Corrigendum 2) requires an 
    implementation to guarantee that the following functions can be safely called 
    inside a signal handler: 

     _Exit() 
     _exit() 
     abort() 
     accept() 
     access() 
     aio_error() 
     aio_return() 
     aio_suspend() 
     alarm() 
     bind() 
     cfgetispeed() 
     cfgetospeed() 
     cfsetispeed() 
     cfsetospeed() 
     chdir() 
     chmod() 
     chown() 
     clock_gettime() 
     close() 
     connect() 
     creat() 
     dup() 
     dup2() 
     execle() 
     execve() 
     fchmod() 
     fchown() 
     fcntl() 
     fdatasync() 
     fork() 
     fpathconf() 
     fstat() 
     fsync() 
     ftruncate() 
     getegid() 
     geteuid() 
     getgid() 
     getgroups() 
     getpeername() 
     getpgrp() 
     getpid() 
     getppid() 
     getsockname() 
     getsockopt() 
     getuid() 
     kill() 
     link() 
     listen() 
     lseek() 
     lstat() 
     mkdir() 
     mkfifo() 
     open() 
     pathconf() 
     pause() 
     pipe() 
     poll() 
     posix_trace_event() 
     pselect() 
     raise() 
     read() 
     readlink() 
     recv() 
     recvfrom() 
     recvmsg() 
     rename() 
     rmdir() 
     select() 
     sem_post() 
     send() 
     sendmsg() 
     sendto() 
     setgid() 
     setpgid() 
     setsid() 
     setsockopt() 
     setuid() 
     shutdown() 
     sigaction() 
     sigaddset() 
     sigdelset() 
     sigemptyset() 
     sigfillset() 
     sigismember() 
     signal() 
     sigpause() 
     sigpending() 
     sigprocmask() 
     sigqueue() 
     sigset() 
     sigsuspend() 
     sleep() 
     sockatmark() 
     socket() 
     socketpair() 
     stat() 
     symlink() 
     sysconf() 
     tcdrain() 
     tcflow() 
     tcflush() 
     tcgetattr() 
     tcgetpgrp() 
     tcsendbreak() 
     tcsetattr() 
     tcsetpgrp() 
     time() 
     timer_getoverrun() 
     timer_gettime() 
     timer_settime() 
     times() 
     umask() 
     uname() 
     unlink() 
     utime() 
     wait() 
     waitpid() 
     write() 

    POSIX.1-2008 removes fpathconf(), pathconf(), and sysconf() from the above 
    list, and adds the following functions: 

     execl() 
     execv() 
     faccessat() 
     fchmodat() 
     fchownat() 
     fexecve() 
     fstatat() 
     futimens() 
     linkat() 
     mkdirat() 
     mkfifoat() 
     mknod() 
     mknodat() 
     openat() 
     readlinkat() 
     renameat() 
     symlinkat() 
     unlinkat() 
     utimensat() 
     utimes() 

creo que esta información sea más fiable que algo que escuchamos a veces en alguna parte. Entonces, Linux solo permite algunas llamadas al sistema, pero no todas. Entonces la respuesta a tu pregunta es simplemente - no.

+1

Sin embargo, parece ser posix en lugar de linux. Sé que Linux (se supone que es) compatible con posix, así que entendí que, como mínimo, esas funciones son seguras, me gustaría entender si hay alguna más allá (en particular, mprotect) que también lo es. –

+1

@gct: Esta es la página de manual específica de Linux. Es lo más preciso que puede obtener después de buscar el código fuente. Si le apetece revisar el código fuente y hacer un mejor análisis ... vaya por él :) –

+0

@gct: Por cierto, hay una mejor manera de manejar las señales sin esta restricción - tiene que usar 'epoll' con 'signalfd'. Luego puede hacer lo que quiera en el controlador, consulte http://www.kernel.org/doc/man-pages/online/pages/man2/signalfd.2.html –

6

sí y no

Sí:

Puede llamar a cualquier llamada al sistema de bienes/prima dentro de un manejador de señales. El kernel tiene la responsabilidad de garantizar que sea seguro (desde el punto de vista del kernel).

1) El núcleo no conoce el contexto del espacio de usuario, o dice que el kernel lo olvida intencionalmente después de guardar el estado en el espacio de usuario cuando se entrega la señal. (NOTA: la reanudación de la ejecución es realizada por el usuario a través de un syscall con la ayuda de los estados guardados, no realmente por el kernel, el kernel ya lo ha olvidado)

2) algunos hilos lib se implementan a través de singles, por lo que los hilos son ya está en "manejador de señal", pero estos hilos pueden llamar a cualquier syscall.

Nº:

Pero las funciones de espacio de usuario tienen su propio propósito y efecto secundario. Algunos no son re-entrance safe, esas funciones no se pueden llamar desde el manejador de señal. man 7 signal le ayudará a saber cuáles son seguros para la reentrada.

Tome ejemplo, puede llamar en cualquier lugar incluyendo sys_futex() manejador de la señal, pero si se utiliza sys_futex() para implementar un mutex, el sys_futex() dentro del manejador de señal puede bloqueado para siempre cuando se interrumpe la señal de la sección crítica de la exclusión mutua.

Además, el único comportamiento portátil para un controlador SIGSEGV es abortar o salida, pero entiendo Linux en realidad se reanudará la ejecución si retorno, verdad?

Sí, si no puede encontrar el motivo. Algunos usuarios pueden usar SIGSEGV para su propio propósito de mapa cuando es necesario (por ejemplo, en JIT, puede traducir el código en el manejador de señal SIGSEGV y mmap el código traducido a la memoria y luego regresar), pueden llamar a mmap() o mprotect() ... etc.

+0

"Puede llamar a cualquier syscall real/sin procesar dentro de un manejador de señal" ¿Tiene algún recurso (por ejemplo, documentos del kernel, comentarios) para respaldar ese reclamo? –

Cuestiones relacionadas