Quelle est la différence entre les syscalls signal et rt_signal sous Linux?

J’ai développé une bibliothèque qui gère les signaux SIGILL. Puisque je veux éviter la dépendance à la libc et utiliser directement les appels système Linux. J’ai remarqué que ma bibliothèque se bloque sur certains systèmes Linux et après beaucoup de débogage, j’ai trouvé que l’utilisation de rt_sigaction rt_sigaction au lieu de sigaction résout le problème. Cependant, je n’ai pas trouvé de description de la différence entre les deux appels système. Est-ce que quelqu’un sur SO connaît les détails sous-jacents?

Mise à jour: J’utilise des gestionnaires de signaux pour détecter le support du processeur pour certaines extensions d’instructions ARM, par exemple l’instruction MIATT XScale. Voici la fonction de sondage des instructions:

 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; } 

Dans le gestionnaire SIGILL, j’avance le registre PC de 4 octets (taille de cette instruction) et change un des registres pour indiquer que le gestionnaire SIGILL a été appelé. Voici le code du gestionnaire de signaux.

 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; } 

Voici comment je fais le sondage (la fonction retourne 0 si l’instruction n’a pas provoqué SIGILL, 1 si le gestionnaire SIGILL a été appelé et 2 si le message sigaction a échoué):

 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; } } 

Voici mon implémentation de la fonction subcation syscall stub:

 static int _syscall_sigaction(int signum, const struct sigaction *new_action, struct sigaction *old_action) __atsortingbute__((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; } 

J’ai testé ce code dans l’émulateur d’Android SDK (qemu) et sur Pandaboard sous Ubuntu. Dans l’émulateur, le code fonctionne bien (tous les deux lors de l’émulation des processeurs ARM9 et Cortex-A8), mais sur Pandaboard, il rest sur l’instruction MIATT si j’utilise __NR_sigaction: après le gestionnaire de signal, le code ne saute pas la même instruction.

De l’ man sigaction ( lien ) je cite:

L’appel système Linux d’origine s’appelait sigaction (). Cependant, avec l’ajout de signaux en temps réel sous Linux 2.2, le type sigset_t de taille fixe, 32 bits, pris en charge par cet appel système, ne convenait plus. Par conséquent, un nouvel appel système, rt_sigaction (), a été ajouté pour prendre en charge un type sigset_t élargi. Le nouvel appel système prend un quasortingème argument, size_t sigsetsize, qui spécifie la taille en octets des ensembles de signaux dans act.sa_mask et oldact.sa_mask.

Je n’ai pas de réponse définitive, mais j’essaierai quand même de consortingbuer:

En regardant les sources du kernel:

  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 

contre.

 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} 

La différence, à mon avis, est que rt_sigaction copie l’intégralité de la structure sigaction, alors que sigaction reçoit et modifie la mémoire en ligne (en utilisant les fonctions utilisateur get / set) … Je ne suis pas sûr, mais cela prend plus de temps la mémoire de l’espace utilisateur directement plutôt que de travailler avec une copie temporaire.