Arquivo descritor do Linux com exemplos

Uma vez, em uma entrevista, eles me perguntaram o que você faria se encontrar um serviço que não está funcionando porque o disco ficou sem espaço?

Obviamente, respondi que veria o que esse lugar estava ocupado e, se possível, limparia o local.
Em seguida, o entrevistador perguntou: e se não houver espaço livre na seção, mas você também não vê os arquivos que ocupariam todo o lugar?

Para isso, eu disse que você sempre pode examinar os descritores de arquivos abertos, por exemplo, com o comando lsof e entender qual aplicativo ocupa todo o espaço disponível e, em seguida, você pode prosseguir de acordo com as circunstâncias, dependendo da necessidade dos dados.

O entrevistador me interrompeu na última palavra, adicionando minha pergunta: "Suponha que não precisamos dos dados, é apenas um log de depuração, mas o aplicativo não funciona porque não pode gravar a depuração"?

"Ok", respondi, "podemos desativar a depuração na configuração do aplicativo e reiniciá-la".
O entrevistador objetou: “Não, não podemos reiniciar o aplicativo, dados importantes ainda estão armazenados em nossa memória e clientes importantes estão conectados ao próprio serviço, que não podemos forçar a reconectar”.

"Bem", eu disse, "se não podemos reiniciar o aplicativo e os dados não são importantes para nós, podemos simplesmente limpar esse arquivo aberto através do descritor de arquivos, mesmo que não o possamos ver no comando ls no sistema de arquivos".

O entrevistador ficou satisfeito, mas eu não.

Então pensei: por que a pessoa que está testando meu conhecimento não se aprofunda? Mas e se os dados ainda forem importantes? E se não pudermos reiniciar o processo e, ao mesmo tempo, esse processo gravar no sistema de arquivos em uma seção na qual não haja espaço livre? E se não pudermos perder não apenas os dados já gravados, mas também os dados que esse processo grava ou tenta gravar?

Tuzik


No início da minha carreira, tentei criar um pequeno aplicativo no qual era necessário armazenar informações sobre os usuários. E então pensei: como posso mapear o usuário para seus dados. Por exemplo, eu tenho Ivan Ivanov Ivanich, e ele tem alguns dados, mas como fazer amizade com eles? Eu posso indicar diretamente que o cachorro chamado "Tuzik" pertence a esse mesmo Ivan. Mas e se ele mudar de nome e, em vez de Ivan, se tornar, por exemplo, Olya? Acontece que nosso Olya Ivanovna Ivanova não terá mais um cachorro e nosso Tuzik ainda pertencerá ao inexistente Ivan. Esse banco de dados foi ajudado por um banco de dados que forneceu a cada usuário um identificador exclusivo (ID), e meu Tuzik foi anexado a esse ID, que, de fato, era apenas um número ordinal. Assim, o proprietário do tuzik estava com o número de identificação 2 e, em algum momento, Ivan estava com esse ID e, em seguida, Olya se tornou o mesmo ID. O problema da humanidade e da criação de animais foi praticamente resolvido.

Descritor de arquivo


O problema do arquivo e do programa que trabalha com esse arquivo é quase o mesmo do nosso cão e pessoa. Suponha que eu abri um arquivo com o nome ivan.txt e comecei a escrever a palavra tuzik nele, mas consegui escrever apenas a primeira letra “t” no arquivo, e esse arquivo foi renomeado por alguém, por exemplo, olya.txt. Mas o arquivo permaneceu o mesmo, e eu ainda quero escrever meu ás nele. Cada vez que abro um arquivo com a chamada de sistema aberta em qualquer linguagem de programação, recebo um ID exclusivo que me indica um arquivo, esse ID é um descritor de arquivo. E não importa o que alguém faça a seguir com esse arquivo, eles podem excluí-lo, renomeá-lo, alterar o proprietário ou tirar permissões de leitura e gravação, ainda terei acesso a ele, porque no momento em que o arquivo é aberto, Eu tinha o direito de ler e / ou escrever e consegui começar a trabalhar com ele, o que significa que devo continuar fazendo isso.

No Linux, a biblioteca libc é aberta para cada arquivo descritor do aplicativo em execução (processo) 3, com números 0,1,2. Você pode encontrar mais informações nos links man stdio e man stdout

  • O descritor de arquivo 0 é chamado STDIN e está associado à entrada de dados do aplicativo
  • O descritor de arquivo 1 é chamado STDOUT e é usado por aplicativos para gerar dados, por exemplo, comandos de impressão
  • O descritor de arquivo 2 é chamado STDERR e é usado por aplicativos para gerar dados de relatório de erros.

Se você abrir um arquivo para leitura ou gravação em seu programa, provavelmente obterá o primeiro ID gratuito e este será o número 3.

Uma lista de arquivos descritores pode ser visualizada a partir de qualquer processo, se você souber seu PID.

Por exemplo, abra um console com bash e veja o PID do nosso processo

[user@localhost ]$ echo $$ 15771 

