47 votes

(x | y) - y pourquoi ne pas être simplement x ou même `x | 0` ?

Je lisais le code d'un noyau, et à un endroit, j'ai vu une expression à l'intérieur de if comme

if (value == (SPINLOCK_SHARED | 1) - 1) {
         ............
}

SPINLOCK_SHARED = 0x80000000 est une constante prédéfinie.

Je me demande pourquoi nous avons besoin (SPINLOCK_SHARED | 1) - 1 - Le résultat de l'expression serait 80000000 - la même chose que 0x80000000, n'est-ce pas ? Mais pourquoi ORing 1 et Subtracting 1 ont-ils de l'importance ?

J'ai l'impression qu'il me manque quelque chose

0 votes

Il y a quelque chose qui n'est pas clair pour moi : est-ce que SPINLOCK_SHARED une variable ou une constante définie par #define ?

3 votes

define SPINLOCK_SHARED 0x80000000

1 votes

Je pense qu'il n'y a pas de raison. Il s'agit peut-être d'un problème de copier-coller. Pourriez-vous ajouter où vous avez trouvé cela exactement (quelle version de quel noyau, quel fichier, etc.).

31voto

Sander De Dycker Points 6074

Le code se trouve dans _spin_lock_contested qui est appelé à partir de _spin_lock_quick lorsque quelqu'un d'autre tente d'obtenir la serrure :

count = atomic_fetchadd_int(&spin->counta, 1);
if (__predict_false(count != 0)) {
    _spin_lock_contested(spin, ident, count);
}

S'il n'y a pas de concours, alors count (la valeur précédente) devrait être 0 mais ce n'est pas le cas. Ce n'est pas le cas. count est transmise en tant que paramètre à _spin_lock_contested en tant que value paramètre. Ce paramètre value est ensuite vérifié avec le if de l'OP :

/*
 * WARNING! Caller has already incremented the lock.  We must
 *      increment the count value (from the inline's fetch-add)
 *      to match.
 *
 * Handle the degenerate case where the spinlock is flagged SHARED
 * with only our reference.  We can convert it to EXCLUSIVE.
 */
if (value == (SPINLOCK_SHARED | 1) - 1) {
    if (atomic_cmpset_int(&spin->counta, SPINLOCK_SHARED | 1, 1))
        return;
}

En gardant à l'esprit que value est la valeur précédente de spin->counta et que ce dernier a déjà été incrémenté de 1, on s'attend à ce que spin->counta à égalité value + 1 (à moins que quelque chose n'ait changé entre-temps).

Il s'agit donc de vérifier si spin->counta == SPINLOCK_SHARED | 1 (la condition préalable de la atomic_cmpset_int ) correspond à vérifier si value + 1 == SPINLOCK_SHARED | 1 qui peut être réécrite comme suit value == (SPINLOCK_SHARED | 1) - 1 (encore une fois, si rien n'a changé entre-temps).

Tandis que value == (SPINLOCK_SHARED | 1) - 1 pourrait être réécrite comme suit value == SPINLOCK_SHARED Il est donc laissé tel quel, afin de clarifier l'intention de la comparaison (c'est-à-dire comparer la valeur précédente incrémentée avec la valeur test).

La réponse semble être : pour la clarté et la cohérence du code.

10voto

Je pense que l'objectif est probablement d'ignorer le bit de poids faible :

  • Si SPINLOCK_SHARED exprimé en binaire est xxx0 -> le résultat est xxx0
  • Si SPINLOCK_SHARED = xxx1 -> le résultat est aussi xxx0

Il aurait peut-être été plus clair d'utiliser une expression de masque de bits ?

8 votes

C'est ce que fait le code, mais la question est la suivante pourquoi Vous feriez cela pour une constante définie dont le bit le moins significatif n'est pas défini ?

4 votes

@SanderDeDycker Parce que le noyau Linux ?

9 votes

@Lundin Le noyau linux n'est pas exempt de pratiques de codage compréhensibles. Bien au contraire.

4voto

Bob Jarvis Points 14906

L'effet de la

(SPINLOCK_SHARED | 1) - 1

est de s'assurer que le bit de poids faible du résultat est effacé avant de procéder à la comparaison avec value . Je suis d'accord que cela semble plutôt inutile, mais apparemment le bit de poids faible a une utilisation ou une signification particulière qui n'est pas apparente dans ce code, et je pense que nous devons supposer que les développeurs avaient une bonne raison de le faire. Une question intéressante serait de savoir si ce même schéma ( | 1) -1 ) utilisés dans la base de code que vous examinez ?

2voto

Lundin Points 21616

Il s'agit d'une façon détournée d'écrire un masque de bits. Version lisible : value == (SPINLOCK_SHARED & ~1u) .

5 votes

Oui, mais pourquoi . Le PO demande pourquoi ce serait le cas si SPINLOCK_SHARED est une constante connue. S'il s'agit simplement de tester SPINLOCK_SHARED être présent dans un masque, pourquoi pas if (value & SPINLOCK_SHARED) ?

0 votes

C'est vrai, c'est ma question.

4 votes

value == (SPINLOCK_SHARED & ~1u) n'est pas équivalent car value == (SPINLOCK_SHARED | 1) - 1 fonctionne même si le type de SPINLOCK_SHARED est plus large que unsigned .

1voto

Matthew Dillon Points 51

Cela a été fait dans un souci de clarté, c'est tout. C'est parce que atomic_fetchadd_int() (dans par exemple sys/spinlock2.h) renvoie la valeur AVANT l'addition/soustraction, et cette valeur est transmise à _spin_lock_contested().

Notez que le compilateur C précalcule complètement toutes les expressions constantes. En fait, le compilateur peut même optimiser le code intégré basé sur des conditionnelles qui utilisent des arguments de procédure passés lorsque les procédures reçoivent des constantes dans ces arguments. C'est la raison pour laquelle le code inline lockmgr() dans sys/lock.h a une déclaration de cas.... parce que cette déclaration de cas entière sera optimisée et se transformera en un appel direct à la fonction appropriée.

En outre, dans toutes ces fonctions de verrouillage, l'overhead des opérations atomiques éclipse tous les autres calculs de deux ou trois ordres de grandeur.

-Matt

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X