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
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
- 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
[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
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
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
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
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
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
#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