Fichier descripteur Linux avec des exemples

Une fois, dans une interview, ils m'ont demandé ce que vous feriez si vous trouvez un service qui ne fonctionne pas parce que le disque est à court d'espace?

Bien sûr, j'ai répondu que je verrais ce que cet endroit était occupé et, si possible, je nettoyerais l'endroit.
L'enquêteur a ensuite demandé s'il n'y avait pas d'espace libre sur la section, mais vous ne voyez pas non plus les fichiers qui occuperaient toute la place?

Pour 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 pris tout l'espace disponible, puis vous pouvez procéder selon les circonstances, selon que les données sont nécessaires ou non.

L'intervieweur m'a interrompu au dernier mot, ajoutant ma 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 enregistrer le 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 s'est opposé: «Non, nous ne pouvons pas redémarrer l'application, des données importantes sont toujours stockées dans notre mémoire, et des clients importants sont connectés au service lui-même, que nous ne pouvons pas forcer à se reconnecter».

«Eh bien, ai-je dit, si nous ne pouvons pas redémarrer l'application et que les données ne sont pas importantes pour nous, nous pouvons simplement effacer ce fichier ouvert via le fichier descripteur, 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.

Alors j'ai pensé, pourquoi la personne qui teste mes connaissances ne creuse pas plus profondément? Mais que se passe-t-il si les données sont toujours importantes? Et si nous ne pouvons pas redémarrer le processus, et en même temps ce processus écrit dans le système de fichiers dans une section sur laquelle il n'y a pas d'espace libre? Et si nous ne pouvons pas perdre non seulement les données déjà enregistrées, mais aussi les données que ce processus écrit ou tente d'enregistrer?

Tuzik


Au début de ma carrière, j'ai essayé de créer une petite application dans laquelle il fallait stocker des informations sur les utilisateurs. Et puis j'ai pensé, comment puis-je mapper l'utilisateur à ses données. Par exemple, j'ai Ivan Ivanov Ivanich, et il a des données, mais comment se faire des amis avec eux? Je peux indiquer directement que le chien nommé "Tuzik" appartient à ce même Ivan. Mais que se passe-t-il s'il change de nom et qu'Ivan devient, par exemple, Olya? Ensuite, il s'avère que notre Olya Ivanovna Ivanova n'aura plus de chien, et notre Tuzik appartiendra toujours à l'Ivan inexistant. Cette base de données a été aidée par une base de données qui a donné à chaque utilisateur un identifiant (ID) unique, et mon Tuzik était attaché à cet ID, qui, en fait, n'était qu'un numéro ordinal. Ainsi, le propriétaire du tuzik était avec le numéro d'identification 2, et à un moment donné, Ivan était sous ce numéro, puis Olya est devenu le même numéro. Le problème de l'humanité et de l'élevage est pratiquement résolu.

Descripteur de fichier


Le problème du fichier et du programme fonctionnant avec ce fichier est à peu près le même que celui de notre chien et de notre personne. Supposons que j'ouvre un fichier sous le nom ivan.txt et que je commence à y écrire le mot tuzik, mais que je ne parvienne à écrire que la première lettre «t» dans le fichier, et ce fichier a été renommé par quelqu'un, par exemple, olya.txt. Mais le fichier est resté le même, et je veux toujours y écrire mon as. Chaque fois que j'ouvre un fichier avec l'appel système ouvert dans n'importe quel langage de programmation, j'obtiens un ID unique qui me pointe vers un fichier, cet ID est un descripteur de fichier. Et peu importe ce que quelqu'un fera ensuite avec ce fichier, ils peuvent le supprimer, ils peuvent le renommer, ils peuvent changer le propriétaire ou retirer les autorisations de lecture et d'écriture, j'y aurai toujours accès, car au moment où le fichier est ouvert, J'avais le droit de le lire et / ou de l'écrire, et j'ai réussi à commencer à travailler avec lui, ce qui signifie que je dois continuer à le faire.

