Descripteur de fichier sous Linux avec exemples

Un jour, lors d'un entretien, on m'a demandé : que ferez-vous si vous trouvez un service qui ne fonctionne pas parce que le disque manque d'espace ?

Bien sûr, j'ai répondu que je verrais ce qui était occupé par cet endroit et, si possible, je nettoierais les lieux.
Ensuite, l’intervieweur a demandé : que se passe-t-il s’il n’y a pas d’espace libre sur la partition, mais que vous ne voyez pas non plus de fichiers qui occuperaient tout l’espace ?

A cela j'ai dit que vous pouvez toujours regarder les descripteurs de fichiers ouverts, par exemple avec la commande lsof, et comprendre quelle application a occupé tout l'espace disponible, et ensuite vous pouvez agir en fonction des circonstances, selon que les données sont nécessaires ou non. .

L’intervieweur m’a interrompu sur le dernier mot, ajoutant à sa question : « Supposons que nous n’ayons pas besoin des données, c’est juste un journal de débogage, mais l’application ne fonctionne pas car elle ne peut pas écrire de débogage » ?

"D'accord", ai-je répondu, "nous pouvons désactiver le débogage dans la configuration de l'application et la redémarrer."
L’intervieweur a objecté : « Non, nous ne pouvons pas redémarrer l’application, nous avons encore des données importantes stockées en mémoire et des clients importants sont connectés au service lui-même, que nous ne pouvons pas forcer à se reconnecter. »

"d'accord", ai-je dit, "si nous ne pouvons pas redémarrer l'application et que les données ne sont pas importantes pour nous, alors nous pouvons simplement effacer ce fichier ouvert via le descripteur de fichier, même si nous ne le voyons pas dans la commande ls sur le système de fichiers.

L’intervieweur était content, mais pas moi.

Puis je me suis demandé : pourquoi la personne qui teste mes connaissances n’approfondit-elle pas ? Mais que se passe-t-il si les données sont importantes après tout ? Que se passe-t-il si nous ne pouvons pas redémarrer un processus et que le processus écrit sur le système de fichiers sur une partition qui n'a pas d'espace libre ? Et si nous ne pouvions pas perdre non seulement les données déjà écrites, mais également les données que ce processus écrit ou tente d'écrire ?

Tuzik

Au début de ma carrière, j'ai essayé de créer une petite application nécessaire pour stocker les informations des utilisateurs. Et puis j'ai pensé : comment puis-je faire correspondre l'utilisateur à ses données. Par exemple, j'ai Ivanov Ivan Ivanovitch, et il a des informations, mais comment puis-je me lier d'amitié avec eux ? Je peux souligner directement que le chien nommé « Tuzik » appartient à cet Ivan même. Mais que se passerait-il s'il changeait de nom et devenait, par exemple, Olya à la place d'Ivan ? Ensuite, il s'avérera que notre Olya Ivanovna Ivanova n'aura plus de chien et que notre Tuzik appartiendra toujours à Ivan qui n'existe pas. Une base de données qui donnait à chaque utilisateur un identifiant (ID) unique a aidé à résoudre ce problème, et mon Tuzik était lié à cet identifiant, qui, en fait, n'était qu'un numéro de série. Ainsi, le propriétaire de l'as avait le numéro d'identification 2, et à un moment donné, Ivan était sous cette pièce d'identité, puis Olya est devenue sous la même pièce d'identité. Le problème de l’humanité et de l’élevage était pratiquement résolu.

Descripteur de fichier

Le problème du fichier et du programme qui fonctionne avec ce fichier est à peu près le même que celui de notre chien et de notre homme. Supposons que j'ai ouvert un fichier appelé ivan.txt et que j'ai commencé à y écrire le mot tuzik, mais que je n'ai réussi à écrire que la première lettre « t » dans le fichier, et que ce fichier a été renommé par quelqu'un, par exemple, en olya.txt. Mais le fichier reste le même et je souhaite toujours y enregistrer mon as. Chaque fois qu'un fichier est ouvert par un appel système ouvert dans n'importe quel langage de programmation, je reçois un identifiant unique qui me dirige vers un fichier, cet identifiant est le descripteur de fichier. Et peu importe quoi et qui fera ensuite ce fichier, il peut être supprimé, il peut être renommé, le propriétaire peut être modifié ou les droits de lecture et d'écriture peuvent être supprimés, j'aurai toujours accès car au moment de l'ouverture du fichier, j'avais les droits de lecture et/ou d'écriture et j'ai réussi à commencer à travailler avec, ce qui signifie que je dois continuer à le faire.

