2012-10-15 18 views
8

Opracowałem bibliotekę, która obsługuje sygnały SIGILL. Ponieważ chcę uniknąć zależności od libc i bezpośrednio korzystać z Linuksa. Zauważyłem, że moja biblioteka zawiesza się na niektórych systemach Linux, a po wielu debugowaniach odkryłem, że używanie rt_sigaction syscall zamiast sigaction rozwiązuje problem. Jednak nie znalazłem opisu różnicy między tymi dwoma systemami. Czy ktokolwiek na SO zna podstawowe szczegóły?Jaka jest różnica między syscalls sygnału i rt_signal w systemie Linux?

Aktualizacja: Używam programów obsługi sygnału do wykrywania obsługi procesorów dla niektórych rozszerzeń instrukcji ARM, np. XScale instrukcja MIATT. Oto funkcja sondowania instrukcja:

static uint32_t probe_xscale() { 
    register uint32_t retValue asm("r0") = 0; 
    asm volatile (
     // Equivalent of the following code: 
     // ".arch xscale\n" 
     // "MIATT acc0, r0, r0;" 
     // If the next line raises SIGILL, the signal handle will change r0 to 1 and skip the instruction (4 bytes) 
     "MCR P0, 0x1, r0, c15, c0, 0;" 
     : "+r" (retValue) 
     : 
     : 
    ); 
    return retValue; 
} 

W obsługi SIGILL I awansować rejestr PC przez 4 bajty (rozmiar instrukcji) i zmienić jedną z rejestrów wskazują, że SIGILL obsługi została wywołana. Oto kod obsługi sygnału.

static void probe_signal_handler(int, siginfo_t *, void* ptr) { 
    ucontext_t* ctx = (ucontext_t*)ptr; 
    ctx->uc_mcontext.arm_pc += 4; 
    ctx->uc_mcontext.arm_r0 = 1; 
} 

Oto jak zrobić to sondowanie (funkcja zwraca 0, jeśli instrukcja nie powoduje SIGILL 1 jeśli SIGILL obsługi nazwano i 2, jeżeli sigaction syscall nie powiodło się):

static uint32_t probeInstruction(uint32_t (*ProbeFunction)()) { 
    struct sigaction oldSigillAction; 
    struct sigaction probeSigillAction; 
    memset(&probeSigillAction, 0, sizeof(probeSigillAction)); 
    probeSigillAction.sa_sigaction = &probe_signal_handler; 
    // Needs Linux >= 2.2 
    probeSigillAction.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO; 
    int sigactionResult = _syscall_sigaction(SIGILL, &probeSigillAction, &oldSigillAction); 
    if (sigactionResult == 0) { 
     const uint32_t probeResult = ProbeFunction(); 
     _syscall_sigaction(SIGILL, &oldSigillAction, NULL); 
     return probeResult; 
    } else { 
     return 2; 
    } 
} 

Tutaj moja realizacja sigaction funkcji syscall skrótowej:

static int _syscall_sigaction(int signum, const struct sigaction *new_action, struct sigaction *old_action) __attribute__((noinline)); 
static int _syscall_sigaction(int signalNumberParameter, const struct sigaction *newActionParameter, struct sigaction *oldActionParameter) { 
    register int result asm ("r0"); 
    register int signalNumber asm ("r0") = signalNumberParameter; 
    register const struct sigaction *newAction asm ("r1") = newActionParameter; 
    register struct sigaction *oldAction asm ("r2") = oldActionParameter; 
    register int syscallNumber asm ("r7") = __NR_rt_sigaction; 
    asm volatile (
     "swi $0;" 
     : "=r" (result) 
     : "r" (signalNumber), "r" (newAction), "r" (oldAction), "r" (syscallNumber) 
     : 
    ); 
    return result; 
} 

testowałem ten kod w emulatorze z Android SDK (qemu), a na Pandaboard Ubuntu. W emulatorze kod działa dobrze (zarówno podczas emulacji ARM9, jak i Cortex-A8), ale na Pandaboard zawiesza się na instrukcji MIATT, jeśli używam __NR_sigaction: wydaje się, że po obsłudze sygnału kod nie pomija 4 bajtów, ale działa ta sama instrukcja.

+0

Zgaduję, że wersja 'rt_sigaction' jest wersją" w czasie rzeczywistym ". Oznacza to, że projekt ma deterministyczny czas połączenia. –

