Comment allouer atomiquement un périphérique en boucle?

11

J'écris des scripts shell pour gérer certains trucs d'image disque, et j'ai besoin d'utiliser des périphériques de boucle pour accéder à certaines images disque. Cependant, je ne sais pas comment allouer correctement un périphérique de boucle sans exposer mon programme à une condition de concurrence critique.

Je sais que je peux utiliser losetup -fpour obtenir le prochain périphérique de boucle non alloué, puis allouer ce périphérique de boucle comme ceci:

ld=$(losetup -f)
sudo losetup $ld myfile.img
dostuffwith $ld

Cependant, dans le cas où je veux exécuter plusieurs instances du programme en même temps, c'est presque un exemple classique d'une condition de concurrence critique, et cela me dérange beaucoup. Si plusieurs instances de ce programme étaient en cours d'exécution ou que d'autres programmes essayaient également d'obtenir un périphérique de boucle, chaque processus pourrait ne pas être en mesure d'allouer le périphérique de boucle avant le prochain appel losetup -f, auquel cas les deux processus penseraient que la même boucle est disponible, mais un seul peut l'obtenir.

Je pourrais utiliser la synchronisation externe pour cela, mais je voudrais (si possible) éviter une complexité supplémentaire. De plus, d'autres programmes qui utilisent des périphériques de boucle ne respecteraient probablement pas la synchronisation que je pourrais proposer.

Comment puis-je éviter cette condition de course potentielle? Idéalement, j'aimerais pouvoir découvrir et lier atomiquement le périphérique de boucle, par exemple avec une commande comme:

ld=$(sudo losetup -f myfile.img)
dostuffwith $ld

Cependant, lorsque je fais cela, il $ldn'est pas attribué au chemin du périphérique de boucle, et le déplacement sudo, comme dans, sudo ld=$(losetup -f myfile.img)donne des erreurs d'autorisation.

AJMansfield
la source

Réponses:

13

Il s'agit d'un problème classique dans la concurrence: lors de l'allocation d'une ressource, vous devez déterminer atomiquement que la ressource est gratuite et la réserver, sinon un autre processus pourrait réserver la ressource entre le moment où vous vérifiez qu'elle est gratuite et le moment où vous la réservez.

Utilisez losetuple mode d'allocation automatique de ( -f) et passez l' --showoption pour lui faire imprimer le chemin du périphérique de boucle.

ld=$(sudo losetup --show -f /tmp/1m)

Cette option est présente dans util-linux depuis la version 2.13 ( initialement ajoutée en tant que-s , mais --showa été prise en charge dans toutes les versions publiées et les versions récentes ont supprimé le -snom de l' option). Malheureusement, la version BusyBox ne l'a pas.

La version 3.1 du noyau Linux a introduit une méthode pour effectuer l'opération d'allocation de périphérique de boucle directement dans le noyau, via le nouveau /dev/loop-controlpériphérique. Cette méthode n'est prise en charge que depuis util-linux 2.21. Avec le noyau <3.1 ou util-linux <2.21, le losetupprogramme énumère les entrées de périphérique de boucle pour en réserver une. Cependant, je ne vois pas de condition de concurrence dans le code; il doit être sûr, mais il peut avoir une petite fenêtre pendant laquelle il signalera à tort que tous les appareils sont alloués même si ce n'est pas le cas.

Gilles 'SO- arrête d'être méchant'
la source
À quoi ça </dev/ttysert?
Stéphane Chazelas
1
La dernière fois que j'ai essayé, même des losetup --find --showcourses. for i in {1..100}; do losetup -f -s $i & donene m'a pas donné 100 appareils en boucle. Les périphériques en boucle sont assez rares pour que cela n'ait pas d'importance normalement; si c'est le cas, votre seule option est de créer vos propres verrous et / ou de vérifier que le bon périphérique de boucle a été créé après coup.
frostschutz
@frostschutz losetuppeut échouer (par exemple parce que vous n'avez plus d'entrées de boucle), mais s'il signale un nom de périphérique, c'est le périphérique qu'il a correctement alloué. Les versions antérieures comportaient-elles un bogue qui provoquait l'écriture d'un nom de périphérique même si l'allocation avait échoué? Je vois dans le code source que l'interface pour l'allocation dans le noyau n'existe que depuis le noyau 3.1, est-ce peut-être un bogue avec l'ancienne interface qui nécessite l' losetuputilitaire pour faire la recherche?
Gilles 'SO- arrête d'être méchant'
Oh, il semble en effet être corrigé dans les versions plus récentes. util-linux-2.26.2 semble fonctionner, util-linux-2.24.1 s'imprime à plusieurs reprises /dev/loop14et ainsi et le périphérique peut être complètement absent à la fin. Peut-être que le correctif est une courtoisie de /dev/loop-control, il regardait juste /proc/partitions...
frostschutz
@frostschutz Depuis util-linux 2.21, losetuputilise /dev/loop-controls'il est présent, et cela ne semble pas avoir une condition de concurrence critique : l'allocation se fait dans le noyau et l'impression du chemin du périphérique est la dernière chose que l'utilitaire fait.
Gilles 'SO- arrête d'être méchant'
6

Je l'ai compris. Bien que je ne sois pas sûr de la nature du problème avec l'autorisation, je peux plutôt tirer en premier et demander plus tard comme ceci:

sudo losetup -f myfile.img
ld=$(losetup -j myfile.img | grep -o "/dev/loop[0-9]*")
dostuffwith $ld
AJMansfield
la source
2

Vous pouvez utiliser flock:

  tryagain=1
  while [[ $tryagain -ne 0 ]]; do
    ld=`losetup -f`
    flock -n $ld -c "losetup $ld myfile.img"
    tryagain=$?
  done

L'idée ici est d'essayer flockle fichier de périphérique de boucle; si une autre instance du même script l'acquiert en premier, elle va appeler losetup $ld myfile.imget flockretournera 0. Pour le script qui perd la course, losetupne sera pas appelé et flockrenverra 1, provoquant la répétition de la boucle.

Pour plus voir man flock.

boucle d'or
la source
0

Si tout ce que vous voulez faire avec l'image en tant que périphérique de bouclage est de la monter en tant que système de fichiers et de travailler avec le contenu, la mountcommande peut s'en occuper automatiquement.

mount -o loop myfile.img /tmp/mountpoint
Aléatoire832
la source
En fait, dans mon cas, je veux en particulier qu'il ne soit pas monté - je l'utilise comme périphérique bloc et non comme système de fichiers.
AJMansfield
@AJMansfield À part le monter, que pouvez-vous faire avec un périphérique bloc que vous ne pouvez pas faire avec un fichier?
Random832
Vous ne pouvez pas le formater comme espace d'échange sur une configuration btrfs à disque complet. J'avais l'intention d'utiliser le fichier pour l'espace d'échange, car btrfs ne prend pas en charge les opérations nécessaires pour les fichiers d'échange, et je ne peux pas avoir une vraie partition d'échange avec une configuration btrfs à disque complet.
AJMansfield