Sous Linux, la bibliothèque libc s'ouvre pour chaque fichier descripteur d'application (processus) 3 en cours d'exécution, avec les nombres 0,1,2. Vous pouvez trouver plus d'informations sur les liens man stdio et man stdout

  • Le descripteur de fichier 0 est appelé STDIN et est associé à une entrée de données de l'application
  • Le descripteur de fichier 1 est appelé STDOUT et est utilisé par les applications pour sortir des données, par exemple, des commandes d'impression
  • Le descripteur de fichier 2 est appelé STDERR et est utilisé par les applications pour produire des données de rapport d'erreurs.

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.

Une liste de fichiers descripteurs peut être consultée à partir de n'importe quel processus si vous connaissez son PID.

Par exemple, ouvrez une console avec bash et voyez le PID de notre processus

[user@localhost ]$ echo $$ 15771 

Dans la deuxième console, exécutez

 [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 dans le cadre de cet article; il a été ouvert pour vos besoins par bash lui-même, et non par une bibliothèque liée.

Maintenant, les 3 fichiers descripteurs sont associés au périphérique pseudo-terminal / dev / pts , mais nous pouvons toujours les manipuler, par exemple, les exécuter dans la deuxième console

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

Et dans la première console, nous verrons

 [user@localhost ]$ hello world 

Rediriger et diriger


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

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

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

Notre processus parent bash avec 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 combiner avec un seul tuyau. Total bash aura besoin de 2 processus enfants et d'un canal.

Avant de créer des processus enfants, bash lance l'appel système de canal et reçoit de nouveaux descripteurs de fichier pour le tampon de canal temporaire, mais ce tampon ne lie pas encore nos deux processus enfants.

Pour le processus parent, il semble que le canal existe déjà, et il n'y a pas encore de processus enfant:

 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 

Ensuite, à l'aide d'un appel système, clone 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 le clone clone le processus avec tous les descripteurs de fichiers, ils seront donc les mêmes dans le processus parent et dans les enfants. La tâche du processus parent avec le PID 15771 est de surveiller les processus enfants, il attend donc simplement une réponse des processus enfants.

Par conséquent, il n'a pas besoin de pipe et ferme les descripteurs de fichiers avec les numéros 3 et 4.

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

Dans le deuxième processus enfant avec PID 9005, bash modifie le descripteur de fichier STDIN avec le numéro 0 en utilisant dup2. Maintenant, tout ce que notre deuxième bash avec PID 9005 lira lira à partir du canal.

Après cela, les descripteurs avec les nombres 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 utilise bash pour les besoins internes et sera également fermé dans les processus enfants.

De plus, dans le premier processus enfant avec PID 9004, bash démarre le fichier exécutable que nous avons spécifié sur la ligne de commande avec l'appel système exec , dans notre cas c'est / usr / bin / cat.

Dans le deuxième processus enfant avec PID 9005, bash démarre le deuxième fichier exécutable que nous avons spécifié, dans notre cas, il s'agit de / usr / bin / sleep.

L'appel système exec ne ferme pas les descripteurs de fichiers s'ils n'ont pas été ouverts avec l'indicateur O_CLOEXEC pendant l'appel ouvert. Dans notre cas, après avoir exécuté 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 voir, le nombre unique de nos tuyaux est le même dans les deux processus. Nous avons donc une connexion entre deux processus différents avec un parent.

Pour ceux qui ne connaissent pas les appels système utilisés par bash, je recommande vivement d'exécuter les commandes via strace et de voir ce qui se passe à l'intérieur, 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 d'enregistrer les données sans redémarrer le processus. Écrivons un petit programme qui écrit sur le disque environ 1 mégaoctet par seconde. De plus, si pour une raison quelconque, nous ne pouvions pas écrire de données sur le disque, nous l'ignorerions simplement et tenterions de réécrire les données après 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écutez le programme et regardez 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 standard et un autre que nous avons ouvert. Vérifiez la taille du fichier:

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

des données sont en cours d'écriture, essayez de modifier les autorisations du 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 constatons que les données sont toujours en cours d'écriture, bien que notre utilisateur n'ait pas le droit d'é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 fichier descripteur existe toujours, et nous pouvons travailler avec ce fichier descripteur comme avec notre ancien fichier, nous pouvons le lire, le nettoyer et le copier.

Nous regardons la taille du fichier:

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

Taille du fichier 19923457. Essayer 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 voir, la taille du fichier ne fait qu'augmenter et notre trankate n'a pas fonctionné. Reportez-vous à la documentation de l'appel système ouvert . Si, lors de l'ouverture d'un fichier, nous utilisons l'indicateur O_APPEND, chaque fois que le système d'exploitation vérifie la taille du fichier et écrit des données à la toute fin du fichier, il le fait 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 trankate uniquement si nous ouvrons le fichier pour un enregistrement supplémentaire, ce qui signifie qu'au lieu de 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 

Programmer un processus déjà en cours


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

Revenant à la question d'origine avec un espace disque insuffisant pour écrire le fichier, nous allons essayer d'émuler le problème.

Créez un fichier pour notre partition, que nous monterons comme un 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éez 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 

Créez un annuaire avec notre propriétaire:

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

Nous ouvrons le fichier en écriture uniquement dans notre programme:

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

Nous lançons

 [user@localhost ]$ python openforwrite.py 

Attendre quelques secondes

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

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

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

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

Essayons de "reprogrammer à la volée" notre code.

Nous 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 

Nous nous connectons au processus via gdb

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

Nous 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 avec le numéro 3, ce qui nous intéresse

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

En se souvenant du type d'appel système que Python fait (voir ci-dessus, où nous avons exécuté strace et trouvé l'appel ouvert), en traitant notre code pour ouvrir le fichier, nous faisons la même chose en notre nom, mais nous avons besoin des bits O_WRONLY | O_CREAT | O_TRUNC remplacer par une valeur numérique. Pour ce faire, ouvrez les sources du noyau, par exemple, ici et voyez quels drapeaux sont responsables de quoi