+0

Są to prawie dokładnie ten sam kod, oba grunty w do_sigaction() w jądrze. Jeśli masz problemy, prawdopodobnie pomoże to je wyszczególnić. –

+0

Dodałem więcej szczegółów i odpowiednią część mojego kodu do pytania. –

Odpowiedz

1

Od man sigaction (link) cytuję:

Oryginalny wywołanie systemowe Linux został nazwany sigaction(). Jednak dzięki dodaniu sygnałów czasu rzeczywistego w systemie Linux 2.2, 32-bitowy typ sigset_t obsługiwany przez to wywołanie systemowe o stałym rozmiarze, , nie był już przeznaczony do tego celu. W związku z tym nowe wywołanie systemowe, rt_sigaction(), zostało dodane w celu obsługi powiększonego typu sigset_t. Nowe wywołanie systemowe pobiera czwarty argument, size_t sigsetsize, który określa rozmiar zbioru sygnałów w act.sa_mask i oldact.sa_mask.

+0

Również z' man sigaction ', jeśli używasz glibc, nie ma potrzeby używania rt_sigaction. "Funkcja opakowująca sigli() glibc ukrywa przed nami te szczegóły, w przejrzysty sposób wywołując funkcję rt_sigaction(), gdy dostarcza ją jądro." – cooperised

4

nie mam jednoznacznej odpowiedzi, ale będę nadal starają się przyczynić:

Patrząc na źródła jądra:

300SYSCALL_DEFINE3(sigaction, int, sig, const struct sigaction __user *, act, 
301  struct sigaction __user *, oact) 
302{ 
303  struct k_sigaction new_ka, old_ka; 
304  int ret; 
305  int err = 0; 
306 
307  if (act) { 
308    old_sigset_t mask; 
309 
310    if (!access_ok(VERIFY_READ, act, sizeof(*act))) 
311      return -EFAULT; 
312    err |= __get_user(new_ka.sa.sa_handler, &act->sa_handler); 
313    err |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); 
314    err |= __get_user(mask, &act->sa_mask.sig[0]); 
315    if (err) 
316      return -EFAULT; 
317 
318    siginitset(&new_ka.sa.sa_mask, mask); 
319  } 
320 
321  ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); 
322 
323  if (!ret && oact) { 
324    if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact))) 
325      return -EFAULT; 
326    err |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); 
327    err |= __put_user(old_ka.sa.sa_handler, &oact->sa_handler); 
328    err |= __put_user(old_ka.sa.sa_mask.sig[0], oact->sa_mask.sig); 
329    err |= __put_user(0, &oact->sa_mask.sig[1]); 
330    err |= __put_user(0, &oact->sa_mask.sig[2]); 
331    err |= __put_user(0, &oact->sa_mask.sig[3]); 
332    if (err) 
333      return -EFAULT; 
334  } 
335 
336  return ret; 
337} 
338#endif 

vs.

2955SYSCALL_DEFINE4(rt_sigaction, int, sig, 
2956    const struct sigaction __user *, act, 
2957    struct sigaction __user *, oact, 
2958    size_t, sigsetsize) 
2959{ 
2960  struct k_sigaction new_sa, old_sa; 
2961  int ret = -EINVAL; 
2962 
2963  /* XXX: Don't preclude handling different sized sigset_t's. */ 
2964  if (sigsetsize != sizeof(sigset_t)) 
2965    goto out; 
2966 
2967  if (act) { 
2968    if (copy_from_user(&new_sa.sa, act, sizeof(new_sa.sa))) 
2969      return -EFAULT; 
2970  } 
2971 
2972  ret = do_sigaction(sig, act ? &new_sa : NULL, oact ? &old_sa : NULL); 
2973 
2974  if (!ret && oact) { 
2975    if (copy_to_user(oact, &old_sa.sa, sizeof(old_sa.sa))) 
2976      return -EFAULT; 
2977  } 
2978out: 
2979  return ret; 
2980} 

differance jako Widzę, że to rt_sigaction kopiuje całą sigaction struct, podczas gdy sigaction pobiera i zmienia pamięć inline (używając funkcji get/set user) ... nie jestem pewien, ale może to zabiera więcej czasu uzyskać dostęp do pamięci przestrzeni użytkownika bezpośrednio, zamiast pracować z tymczasową kopią.