“EXC_BAD_ACCESS” vs “Erreur de segmentation”. Les deux sont-ils pratiquement pareils?

Dans mes premières applications factices (pour EXC_BAD_ACCESS tout en apprenant), j’ai rencontré beaucoup d’ EXC_BAD_ACCESS , qui m’a en quelque sorte appris que Bad-Access est: vous touchez / accédez à un object que vous ne devriez pas car il n’est pas encore alloué ou désalloué ou tout simplement vous n’êtes pas autorisé à y accéder.

Regardez cet exemple de code qui pose un problème d’access incorrect, car j’essaie de modifier un const :

 -(void)myStartMethod{ NSSsortingng *str = @"testing"; const char *charStr = [str UTF8Ssortingng]; charStr[4] = '\0'; // bad access on this line. NSLog(@"%s",charStr); } 

Alors que l’erreur de segmentation indique : L’erreur de segmentation est un type d’erreur spécifique causé par l’access à une mémoire qui “ne vous appartient pas”. Il s’agit d’un mécanisme d’assistance qui vous empêche de corrompre la mémoire et d’introduire des bogues mémoires difficiles à déboguer. Chaque fois que vous obtenez une erreur de segmentation, vous savez que vous faites quelque chose de mal avec la mémoire (plus de description ici .

Je veux savoir deux choses. Premièrement , ai-je raison pour objective-C's EXC_BAD_ACCESS ? Est-ce que je comprends bien?

Deuxièmement , EXC_BAD_ACCESS and Segmentation fault même chose et Apple vient d’improviser son nom?

Non, EXC_BAD_ACCESS n’est pas identique à SIGSEGV .

EXC_BAD_ACCESS est une exception Mach (une combinaison de Mach et de xnu compose le kernel Mac OS X), tandis que SIGSEGV est un signal POSIX. Lorsque des accidents se produisent avec la cause indiquée sous la forme EXC_BAD_ACCESS , le signal est souvent indiqué entre parenthèses immédiatement après: Par exemple, EXC_BAD_ACCESS(SIGSEGV) . Cependant, il existe un autre signal POSIX visible avec EXC_BAD_ACCESS : il s’agit de SIGBUS , signalé par EXC_BAD_ACCESS(SIGBUS) .

SIGSEGV plus souvent lors de la lecture / écriture d’une adresse qui n’est pas du tout mappée dans la mappe de mémoire, comme le pointeur NULL , ou lors d’une tentative d’écriture dans un emplacement de mémoire en lecture seule (comme dans l’exemple ci-dessus). SIGBUS en revanche, est visible même pour les adresses SIGBUS le processus a un access légitime. Par exemple, SIGBUS peut SIGBUS à une adresse mémoire non alignée un processus qui ose charger / stocker de / vers une adresse alignée non alignée, ou un processus qui tente d’écrire sur une page pour laquelle il n’a pas le niveau de privilège requirejs. .

Ainsi, EXC_BAD_ACCESS peut être mieux compris comme étant l’ensemble des deux SIGSEGV et SIGBUS , et fait référence à tous les moyens d’accéder de manière incorrecte à la mémoire (que cette mémoire n’existe pas ou existe mais qu’elle soit mal alignée, privilégiée ou non), d’où son nom: exception – mauvais access .

Pour régaler vos yeux, voici le code, dans le code source du kernel xnu-1504.15.3 (Mac OS X 10.6.8 build 10K459) , fichier bsd/uxkern/ux_exception.c commençant à la ligne 429 , qui traduit EXC_BAD_ACCESS en SIGSEGV ou SIGBUS .

 /* * ux_exception translates a mach exception, code and subcode to * a signal and u.u_code. Calls machine_exception (machine dependent) * to attempt translation first. */ static void ux_exception( int exception, mach_exception_code_t code, mach_exception_subcode_t subcode, int *ux_signal, mach_exception_code_t *ux_code) { /* * Try machine-dependent translation first. */ if (machine_exception(exception, code, subcode, ux_signal, ux_code)) return; switch(exception) { case EXC_BAD_ACCESS: if (code == KERN_INVALID_ADDRESS) *ux_signal = SIGSEGV; else *ux_signal = SIGBUS; break; case EXC_BAD_INSTRUCTION: *ux_signal = SIGILL; break; ... 

Modifier en relation avec une autre de vos questions

Veuillez noter que l’ exception ici ne fait pas référence à une exception au niveau de la langue, du type que l’on peut intercepter avec du sucre syntaxique, comme try{} catch{} . L’exception ici fait référence aux actions d’un processeur lorsqu’il rencontre certains types d’erreurs dans votre programme (elles peuvent être fatales ou non), comme une déréférence null-pointeur, nécessitant une intervention extérieure.

Lorsque cela se produit, la CPU soulève ce que l’on appelle communément une exception ou une interruption . Cela signifie que le processeur enregistre ce qu’il faisait (le contexte ) et gère la situation exceptionnelle.

Pour faire face à une telle situation exceptionnelle, la CPU ne commence à exécuter aucun code de “gestion des exceptions” ( catch blocks ou autres) dans votre application. Il donne d’abord le contrôle du système d’exploitation en commençant à exécuter un morceau de code fourni par le kernel, appelé routine de service d’interruption . Il s’agit d’un code qui explique ce qui est arrivé à quel processus et comment y remédier. Le système d’exploitation a ainsi l’occasion de juger de la situation et de prendre les mesures qu’il souhaite.

L’action qu’il effectue pour un access mémoire non valide (tel qu’un déréférencement de pointeur nul) consiste à signaler le processus coupable avec EXC_BAD_ACCESS(SIGSEGV) . L’action qu’il effectue pour un access mémoire mal aligné est de signaler le processus coupable avec EXC_BAD_ACCESS(SIGBUS) . Il existe de nombreuses autres situations exceptionnelles et actions correspondantes, qui ne comportent pas toutes des signaux.

Nous sums maintenant de retour dans le contexte de votre programme. Si votre programme reçoit les signaux SIGSEGV ou SIGBUS , il appellera le gestionnaire de signaux qui a été installé pour ce signal, ou le gestionnaire par défaut s’il n’en avait aucun. Il est rare que des personnes installent des gestionnaires personnalisés pour SIGSEGV et SIGBUS gestionnaires par défaut arrêtent votre programme. Vous obtenez donc généralement la fermeture de votre programme.

Ce type d’exceptions est donc totalement différent du type throw dans try{} -blocks et catch{} es. Ces exceptions sont traitées uniquement dans l’application, sans impliquer le système d’exploitation. Ici, ce qui se passe, c’est qu’une déclaration de throw est simplement un saut glorifié au bloc d’ catch plus interne qui gère cette exception. Lorsque l’exception bouillonne à travers la stack, elle décompresse la stack derrière elle, en exécutant des destructeurs et autres, selon les besoins.

En gros oui, effectivement, un EXC_BAD_ACCESS est généralement associé à un SIGSEGV, qui est un signal qui avertit de l’échec de la segmentation.

Un échec de segmentation se produit lorsque vous utilisez un pointeur pointant vers des données non valides (peut-être n’appartenant pas au processus, peut-être en lecture seule, peut-être une adresse non valide en général).

Ne pensez pas à l’erreur de segmentation en termes “d’access à un object”, vous accédez à un emplacement de mémoire, donc à une adresse. Cette adresse doit être considérée comme cohérente par le système de protection de la mémoire du système d’exploitation.

Le gestionnaire de mémoire ne peut pas suivre toutes les erreurs liées à l’access à des données non valides; pensez à un pointeur sur une variable allouée à une stack, qui est considérée comme valide même si son contenu n’est plus valide lors de la restauration du cadre de stack.