No segundo console, execute

 [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 

Você pode ignorar com segurança o descritor de arquivo número 255 na estrutura deste artigo; ele foi aberto para suas necessidades pelo próprio bash e não por uma biblioteca vinculada.

Agora, todos os três arquivos descritores estão associados ao dispositivo pseudo-terminal / dev / pts , mas ainda podemos manipulá-los, por exemplo, para rodar no segundo console

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

E no primeiro console veremos

 [user@localhost ]$ hello world 

Redirecionar e canalizar


É possível substituir facilmente esses três arquivos de descritor em qualquer processo, inclusive no bash, por exemplo, através de um canal que conecta dois processos, consulte

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

Você pode executar esse comando com strace -f e ver o que acontece por dentro, mas eu vou lhe dizer brevemente.

Nosso processo pai bash com PID 15771 analisa nosso comando e entende exatamente quantos comandos queremos executar, no nosso caso, existem dois deles: gato e sono. Bash sabe que ele precisa criar dois processos filhos e combiná-los com um canal. O bash total precisará de 2 processos filho e um pipe.

Antes de criar os processos filho, o bash inicia a chamada do sistema de canal e recebe novos descritores de arquivo para o buffer de canal temporário, mas esse buffer ainda não vincula nossos dois processos filhos.

Para o processo pai, parece que o pipe já existe e ainda não há processos filhos:

 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 

Em seguida, usando uma chamada de sistema, o clone bash cria dois processos filhos e nossos três processos terão a seguinte aparência:

 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ão esqueça que o clone clona o processo junto com todos os descritores de arquivo, portanto eles serão os mesmos no processo pai e nos filhos. A tarefa do processo pai com o PID 15771 é monitorar os processos filhos, portanto, apenas aguarda uma resposta dos processos filhos.

Portanto, ele não precisa de pipe e fecha os descritores de arquivo com os números 3 e 4.

No primeiro processo filho do bash com PID 9004, a chamada do sistema dup2 altera nosso descritor de arquivo STDOUT com o número 1 para um descritor de arquivo apontando para pipe, no nosso caso, é o número 3. Assim, tudo o que o primeiro processo filho com o PID 9004 gravará no STDOUT cairá automaticamente no buffer do tubo.

No segundo processo filho com o PID 9005, o bash altera o descritor de arquivo STDIN com o número 0. Agora, tudo o que nosso segundo bash com o PID 9005 ler, será lido no pipe.

Depois disso, os descritores com os números 3 e 4 também são fechados em processos filhos, pois não são mais utilizados.

Eu deliberadamente ignoro o descritor de arquivo 255, ele usa o bash para necessidades internas e também será fechado em processos filho.

Além disso, no primeiro processo filho do PID 9004, o bash inicia o arquivo executável especificado na linha de comando com a chamada de sistema exec , no nosso caso, é / usr / bin / cat.

No segundo processo filho com o PID 9005, o bash inicia o segundo arquivo executável especificado, no nosso caso, é / usr / bin / sleep.

A chamada do sistema exec não fecha os descritores de arquivo se eles não foram abertos com o sinalizador O_CLOEXEC durante a chamada de abertura. No nosso caso, após a execução dos arquivos executáveis, todos os descritores de arquivos atuais serão salvos.

Verifique no 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 

Como você pode ver, o número único de nossos tubos é o mesmo nos dois processos. Portanto, temos uma conexão entre dois processos diferentes com um pai.

Para aqueles que não estão familiarizados com as chamadas do sistema que o bash usa, eu recomendo executar os comandos através do strace e ver o que acontece dentro, por exemplo, desta forma:

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

Vamos voltar ao nosso problema com falta de espaço em disco e tentando salvar dados sem reiniciar o processo. Vamos escrever um pequeno programa que irá gravar no disco cerca de 1 megabyte por segundo. Além disso, se por algum motivo não pudemos gravar dados no disco, simplesmente os ignoramos e tentamos gravar os dados novamente após um segundo. No exemplo que eu uso Python, você pode usar qualquer outra linguagem de programação.

 [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 

Execute o programa e veja os descritores de arquivo

 [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 

Como você pode ver, temos nossos três descritores de arquivo padrão e outro que abrimos. Verifique o tamanho do arquivo:

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

dados estão sendo gravados, tente alterar as permissões do arquivo:

 [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 

Vemos que os dados ainda estão sendo gravados, embora nosso usuário não tenha o direito de gravar no arquivo. Vamos tentar removê-lo:

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

Onde estão os dados gravados? E eles estão escritos? Verificamos:

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

Sim, nosso arquivo descritor ainda existe e podemos trabalhar com esse arquivo descritor, assim como com nosso arquivo antigo, podemos ler, limpar e copiá-lo.

Observamos o tamanho do arquivo:

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

Tamanho do arquivo 19923457. Tentando limpar o arquivo:

 [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 

Como você pode ver, o tamanho do arquivo está aumentando apenas e nosso trankate não funcionou. Consulte a documentação para a chamada de sistema aberta . Se usarmos o sinalizador O_APPEND ao abrir um arquivo, sempre que o sistema operacional verificar o tamanho do arquivo e gravar dados no final do arquivo, ele fará isso de maneira atômica. Isso permite que vários threads ou processos gravem no mesmo arquivo. Mas em nosso código, não usamos esse sinalizador. Podemos ver um tamanho de arquivo diferente em lsof após trankate somente se abrirmos o arquivo para gravação adicional, o que significa que, em vez de em nosso código

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

nós temos que colocar

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

Verificando com a bandeira "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 

e com a bandeira "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 

Programar um processo já em execução


Frequentemente, os programadores usam depuradores (por exemplo, GDB) ou vários níveis de logon no aplicativo ao criar e testar programas. O Linux fornece a capacidade de realmente escrever e alterar um programa já em execução, por exemplo, alterando os valores das variáveis, definindo o ponto de interrupção etc.

Voltando à pergunta original, com pouco espaço em disco para gravar o arquivo, tentaremos emular o problema.

Crie um arquivo para nossa partição, que montaremos como um disco separado:

 [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 ~]$ 

Crie um sistema de arquivos:

 [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 ~]$ 

Monte o sistema de arquivos:

 [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 

Crie um diretório com nosso proprietário:

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

Abrimos o arquivo para gravação apenas em nosso programa:

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

Lançamos

 [user@localhost ]$ python openforwrite.py 

Esperando alguns segundos

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

Portanto, temos o problema descrito no início deste artigo. Espaço livre 0, ocupado 100%.

Lembramos que, de acordo com as condições da tarefa, estamos tentando registrar dados muito importantes que não podem ser perdidos. E, ao mesmo tempo, precisamos reparar o serviço sem reiniciar o processo.

Suponha que ainda tenhamos espaço em disco, mas em uma partição diferente, por exemplo, em / home.

Vamos tentar "reprogramar em tempo real" nosso código.

Observamos o PID do nosso processo, que consumiu todo o espaço em disco:

 [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 

Nós nos conectamos ao processo via gdb

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

Examinamos os descritores de arquivos abertos:

 (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 

Examinamos as informações sobre o descritor de arquivo com o número 3, que nos interessam

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

Lembrando que tipo de chamada de sistema o Python faz (veja acima, onde executamos o strace e encontramos a chamada aberta), processando nosso código para abrir o arquivo, fazemos o mesmo em nosso próprio nome, mas precisamos dos bits O_WRONLY | O_CREAT | O_TRUNC substitua por um valor numérico. Para fazer isso, abra as fontes do kernel, por exemplo, aqui e veja quais sinalizadores são responsáveis ​​por quais

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

Combinamos todos os valores em um, obtemos 00001101

Execute nossa chamada do gdb

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

Portanto, temos um novo arquivo descritor com o número 4 e um novo arquivo aberto em outra seção, verifique:

 (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 

Lembramos do exemplo de canal - como o bash altera os descritores de arquivo e já aprendemos a chamada do sistema dup2.

Tentamos substituir um descritor de arquivo por outro

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

Verificamos:

 (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 

Encerramos o descritor de arquivo 4, pois não precisamos dele:

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

E saia do 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 

Verifique o novo arquivo:

 [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 

Como você pode ver, os dados são gravados em um novo arquivo, verificamos o antigo:

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

Os dados não são perdidos, o aplicativo funciona, os logs são gravados em um novo local.

Vamos complicar um pouco a tarefa


Imagine que os dados são importantes para nós, mas não temos espaço em disco em nenhuma das seções e não podemos conectar o disco.

O que podemos fazer é redirecionar nossos dados em algum lugar, por exemplo, para canalizar, e os dados do canal, por sua vez, redirecionam para a rede através de algum programa, por exemplo, netcat.
Podemos criar um pipe nomeado com o comando mkfifo. Ele criará um pseudo arquivo no sistema de arquivos, mesmo se não houver espaço livre nele.

Reiniciamos o aplicativo e verificamos:

 [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 

Não há espaço em disco, mas criamos com êxito um pipe nomeado lá:

 [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 

Agora, de alguma forma, precisamos agrupar todos os dados que entram nesse canal em outro servidor pela rede, para isso tudo o que o netcat fará.

No servidor remote-server.example.com, execute

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

No nosso servidor com problemas, execute em um terminal separado

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

Agora, todos os dados que entram no canal serão automaticamente enviados para stdin no netcat, que os enviará para a rede na porta 7777.

Tudo o que precisamos fazer é começar a gravar nossos dados nesse pipe nomeado.

Já temos um aplicativo em execução:

 [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 todos os sinalizadores, precisamos apenas de O_WRONLY, pois o arquivo já existe e não precisamos limpá-lo

 [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 

Verificando o servidor remoto remote-server.example.com

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

Os dados vão, verificamos um servidor com problema

 [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 

Dados salvos, problema resolvido.

Aproveito esta oportunidade para expressar meus cumprimentos aos meus colegas da Degiro.
Ouça os podcasts da Radio T.

Bom para todos.

Como lição de casa, proponho pensar sobre o que estará nos descritores de arquivo do processo cat e sleep se você executar este comando:

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

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


All Articles