Forum Programmation.shell accès concurrentiel aux fichiers

Posté par  . Licence CC By‑SA.
Étiquettes :
2
9
mar.
2023

Bonjour,

j'ai écrit 2 scripts :
- le premier réalise un : echo $1 >> /tmp/fichier_commun.log
- le deuxième réalise un : mv /tmp/fichier_commun.log > /tmp/fichier2.log

Les 2 scripts peuvent être exécutés en même temps par des applications différentes.(les utilisateurs peuvent différents aussi)

Je prétends qu'il ne peut y avoir de problème, mon collègue le contraire.

Pouvez-vous arbitrer ? svp

merci d'avance.

  • # flock

    Posté par  (site web personnel, Mastodon) . Évalué à 6. Dernière modification le 09 mars 2023 à 20:39.

    Dans le doute, tu peux utiliser flock pour n'autoriser qu'une instance.

    exec 9>/path/to/lock/file
    if ! flock -n 9; then
        printf 'another instance is running\n';
        exit 1
    fi

    (dans les 2 scripts, le même lock bien sur)

    On ne peut pas mettre d'array dans le string...

  • # Tu as raison

    Posté par  . Évalué à 8.

    Tu as raison, car le second script qui fait :

    mv /tmp/fichier_commun.log > /tmp/fichier2.log
    

    donnera seulement un message d'erreur de syntaxe sur la commande mv. Ainsi, /tmp/fichier_commun.log sera préservé.

    • [^] # Re: Tu as raison

      Posté par  (site web personnel, Mastodon) . Évalué à -1.

      C'est incorrect. Le mv n'aura aucun problème, même si quelqu'un écrit dedans au même moment.

      Quand un programme a ouvert un fichier, il se fiche de savoir s'il est déplacé, même supprimé. Il ne retient que le file descriptor, qui n'est qu'un identifiant d'inode (pour faire simple) et peu importe si le fichier est déplacé ou supprimé.

      Un gentil du net

      • [^] # Re: Tu as raison

        Posté par  (Mastodon) . Évalué à 4.

        C'est incorrect. Le mv n'aura aucun problème, même si quelqu'un écrit dedans au même moment.

        tth@redlady:~/A$ ls -l
        total 4
        -rw-r--r-- 1 tth tth 29 Mar 10 05:52 foo.txt
        tth@redlady:~/A$ mv foo.txt > quux.txt
        mv: missing destination file operand after 'foo.txt'
        Try 'mv --help' for more information.
        tth@redlady:~/A$ ls -l
        total 4
        -rw-r--r-- 1 tth tth 29 Mar 10 05:52 foo.txt
        -rw-r--r-- 1 tth tth  0 Mar 10 05:52 quux.txt
        tth@redlady:~/A$
        

        Là, le mv il dit qu'il a plus de genoux…

        Quand un programme a ouvert un fichier, il se fiche de savoir s'il est déplacé, même supprimé. Il ne retient que le file descriptor, qui n'est qu'un identifiant d'inode (pour faire simple) et peu importe si le fichier est déplacé ou supprimé.

        Ce qui est valable quand les fichiers concernés (source+copie) sont sur le même filesystem, l'opération mv étant dans ce cas atomique.

        Par contre, si il y a changement de fs, j'ai quand même un doute sur le déroulement des opérations…

        • [^] # Re: Tu as raison

          Posté par  . Évalué à 5.

          Je me trompe peut-être, mais la syntaxe de la commande mv est mv source destination. Donc, pas de > entre les deux. Sinon, la commande mv a un seul argument, et ta sortie standard est dupliqué dans ton deuxième fichier. Voulais-tu dire mv /tmp/fichier_commun.log /tmp/fichier2.log ou cat /tmp/fichier_commun.log > /tmp/fichier2.log ?

          L'informatique n'est pas une science exacte, on n'est jamais à l'abri d'un succès

      • [^] # Re: Tu as raison

        Posté par  . Évalué à 6. Dernière modification le 10 mars 2023 à 19:51.

        Quand un programme a ouvert un fichier, il se fiche de savoir s'il est déplacé, même supprimé. Il ne retient que le file descriptor, qui n'est qu'un identifiant d'inode (pour faire simple) et peu importe si le fichier est déplacé ou supprimé.

        Farpaitement ! Jusqu'au moment où tous les fd qui pointent vers l'emplacement sont fermés.

        Petite démo rigolote :

        ~$ cat /dev/random > /tmp/salut &                          <-- on met des trucs dans un fichier
        [1] 9906
        ~$ rm /tmp/salut                                           <-- on efface le fichier                     
        ~$ for i in `seq 1 30`; do df -h /tmp ; sleep 1 ; done     <-- on regarde l'espace disque
        Filesystem      Size  Used Avail Use% Mounted on
        tmpfs           3.9G  804M  3.1G  21% /tmp
        Filesystem      Size  Used Avail Use% Mounted on
        tmpfs           3.9G  1.1G  2.8G  28% /tmp                 <-- ça se remplit, pourtant
        Filesystem      Size  Used Avail Use% Mounted on               le fichier /tmp/salut n'existe
        tmpfs           3.9G  1.4G  2.6G  35% /tmp                     pas !
        Filesystem      Size  Used Avail Use% Mounted on
        tmpfs           3.9G  1.6G  2.3G  41% /tmp                     Mais le process cat y a accès
        Filesystem      Size  Used Avail Use% Mounted on               via un descripteur de fichier :)
        tmpfs           3.9G  1.9G  2.1G  48% /tmp
        Filesystem      Size  Used Avail Use% Mounted on
        tmpfs           3.9G  2.1G  1.8G  55% /tmp
        Filesystem      Size  Used Avail Use% Mounted on
        tmpfs           3.9G  2.4G  1.5G  62% /tmp
        Filesystem      Size  Used Avail Use% Mounted on
        tmpfs           3.9G  2.7G  1.3G  69% /tmp
        Filesystem      Size  Used Avail Use% Mounted on
        tmpfs           3.9G  2.9G  987M  75% /tmp
        Filesystem      Size  Used Avail Use% Mounted on
        tmpfs           3.9G  3.2G  719M  82% /tmp
        Filesystem      Size  Used Avail Use% Mounted on
        tmpfs           3.9G  3.4G  452M  89% /tmp
        Filesystem      Size  Used Avail Use% Mounted on
        tmpfs           3.9G  3.7G  186M  96% /tmp                   <-- ça chauffe !
        cat: write error: No space left on device
        Filesystem      Size  Used Avail Use% Mounted on
        tmpfs           3.9G  3.9G     0 100% /tmp
        [1]+  Exit 1                  cat /dev/random > /tmp/salut   <-- cat stoppe
        Filesystem      Size  Used Avail Use% Mounted on             <-- et hop plein de place
        tmpfs           3.9G  4.0K  3.9G   1% /tmp                       de nouveau
        Filesystem      Size  Used Avail Use% Mounted on
        tmpfs           3.9G  4.0K  3.9G   1% /tmp
        Filesystem      Size  Used Avail Use% Mounted on
        tmpfs           3.9G  4.0K  3.9G   1% /tmp
        

        On peut utiliser lsof pour voir le fd et l'inode :

        ~$ cat /dev/random > /tmp/salut &
        ~$ rm /tmp/salut
        [1] 10002
        ~$ lsof -p 10002
        COMMAND   PID    USER   FD   TYPE DEVICE   SIZE/OFF    NODE NAME
        cat     10002 charles  cwd    DIR    8,2      12288 6033250 /home/charles
        cat     10002 charles  rtd    DIR    8,2       4096       2 /
        cat     10002 charles  txt    REG    8,2      34816 5900785 /usr/bin/cat
        cat     10002 charles  mem    REG    8,2    3719360 5945566 /usr/lib/locale/locale-archive
        cat     10002 charles  mem    REG    8,2    1953112 5901580 /usr/lib/libc.so.6
        cat     10002 charles  mem    REG    8,2     216192 5901565 /usr/lib/ld-linux-x86-64.so.2
        cat     10002 charles    0u   CHR  136,1        0t0       4 /dev/pts/1
        cat     10002 charles    1w   REG   0,35 2362441728    4127 /tmp/salut (deleted)
        cat     10002 charles    2u   CHR  136,1        0t0       4 /dev/pts/1
        cat     10002 charles    3r   CHR    1,8        0t0       8 /dev/random
        

        Bon au départ je voulais juste faire un peu d'humour avec mon commentaire débile hein :).

  • # Il peut y avoir un problème ...

    Posté par  . Évalué à 4. Dernière modification le 09 mars 2023 à 21:21.

    Les 2 scripts peuvent être exécutés en même temps par des applications différentes.(les utilisateurs peuvent différents aussi)

    Veux-tu dire que script 1 peut être exécuté par plusieurs
    utilisateurs en même temps, et script 2 aussi ?

    Pour utilisation simultanée de une ou plusieurs instances de script1 et une instance de script2 il n'y a pas de problème.

    Pour utilisation simultanée de plusieurs instances de script2 …. supposons :
    - un utilisateur execute script 1
    - deux utilisateurs executent script2, en déplaçant le fichier commun vers le fichier 2.
    - entre temps un utilisateur exécute script1. L'enchainement donne, mais dans un temps très court :
    * exécution de script2 (déplacement du fichier commun vers fichier2)
    * exécution de script1 (regénération du fichier commun
    * exécution de script2 (déplacement de fichier commun vers fichier 2)

    Tu risques d'écraser fichier2. Il faut donc faire attention au nommage de fichier2 dans ton script. C'est surtout cette partie qui pourrait poser problème.

    Maintenant tout dépend de ta façon de nommer script2.

    • [^] # Re: Il peut y avoir un problème ...

      Posté par  (site web personnel) . Évalué à 2. Dernière modification le 09 mars 2023 à 22:04.

      Je vois deux autres problèmes potentiels.

      Si le premier programme ouvre le fichier plusieurs fois, ses écritures peuvent être réparties dans plusieurs fichiers. Ça peut être le cas par exemple s'il s'agit d'un script shell qui a plusieurs lignes comme ceci :

      echo foo >> fichier_commun.log
      echo bar >> fichier_commun.log

      Si le fichier est renommé entre les deux, les deux lignes seront dans des fichiers différents.

      D'autre part, ce qui va lire fichier2.log devrait avoir un moyen de savoir si les écritures dans ce fichier sont terminées. Sinon on risque de se retrouver dans une situation ou on lit fichier2.log jusqu'à la fin alors qu'il n'est pas fini.

      Selon le cas d'utilisation exact, ces deux situations peuvent poser problème ou non.

      Une solution que j'aime bien est de tout donner à rotatelogs et de lui laisser gérer les fichiers : https://httpd.apache.org/docs/2.4/programs/rotatelogs.html
      J'ai aussi écrit un patch pour ajouter un mode de tampon circulaire mais je sais pas s'il fonctionne toujours : https://bz.apache.org/bugzilla/show_bug.cgi?id=47170

      pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

    • [^] # Re: Il peut y avoir un problème ...

      Posté par  . Évalué à 1.

      Bonjour,

      il y a 2 applications.
      l'app1 run script1
      l'app2 run script2

      il n'y aura qu'une app1 avec 1 user qui run script1.
      mais il faut effectivement que je vérifier que l'app2 ne peux pas être lancé par 2 users.

      merci beaucoup à tous pour votre participation.

      • [^] # Re: Il peut y avoir un problème ...

        Posté par  . Évalué à 4.

        mais il faut effectivement que je vérifier que l'app2 ne peux pas être lancé par 2 users.

        dans ton app qui ne doit etre lancée que par un seul utilisateur ajouter simplement laa detection de /var/lock/tonappli.lock ou /var/run/tonappli.pid

        ces fichiers sont créés à l'ouverture de ton script/appli
        s'ils existent c'est que l'appli tourne deja, et tu quittes
        s'ils n'existent pas, alors tu executes l'appli

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.