#define O_WRONLY 00000001
#define O_CREAT 00000100
#define O_TRUNC 00001000

Nous combinons toutes les valeurs en une seule, nous obtenons 00001101

Exécutez notre appel depuis gdb

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

Nous avons donc obtenu un nouveau fichier descripteur avec le numéro 4 et un nouveau fichier ouvert sur une autre section, vérifiez:

 (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 de pipe - comment bash change 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 

Nous vérifions:

 (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, car 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érifiez le 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, nous 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 

Les données ne sont pas perdues, l'application fonctionne, les journaux sont écrits dans un nouvel emplacement.

Compliquons un peu la tâche


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

Ce que nous pouvons faire, c'est rediriger nos données quelque part, par exemple vers le tube, et les données du tube, à leur tour, sont redirigées 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.

Nous redémarrons l'application et vérifions:

 [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 y avons réussi à 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, pour cela, tout le même netcat fera.

Sur le serveur remote-server.example.com, exécutez

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

Sur notre serveur à problèmes, exécutez dans un terminal séparé

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

Maintenant, toutes les données qui entrent dans le canal iront automatiquement à 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 canal nommé.

Nous avons déjà une 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 car 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 vont, nous vérifions un serveur problème

 [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 

Données enregistrées, problème résolu.

J'en profite pour transmettre mes salutations à mes collègues de Degiro.
Écoutez les podcasts de Radio T.

Bon à tous.

En guise de devoirs, je propose de penser à ce qui sera dans les descripteurs de fichiers du processus chat et sleep si vous exécutez cette commande:

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

Source: https://habr.com/ru/post/fr471038/


All Articles