Sous Linux, la bibliothèque libc ouvre 3 fichiers descripteurs pour chaque application (processus) en cours d'exécution, numérotés 0,1,2. Plus d'informations peuvent être trouvées sur les liens homme stdio и homme dehors

  • Le descripteur de fichier 0 est appelé STDIN et est associé à l'entrée de l'application
  • Le descripteur de fichier 1 est appelé STDOUT et est utilisé par les applications pour générer des données, telles que des commandes d'impression.
  • Le descripteur de fichier 2 s'appelle STDERR et est utilisé par les applications pour afficher des messages d'erreur.

Si dans votre programme vous ouvrez un fichier en lecture ou en écriture, vous obtiendrez très probablement le premier identifiant gratuit et ce sera le numéro 3.

La liste des descripteurs de fichiers peut être consultée pour n'importe quel processus si vous connaissez son PID.

Par exemple, ouvrons la console bash et regardons le PID de notre processus

[user@localhost ]$ echo $$
15771

Dans la deuxième console, courons

[user@localhost ]$ ls -lah /proc/15771/fd/
total 0
dr-x------ 2 user user  0 Oct  7 15:42 .
dr-xr-xr-x 9 user user  0 Oct  7 15:42 ..
lrwx------ 1 user user 64 Oct  7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 255 -> /dev/pts/21

Vous pouvez ignorer en toute sécurité le descripteur de fichier numéro 255 pour les besoins de cet article ; il a été ouvert pour ses besoins par bash lui-même, et non par la bibliothèque liée.

Désormais, les 3 fichiers descripteurs sont associés au pseudo-terminal /dev/pts, mais on peut toujours les manipuler, par exemple, les exécuter dans une deuxième console

[user@localhost ]$ echo "hello world" > /proc/15771/fd/0

Et dans la première console nous verrons

[user@localhost ]$ hello world

Redirection et canalisation

Vous pouvez facilement remplacer ces 3 fichiers descripteurs dans n'importe quel processus, y compris dans bash, par exemple via un tube reliant deux processus, voir

[user@localhost ]$ cat /dev/zero | sleep 10000

Vous pouvez exécuter cette commande vous-même avec strace-f et voyez ce qui se passe à l'intérieur, mais je vais vous le dire brièvement.

Notre processus parent bash avec le PID 15771 analyse notre commande et comprend exactement combien de commandes nous voulons exécuter, dans notre cas, il y en a deux : cat et sleep. Bash sait qu'il doit créer deux processus enfants et les fusionner en un seul canal. Au total, bash aura besoin de 2 processus enfants et d'un tube.

Bash exécute un appel système avant de créer des processus enfants pipe et reçoit de nouveaux descripteurs de fichiers sur le tampon de canal temporaire, mais ce tampon ne connecte pas encore nos deux processus enfants.

Pour le processus parent, il semble qu’il existe déjà un canal, mais il n’y a pas encore de processus enfants :

PID    command
15771  bash
lrwx------ 1 user user 64 Oct  7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:42 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:42 255 -> /dev/pts/21

Puis en utilisant l'appel système cloner bash crée deux processus enfants, et nos trois processus ressembleront à ceci :

PID    command
15771  bash
lrwx------ 1 user user 64 Oct  7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:42 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:42 255 -> /dev/pts/21
PID    command
9004  bash
lrwx------ 1 user user 64 Oct  7 15:57 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:57 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:57 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:57 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:57 255 -> /dev/pts/21
PID    command
9005  bash
lrwx------ 1 user user 64 Oct  7 15:57 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:57 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:57 3 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:57 4 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:57 255 -> /dev/pts/21

