2012-07-26 1 views
15

La mia comprensione è che, in generale, il comportamento non è definito se si chiama una funzione di segnale non asincrono da un gestore di segnale, ma ho sentito che linux consente di chiamare qualsiasi chiamata di sistema in modo sicuro. È vero? Inoltre, l'unico comportamento portatile per un gestore SIGSEGV è quello di interrompere o uscire, ma capisco che linux riprenderà effettivamente l'esecuzione se si ritorna, vero?Linux consente a qualsiasi chiamata di sistema di essere effettuata da gestori di segnale?

+1

dove hai sentito parlare di questo? – moooeeeep

+2

in particolare la risposta a questa domanda: http://stackoverflow.com/questions/2663456/write-a-signal-handler-to-catch-sigsegv –

+0

Vorrei che avessi aggiunto il termine "raw system call" al titolo: -) –

risposta

5

Credo che qualsiasi chiamata di sistema reale possa essere chiamata da un gestore di segnale. Un vero syscall ha un numero in <asm/unistd.h> (o <asm/unistd_64.h>).

alcune funzioni POSIX dalla sezione 2 della pagine man vengono implementate attraverso una chiamata di sistema "multiplexing", quindi non sono "vere chiamate di sistema" nel mio senso

Una chiamata di sistema è un'operazione atomica dal punto di vista dell'applicazione; è quasi come una singola istruzione macchina (dall'interno dell'applicazione). Vedi this answer.

Se la tua domanda è: un gestore SIGSEGV può modificare il mapping degli indirizzi difettoso tramite mprotect o mmap? quindi credo che la risposta sia (almeno su architetture x86-64 & x86-32), come said here in una domanda che hai citato, ma non ci ho provato. Ho letto che farlo è abbastanza inefficiente (la gestione dello SIGSEGV non è molto veloce, e mprotect o mmap è anche un po 'lento). In particolare, l'imitazione di questo modo Hurd/Mach external pagers potrebbe essere inefficiente.

+0

Le chiamate di sistema sono un po 'atomiche, ma possono restituire il controllo al codice utente da cui sono stati chiamati o in un gestore di segnali. Il comportamento normale è che il gestore di segnali esegua qualcosa, quindi restituisca, quindi la chiamata di sistema interrotta restituisce EINTR. (e glibc può chiamarlo di nuovo, penso sia quello che succede.) Se si effettua una chiamata di sistema da un gestore di segnale, il sistema operativo suppongo che le code segnali ulteriormente fino al termine. Oppure opzionalmente fa qualcosa di strano se chiami una chiamata di sistema che non è nell'elenco delle sicurezze garantite. –

+0

Ci crederò solo quando vedrò un documento in-tree scritto dallo stesso Linus! :-) –

16

Secondo section 2 signal manual:

Vedere il segnale (7) per un elenco delle funzioni async-signal-safe che possono essere chiamati in modo sicuro dall'interno di un gestore di segnale.

E section 7 signals manual elenca le seguenti funzioni e/o chiamate di sistema insieme a una descrizione abbastanza chiaro: funzioni

Async-signal-safe

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() 

credo queste informazioni per essere più affidabile rispetto qualcosa che sentiamo a volte da qualche parte. Quindi Linux consente solo alcune chiamate di sistema ma non tutte. Quindi la risposta alla tua domanda è semplicemente - no.

+1

Questo sembrerebbe essere invece posix piuttosto che specifico di Linux. So che Linux è (dovrebbe essere) compatibile con posix, quindi ho pensato che significasse almeno che le funzioni sono sicure, mi piacerebbe capire se ce ne sono anche sopra e oltre (in particolare mprotect). –

+1

@gct: questa è una pagina di manuale specifica per Linux. È la cosa più accurata che puoi ottenere dopo aver guardato il codice sorgente. Se hai voglia di leggere il codice sorgente e fare un'analisi migliore ... vai per questo :) –

+0

@gct: BTW, c'è un modo migliore di gestire i segnali senza questa restrizione - devi usare 'epoll' con 'signalfd'. Quindi puoi fare tutto ciò che vuoi nel gestore, vedi http://www.kernel.org/doc/man-pages/online/pages/man2/signalfd.2.html –

6

sì e no

Sì:

È possibile chiamare qualsiasi reale/grezza chiamata di sistema all'interno di un gestore di segnale. Il kernel ha la responsabilità di garantire che sia sicuro (nella vista del kernel).

1) Il kernel non conosce il contesto dello spazio utente, o dice che il kernel lo dimentica intenzionalmente dopo aver salvato lo stato nello spazio utente quando viene inviato il segnale. (NOTA: la ripresa dell'esecuzione viene eseguita dall'utente tramite un syscall con l'aiuto degli stati salvati, non proprio dal kernel, il kernel ha già dimenticato)

2) alcuni thread lib sono implementati tramite singoli, quindi i thread sono già in "signal handler", ma questi thread possono chiamare qualsiasi syscall.

NO:

ma le funzioni di user space hanno il loro scopo e effetto collaterale proprio. Alcuni non sono re-entrance safe, quelle funzioni non possono essere chiamate dal gestore di segnale. man 7 signal ti aiuterà a scoprire quali sono di nuovo al sicuro.

Prendete ad esempio, è possibile chiamare sys_futex() ovunque tra cui gestore di segnale, ma se si utilizza sys_futex() per implementare un mutex, il sys_futex() all'interno gestore di segnale potrebbe essere bloccato per sempre quando il segnale interrompe la sezione critica del mutex.

Inoltre, l'unico comportamento portatile per un gestore SIGSEGV è per interrompere o uscita, ma capisco linux sarà effettivamente riprendere l'esecuzione se si ritorno, vero?

Sì, se non riesci a scoprirne il motivo. Alcuni utenti possono utilizzare SIGSEGV per il proprio scopo map-when-demand (ad esempio, in JIT, è possibile tradurre il codice nel gestore di segnale SIGSEGV e mmap il codice tradotto in memoria e quindi tornare), possono chiamare mmap() o mprotect() ...eccetera.

+0

"Puoi chiamare qualsiasi syscall reale/grezzo all'interno di un gestore di segnali" Hai qualche risorsa (ad esempio documenti del kernel, commenti) per supportare tale richiesta? –