N'oubliez pas que clone clone le processus ainsi que tous les descripteurs de fichiers, ils seront donc les mêmes dans le processus parent et dans ceux enfants. Le travail du processus parent avec le PID 15771 est de surveiller les processus enfants, il attend donc simplement une réponse des enfants.

Par conséquent, il n’a pas besoin de tube et ferme les descripteurs de fichiers numérotés 3 et 4.

Dans le premier processus child bash avec le PID 9004, l'appel système dup2, change notre descripteur de fichier STDOUT numéro 1 en un descripteur de fichier pointant vers un tube, dans notre cas c'est le numéro 3. Ainsi, tout ce que le premier processus enfant avec le PID 9004 écrit dans STDOUT finira automatiquement dans le tampon du tube.

Dans le deuxième processus enfant avec le PID 9005, bash utilise dup2 pour modifier le descripteur de fichier numéro STDIN 0. Désormais, tout ce que notre deuxième bash avec le PID 9005 lira sera lu à partir du tube.

Après cela, les descripteurs de fichiers numérotés 3 et 4 sont également fermés dans les processus enfants, car ils ne sont plus utilisés.

J'ignore délibérément le descripteur de fichier 255 ; il est utilisé à des fins internes par bash lui-même et sera également fermé dans les processus enfants.

Ensuite, dans le premier processus enfant avec le PID 9004, bash commence à utiliser un appel système exec le fichier exécutable que nous avons spécifié sur la ligne de commande, dans notre cas il s'agit de /usr/bin/cat.

Dans le deuxième processus enfant avec le PID 9005, bash exécute le deuxième exécutable que nous avons spécifié, dans notre cas /usr/bin/sleep.

L'appel système exec ne ferme pas les descripteurs de fichiers à moins qu'ils n'aient été ouverts avec l'indicateur O_CLOEXEC au moment où l'appel d'ouverture a été effectué. Dans notre cas, après avoir lancé les fichiers exécutables, tous les descripteurs de fichiers actuels seront enregistrés.

Vérifiez dans la console :

[user@localhost ]$ pgrep -P 15771
9004
9005
[user@localhost ]$ ls -lah /proc/15771/fd/
total 0
dr-x------ 2 user user  0 Oct  7 15:42 .
dr-xr-xr-x 9 user user  0 Oct  7 15:42 ..
lrwx------ 1 user user 64 Oct  7 15:42 0 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 2 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:42 255 -> /dev/pts/21
[user@localhost ]$ ls -lah /proc/9004/fd
total 0
dr-x------ 2 user user  0 Oct  7 15:57 .
dr-xr-xr-x 9 user user  0 Oct  7 15:57 ..
lrwx------ 1 user user 64 Oct  7 15:57 0 -> /dev/pts/21
l-wx------ 1 user user 64 Oct  7 15:57 1 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:57 2 -> /dev/pts/21
lr-x------ 1 user user 64 Oct  7 15:57 3 -> /dev/zero
[user@localhost ]$ ls -lah /proc/9005/fd
total 0
dr-x------ 2 user user  0 Oct  7 15:57 .
dr-xr-xr-x 9 user user  0 Oct  7 15:57 ..
lr-x------ 1 user user 64 Oct  7 15:57 0 -> pipe:[253543032]
lrwx------ 1 user user 64 Oct  7 15:57 1 -> /dev/pts/21
lrwx------ 1 user user 64 Oct  7 15:57 2 -> /dev/pts/21
[user@localhost ]$ ps -up 9004
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
user  9004  0.0  0.0 107972   620 pts/21   S+   15:57   0:00 cat /dev/zero
[user@localhost ]$ ps -up 9005
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
user  9005  0.0  0.0 107952   360 pts/21   S+   15:57   0:00 sleep 10000

Comme vous pouvez le constater, le numéro unique de notre canalisation est le même dans les deux procédés. Nous avons donc une connexion entre deux processus différents avec le même parent.

Pour ceux qui ne sont pas familiers avec les appels système utilisés par bash, je recommande fortement d'exécuter les commandes via strace et de voir ce qui se passe en interne, par exemple comme ceci :

strace -s 1024 -f bash -c "ls | grep hello"

Revenons à notre problème de manque d'espace disque et d'essayer de sauvegarder des données sans redémarrer le processus. Écrivons un petit programme qui écrira environ 1 mégaoctet par seconde sur le disque. De plus, si, pour une raison quelconque, nous ne parvenons pas à écrire des données sur le disque, nous l'ignorerons simplement et essaierons de réécrire les données dans une seconde. Dans l'exemple que j'utilise Python, vous pouvez utiliser n'importe quel autre langage de programmation.

[user@localhost ]$ cat openforwrite.py 
import datetime
import time

mystr="a"*1024*1024+"n"
with open("123.txt", "w") as f:
    while True:
        try:
            f.write(str(datetime.datetime.now()))
            f.write(mystr)
            f.flush()
            time.sleep(1)
        except:
            pass

Exécutons le programme et regardons les descripteurs de fichiers

[user@localhost ]$ python openforwrite.py &
[1] 3762
[user@localhost ]$ ps axuf | grep [o]penforwrite
user  3762  0.0  0.0 128600  5744 pts/22   S+   16:28   0:00  |   _ python openforwrite.py
[user@localhost ]$ ls -la /proc/3762/fd
total 0
dr-x------ 2 user user  0 Oct  7 16:29 .
dr-xr-xr-x 9 user user  0 Oct  7 16:29 ..
lrwx------ 1 user user 64 Oct  7 16:29 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  7 16:29 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  7 16:29 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  7 16:29 3 -> /home/user/123.txt

Comme vous pouvez le voir, nous avons nos 3 descripteurs de fichiers standards et un de plus que nous avons ouvert. Vérifions la taille du fichier :

[user@localhost ]$ ls -lah 123.txt 
-rw-rw-r-- 1 user user 117M Oct  7 16:30 123.txt

Les données sont en cours d'écriture, on essaie de changer les permissions sur le fichier :

[user@localhost ]$ sudo chown root: 123.txt
[user@localhost ]$ ls -lah 123.txt 
-rw-rw-r-- 1 root root 168M Oct  7 16:31 123.txt
[user@localhost ]$ ls -lah 123.txt 
-rw-rw-r-- 1 root root 172M Oct  7 16:31 123.txt

Nous voyons que les données sont toujours en cours d'écriture, bien que notre utilisateur ne soit pas autorisé à écrire dans le fichier. Essayons de le supprimer :

[user@localhost ]$ sudo rm 123.txt 
[user@localhost ]$ ls 123.txt
ls: cannot access 123.txt: No such file or directory

Où sont écrites les données ? Et sont-ils écrits du tout ? Nous vérifions:

[user@localhost ]$ ls -la /proc/3762/fd
total 0
dr-x------ 2 user user  0 Oct  7 16:29 .
dr-xr-xr-x 9 user user  0 Oct  7 16:29 ..
lrwx------ 1 user user 64 Oct  7 16:29 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  7 16:29 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  7 16:29 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  7 16:29 3 -> /home/user/123.txt (deleted)

Oui, notre descripteur de fichier existe toujours et nous pouvons traiter ce descripteur de fichier comme notre ancien fichier, nous pouvons le lire, l'effacer et le copier.

Regardons la taille du fichier :

[user@localhost ]$ lsof | grep 123.txt
python    31083             user    3w      REG                8,5   19923457   2621522 /home/user/123.txt

La taille du fichier est 19923457. Essayons d'effacer le fichier :

[user@localhost ]$ truncate -s 0 /proc/31083/fd/3
[user@localhost ]$ lsof | grep 123.txt
python    31083             user    3w      REG                8,5  136318390   2621522 /home/user/123.txt

Comme vous pouvez le constater, la taille du fichier ne fait qu'augmenter et notre coffre ne fonctionnait pas. Regardons la documentation sur les appels système ouvert. Si nous utilisons l'indicateur O_APPEND lors de l'ouverture d'un fichier, alors à chaque écriture, le système d'exploitation vérifie la taille du fichier et écrit les données jusqu'à la toute fin du fichier, et ce de manière atomique. Cela permet à plusieurs threads ou processus d'écrire dans le même fichier. Mais dans notre code, nous n'utilisons pas ce drapeau. Nous pouvons voir une taille de fichier différente dans lsof après trunk uniquement si nous ouvrons le fichier pour une écriture supplémentaire, ce qui signifie plutôt dans notre code

with open("123.txt", "w") as f:

nous devons mettre

with open("123.txt", "a") as f:

Vérification avec le drapeau « w »

[user@localhost ]$ strace -e trace=open python openforwrite.py 2>&1| grep 123.txt
open("123.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3

et avec le drapeau "a"

[user@localhost ]$ strace -e trace=open python openforwrite.py 2>&1| grep 123.txt
open("123.txt", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3

Programmation d'un processus déjà en cours d'exécution

Souvent, les programmeurs, lors de la création et du test de programmes, utilisent des débogueurs (par exemple GDB) ou différents niveaux de journalisation dans l'application. Linux offre la possibilité d'écrire et de modifier un programme déjà en cours d'exécution, par exemple, modifier les valeurs des variables, définir un point d'arrêt, etc., etc.

Revenant à la question initiale concernant le manque d'espace disque pour écrire un fichier, essayons de simuler le problème.

Créons un fichier pour notre partition, que nous monterons en tant que disque séparé :

[user@localhost ~]$ dd if=/dev/zero of=~/tempfile_for_article.dd bs=1M count=10
10+0 records in
10+0 records out
10485760 bytes (10 MB) copied, 0.00525929 s, 2.0 GB/s
[user@localhost ~]$

Créons un système de fichiers :

[user@localhost ~]$ mkfs.ext4 ~/tempfile_for_article.dd
mke2fs 1.42.9 (28-Dec-2013)
/home/user/tempfile_for_article.dd is not a block special device.
Proceed anyway? (y,n) y
...
Writing superblocks and filesystem accounting information: done
[user@localhost ~]$

Montez le système de fichiers :

[user@localhost ~]$ sudo mount ~/tempfile_for_article.dd /mnt/
[sudo] password for user: 
[user@localhost ~]$ df -h | grep mnt
/dev/loop0      8.7M  172K  7.9M   3% /mnt

Nous créons un répertoire avec notre propriétaire :

[user@localhost ~]$ sudo mkdir /mnt/logs
[user@localhost ~]$ sudo chown user: /mnt/logs

Ouvrons le fichier en écriture uniquement dans notre programme :

with open("/mnt/logs/123.txt", "w") as f:

Run

[user@localhost ]$ python openforwrite.py 

On attend quelques secondes

[user@localhost ~]$ df -h | grep mnt
/dev/loop0      8.7M  8.0M     0 100% /mnt

Nous avons donc le problème décrit au début de cet article. Espace libre 0, occupé à 100%.

Nous rappelons que selon les conditions de la tâche, nous essayons d'enregistrer des données très importantes qui ne peuvent être perdues. Et en même temps, nous devons réparer le service sans redémarrer le processus.

Disons que nous avons encore de l'espace disque, mais dans une partition différente, par exemple dans /home.

Essayons de « reprogrammer à la volée » notre code.

Regardons le PID de notre processus, qui a consommé tout l'espace disque :

[user@localhost ~]$ ps axuf | grep [o]penfor
user 10078 27.2  0.0 128600  5744 pts/22   R+   11:06   0:02  |   _ python openforwrite.py

Connectez-vous au processus via gdb

[user@localhost ~]$ gdb -p 10078
...
(gdb) 

Regardons les descripteurs de fichiers ouverts :

(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user  0 Oct  8 11:06 .
dr-xr-xr-x 9 user user  0 Oct  8 11:06 ..
lrwx------ 1 user user 64 Oct  8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  8 11:09 3 -> /mnt/logs/123.txt

Nous regardons les informations sur le descripteur de fichier numéro 3, qui nous intéresse

(gdb) shell cat /proc/10078/fdinfo/3
pos:    8189952
flags:  0100001
mnt_id: 482

En gardant à l'esprit l'appel système effectué par Python (voir ci-dessus où nous avons exécuté strace et trouvé l'appel open), lors du traitement de notre code pour ouvrir un fichier, nous faisons la même chose nous-mêmes au nom de notre processus, mais nous avons besoin du O_WRONLY|O_CREAT| Les bits O_TRUNC sont remplacés par une valeur numérique. Pour cela, ouvrez les sources du noyau, par exemple ici et regardez quels drapeaux sont responsables de quoi

#définir O_WRONLY 00000001
#définir O_CREAT 00000100
#définir O_TRUNC 00001000

On combine toutes les valeurs en une seule, on obtient 00001101

Nous lançons notre appel depuis gdb

(gdb) call open("/home/user/123.txt", 00001101,0666)
$1 = 4

On obtient donc un nouveau descripteur de fichier avec le numéro 4 et un nouveau fichier ouvert sur une autre partition, on vérifie :

(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user  0 Oct  8 11:06 .
dr-xr-xr-x 9 user user  0 Oct  8 11:06 ..
lrwx------ 1 user user 64 Oct  8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  8 11:09 3 -> /mnt/logs/123.txt
l-wx------ 1 user user 64 Oct  8 11:15 4 -> /home/user/123.txt

Nous nous souvenons de l'exemple avec pipe - comment bash modifie les descripteurs de fichiers, et nous avons déjà appris l'appel système dup2.

Nous essayons de remplacer un descripteur de fichier par un autre

(gdb) call dup2(4,3)
$2 = 3

vérifier:

(gdb) shell ls -lah /proc/10078/fd/
total 0
dr-x------ 2 user user  0 Oct  8 11:06 .
dr-xr-xr-x 9 user user  0 Oct  8 11:06 ..
lrwx------ 1 user user 64 Oct  8 11:09 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:09 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:06 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  8 11:09 3 -> /home/user/123.txt
l-wx------ 1 user user 64 Oct  8 11:15 4 -> /home/user/123.txt

Nous fermons le descripteur de fichier 4, puisque nous n'en avons pas besoin :

(gdb) call close (4)
$1 = 0

Et quittez GDB

(gdb) quit
A debugging session is active.

    Inferior 1 [process 10078] will be detached.

Quit anyway? (y or n) y
Detaching from program: /usr/bin/python2.7, process 10078

Vérification du nouveau fichier :

[user@localhost ~]$ ls -lah /home/user/123.txt
-rw-rw-r-- 1 user user 5.1M Oct  8 11:18 /home/user/123.txt
[user@localhost ~]$ ls -lah /home/user/123.txt
-rw-rw-r-- 1 user user 7.1M Oct  8 11:18 /home/user/123.txt

Comme vous pouvez le voir, les données sont écrites dans un nouveau fichier, vérifions l'ancien :

[user@localhost ~]$ ls -lah /mnt/logs/123.txt 
-rw-rw-r-- 1 user user 7.9M Oct  8 11:08 /mnt/logs/123.txt

Aucune donnée n'est perdue, l'application fonctionne, les journaux sont écrits dans un nouvel emplacement.

Compliquons un peu la tâche

Imaginons que les données soient importantes pour nous, mais nous n’avons d’espace disque dans aucune des partitions et nous ne pouvons pas connecter le disque.

Ce que nous pouvons faire, c'est rediriger nos données quelque part, par exemple vers un canal, et à leur tour rediriger les données du canal vers le réseau via un programme, par exemple netcat.
Nous pouvons créer un canal nommé avec la commande mkfifo. Il créera un pseudo-fichier sur le système de fichiers même s'il n'y a pas d'espace libre dessus.

Redémarrez l'application et vérifiez :

[user@localhost ]$ python openforwrite.py 
[user@localhost ~]$ ps axuf | grep [o]pen
user  5946 72.9  0.0 128600  5744 pts/22   R+   11:27   0:20  |   _ python openforwrite.py
[user@localhost ~]$ ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user  0 Oct  8 11:27 .
dr-xr-xr-x 9 user user  0 Oct  8 11:27 ..
lrwx------ 1 user user 64 Oct  8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  8 11:28 3 -> /mnt/logs/123.txt
[user@localhost ~]$ df -h | grep mnt
/dev/loop0      8.7M  8.0M     0 100% /mnt

Il n'y a pas d'espace disque, mais nous avons réussi à y créer un canal nommé :

[user@localhost ~]$ mkfifo /mnt/logs/megapipe
[user@localhost ~]$ ls -lah /mnt/logs/megapipe 
prw-rw-r-- 1 user user 0 Oct  8 11:28 /mnt/logs/megapipe

Maintenant, nous devons en quelque sorte envelopper toutes les données qui entrent dans ce canal vers un autre serveur via le réseau ; le même netcat convient pour cela.

Sur le serveur remote-server.example.com nous lançons

[user@localhost ~]$ nc -l 7777 > 123.txt 

Sur notre serveur problématique, nous lançons dans un terminal séparé

[user@localhost ~]$ nc remote-server.example.com 7777 < /mnt/logs/megapipe 

Désormais, toutes les données qui aboutissent dans le tube iront automatiquement vers stdin dans netcat, qui les enverra au réseau sur le port 7777.

Tout ce que nous avons à faire est de commencer à écrire nos données dans ce tube nommé.

Nous avons déjà l'application en cours d'exécution :

[user@localhost ~]$ ps axuf | grep [o]pen
user  5946 99.8  0.0 128600  5744 pts/22   R+   11:27 169:27  |   _ python openforwrite.py
[user@localhost ~]$ ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user  0 Oct  8 11:27 .
dr-xr-xr-x 9 user user  0 Oct  8 11:27 ..
lrwx------ 1 user user 64 Oct  8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  8 11:28 3 -> /mnt/logs/123.txt

De tous les indicateurs, nous n'avons besoin que de O_WRONLY puisque le fichier existe déjà et nous n'avons pas besoin de l'effacer

[user@localhost ~]$ gdb -p 5946
...
(gdb) call open("/mnt/logs/megapipe", 00000001,0666)
$1 = 4
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user  0 Oct  8 11:27 .
dr-xr-xr-x 9 user user  0 Oct  8 11:27 ..
lrwx------ 1 user user 64 Oct  8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  8 11:28 3 -> /mnt/logs/123.txt
l-wx------ 1 user user 64 Oct  8 14:20 4 -> /mnt/logs/megapipe
(gdb) call dup2(4,3)
$2 = 3
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user  0 Oct  8 11:27 .
dr-xr-xr-x 9 user user  0 Oct  8 11:27 ..
lrwx------ 1 user user 64 Oct  8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  8 11:28 3 -> /mnt/logs/megapipe
l-wx------ 1 user user 64 Oct  8 14:20 4 -> /mnt/logs/megapipe
(gdb) call close(4)
$3 = 0
(gdb) shell ls -lah /proc/5946/fd
total 0
dr-x------ 2 user user  0 Oct  8 11:27 .
dr-xr-xr-x 9 user user  0 Oct  8 11:27 ..
lrwx------ 1 user user 64 Oct  8 11:28 0 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:28 1 -> /dev/pts/22
lrwx------ 1 user user 64 Oct  8 11:27 2 -> /dev/pts/22
l-wx------ 1 user user 64 Oct  8 11:28 3 -> /mnt/logs/megapipe
(gdb) quit
A debugging session is active.

    Inferior 1 [process 5946] will be detached.

Quit anyway? (y or n) y
Detaching from program: /usr/bin/python2.7, process 5946

Vérification du serveur distant remote-server.example.com

[user@localhost ~]$ ls -lah 123.txt 
-rw-rw-r-- 1 user user 38M Oct  8 14:21 123.txt

Les données arrivent, nous vérifions le serveur problématique

[user@localhost ~]$ ls -lah /mnt/logs/
total 7.9M
drwxr-xr-x 2 user user 1.0K Oct  8 11:28 .
drwxr-xr-x 4 root     root     1.0K Oct  8 10:55 ..
-rw-rw-r-- 1 user user 7.9M Oct  8 14:17 123.txt
prw-rw-r-- 1 user user    0 Oct  8 14:22 megapipe

Les données sont enregistrées, le problème est résolu.

J'en profite pour saluer mes collègues de Degiro.
Écoutez les podcasts de Radio-T.

Tout va bien.

En guise de devoirs, je vous suggère de réfléchir à ce que seront les descripteurs de fichiers cat et sleep si vous exécutez la commande suivante :

[user@localhost ~]$ cat /dev/zero 2>/dev/null| sleep 10000

Source: habr.com

Ajouter un commentaire