É seguro analisar um arquivo / proc /?

152

Eu quero analisar /proc/net/tcp/, mas é seguro?

Como devo abrir e ler arquivos /proc/e não ter medo de que algum outro processo (ou o próprio sistema operacional) o altere ao mesmo tempo?

Kiril Kirov
fonte
29
+1. Essa é uma pergunta muito boa. Eu só queria ter a resposta, mas estou ansioso para descobrir, já que já fiz esse tipo de coisa um pouco antes.
paxdiablo
1
Tenho certeza de que apenas a leitura fornecerá uma lista de conexões, além do UID que possui cada uma delas, como eram quando você a abriu . Não consigo encontrar isso documentado, no entanto, tornando isso um comentário por enquanto.
Tim Post
3
A resposta simples é obviamente sim, pois não é um arquivo - a leitura deve ser sempre segura. As respostas podem não ser consistentes nas próximas horas em que você a ler, mas será seguro.
Rory Alsop
É por isso que você deve usar o sysctl. (seus também menos syscalls)
Boa Pessoa
@ GoodPerson - como isso pode sysctlme ajudar a analisar um /proc/net/tcp/arquivo, por exemplo?
26412 Kiril Kirov

Respostas:

111

Em geral, não. (Assim, a maioria das respostas aqui estão errados.) Ele pode ser seguro, dependendo do que propriedade desejada. Mas é fácil acabar com erros no seu código se você assumir demais a consistência de um arquivo /proc. Por exemplo, veja este bug resultante da suposição de que /proc/mountsera um instantâneo consistente .

Por exemplo:

  • /proc/uptimeé totalmente atômico , como alguém mencionado em outra resposta - mas apenas desde o Linux 2.6.30 , que tem menos de dois anos. Portanto, mesmo esse arquivo minúsculo e trivial estava sujeito a uma condição de corrida até então, e ainda está na maioria dos kernels corporativos. Veja fs/proc/uptime.ca fonte atual ou o commit que a tornou atômica . Em um kernel anterior à 2.6.30, é possível openarquivar readum pouco, e se você voltar mais tarde read, a peça que você obtiver será inconsistente com a primeira. (Acabei de demonstrar isso - tente você mesmo por diversão.)

  • /proc/mountsé atômico em uma única readchamada do sistema. Portanto, se você readarquivar todo o arquivo de uma só vez, obterá uma única captura instantânea consistente dos pontos de montagem no sistema. No entanto, se você usar várias readchamadas do sistema - e se o arquivo for grande, será exatamente isso que acontecerá se você usar as bibliotecas de E / S normais e não prestar atenção especial a esse problema - estará sujeito a uma corrida doença. Você não apenas obterá uma captura instantânea consistente, mas os pontos de montagem que estavam presentes antes de iniciar e nunca deixaram de estar presentes podem desaparecer no que você vê. Para ver que é atômico para um read(), olhe para m_start()dentrofs/namespace.c e veja-o pegar um semáforo que guarda a lista de pontos de montagem, que ele mantém até m_stop(), que é chamado quando oread()é feito. Para ver o que pode dar errado, veja este bug do ano passado (o mesmo que eu vinculei acima) em software de alta qualidade que lia alegremente /proc/mounts.

  • /proc/net/tcp, o que você realmente está perguntando, é ainda menos consistente do que isso. É atômico apenas dentro de cada linha da tabela . Para ver isso, olhe listening_get_next()dentronet/ipv4/tcp_ipv4.c e established_get_next()logo abaixo no mesmo arquivo e veja os bloqueios que eles retiram em cada entrada. Não tenho o código de reprodução disponível para demonstrar a falta de consistência de uma linha para outra, mas não há bloqueios (ou qualquer outra coisa) que o tornem consistente. O que faz sentido se você pensar a respeito - a rede geralmente é uma parte super ocupada do sistema; portanto, não vale a pena apresentar uma visão consistente nessa ferramenta de diagnóstico.

A outra peça que mantém /proc/net/tcpatômica dentro de cada linha é o buffer no seq_read(), que você pode ler nofs/seq_file.c . Isso garante que, depois que você fizer read()parte de uma linha, o texto da linha inteira seja mantido em um buffer, para que a próxima read()obtenha o restante dessa linha antes de iniciar uma nova. O mesmo mecanismo é usado /proc/mountspara manter cada linha atômica, mesmo se você fizer várias read()chamadas, e também é o mecanismo que /proc/uptimenos kernels mais novos usa para permanecer atômico. Esse mecanismo não armazena em buffer o arquivo inteiro, porque o kernel é cauteloso sobre o uso de memória.

A maioria dos arquivos /procinseridos será pelo menos tão consistente quanto /proc/net/tcp, em cada linha, uma imagem consistente de uma entrada em qualquer informação fornecida, porque a maioria deles usa a mesma seq_fileabstração. Como o /proc/uptimeexemplo ilustra, alguns arquivos ainda estavam sendo migrados para uso seq_fileem 2009; Aposto que ainda existem alguns que usam mecanismos mais antigos e nem sequer têm esse nível de atomicidade. Essas advertências raramente são documentadas. Para um determinado arquivo, sua única garantia é ler a fonte.

No caso de /proc/net/tcp, você pode ler e analisar cada linha sem medo. Mas se você tentar tirar conclusões de várias linhas ao mesmo tempo - cuidado, outros processos e o kernel o estão alterando enquanto você o lê e provavelmente está criando um bug.

Greg Price
fonte
1
e quanto a atomicidade readdir? gosta de ler / proc / self / fd? é seguro?
socketpair
Não que isso responde à pergunta, mas a acrescentar sobre como verificar o tempo de atividade você pode usar clock_gettime(2)com CLOCK_MONOTONIC(embora talvez haja uma tecnicalidade Estou desconhece aqui, mas eu, pessoalmente, só tê-lo visto com desde o tempo de boot). Para Linux, você também tem a opção de sysinfo(2).
Pryftan #
44

Embora os arquivos /procaparecem como arquivos regulares no espaço de usuário, eles não são realmente arquivos, mas sim entidades que apóiam as operações de arquivo padrão de userspace ( open, read, close). Observe que isso é bem diferente de ter um arquivo comum no disco que está sendo alterado pelo kernel.

Tudo o que o kernel faz é imprimir seu estado interno em sua própria memória usando uma sprintffunção-like, e essa memória é copiada no espaço do usuário sempre que você emitir uma read(2)chamada do sistema.

O kernel lida com essas chamadas de uma maneira totalmente diferente da dos arquivos comuns, o que pode significar que todo o instantâneo dos dados que você lerá estará pronto no momento em que você open(2)o fizer , enquanto o kernel garante que as chamadas simultâneas sejam consistentes e atômicas. Eu não li isso em lugar nenhum, mas realmente não faz sentido ser o contrário.

Meu conselho é dar uma olhada na implementação de um arquivo proc no seu sabor Unix específico. Este é realmente um problema de implementação (como é o formato e o conteúdo da saída) que não é governado por um padrão.

O exemplo mais simples seria a implementação do uptimearquivo proc no Linux. Observe como todo o buffer é produzido na função de retorno de chamada fornecida para single_open.

Blagovest Buyukliev
fonte
3
@ Ignacio: Estou apenas apontando o OP nessa direção, porque fiquei com a impressão de que ele acha que os procarquivos são arquivos comuns abertos para gravação pelo kernel.
Blagovest Buyukliev
4
Seu conselho para analisar a implementação do arquivo específico é bom. Infelizmente, o palpite de que tudo está instantâneo open()está errado para muitos arquivos e, em particular /proc/net/tcp, para os quais o OP está preocupado. Isso faz sentido se você pensar no custo de fornecer essas semânticas - teria que fazer algo como bloquear as estruturas de dados internas que registram todas essas conexões TCP, o que em um sistema ocupado é um desastre, mesmo que você o mantenha por muito tempo o suficiente para digitalizar e formatar os dados em um buffer. Veja minha resposta para obter detalhes sobre o que realmente acontece.
Greg Price
16

/ proc é um sistema de arquivos virtual: na verdade, apenas fornece uma visão conveniente dos componentes internos do kernel. É definitivamente seguro lê-lo (é por isso que está aqui), mas é arriscado a longo prazo, pois o interno desses arquivos virtuais pode evoluir com a versão mais recente do kernel.

EDITAR

Mais informações disponíveis na documentação do proc no documento do kernel do Linux , capítulo 1.4 Rede Não consigo encontrar se as informações de como as informações evoluem ao longo do tempo. Eu pensei que estava congelado em aberto, mas não posso ter uma resposta definitiva.

EDIT2

De acordo com o Sco doc (não o linux, mas tenho certeza de que todos os sabores do * nix se comportam assim)

Embora o estado do processo e, conseqüentemente, o conteúdo dos arquivos / proc possam mudar de instante para instante, é garantido que uma única leitura (2) de um arquivo / proc retorne uma representação `` sã '' do estado, ou seja, a leitura será um instantâneo atômico do estado do processo. Essa garantia não se aplica a leituras sucessivas aplicadas a um arquivo / proc para um processo em execução. Além disso, a atomicidade não é especificamente garantida para qualquer E / S aplicada ao arquivo as (espaço de endereço); o conteúdo do espaço de endereço de qualquer processo pode ser modificado simultaneamente por um LWP desse processo ou por qualquer outro processo no sistema.

Bruce
fonte
3
"Eu acho que" ? Seria bom ter uma resposta definitiva :)
static_rtti
Dada a implementação de / proc no kernel, isso também se aplica ao linux. Se você ler um arquivo procfs em uma única chamada de leitura, é consistente - é claro, assumindo que o arquivo proc que você leu foi implementado corretamente no kernelside.
Erik
8
Eu não acho que você poderia ter uma fonte de informação possível pior do que a SCO, e tentar tratar proccomo se tivesse um comportamento semelhante entre diferentes kernels (ou mesmo supondo que exista) - não é necessário em um sistema Unix ) vai te dar um mundo de mágoa.
Nicholas Knight
1
@ Nicolas: bem, não foi possível encontrar uma resposta definitiva no documento do kernel, fique à vontade para apontá-la, se você souber.
19711 Bruce
2
Interessante que os documentos da SCO digam isso. Infelizmente, nem sempre é verdade no Linux e, em particular, não é verdade /proc/net/tcp, o que é a principal preocupação do OP. Em vez disso, apenas cada linha individual na saída é atômica. Veja minha resposta para detalhes.
Greg Price
14

A API procfs no kernel do Linux fornece uma interface para garantir que as leituras retornem dados consistentes. Leia os comentários em __proc_file_read. O item 1) no grande bloco de comentários explica essa interface.

Dito isto, é claro que depende da implementação de um arquivo proc específico para usar essa interface corretamente para garantir que os dados retornados sejam consistentes. Portanto, para responder à sua pergunta: não, o kernel não garante a consistência dos arquivos proc durante uma leitura, mas fornece os meios para as implementações desses arquivos fornecerem consistência.

Trabalho
fonte
4
Infelizmente, muitos arquivos /procnão fornecem consistência. Veja minha resposta para detalhes.
Greg Price
3
Além disso, __proc_file_read()foi preterido em favor de seq_file. Veja o comentário bastante exasperado (de Linus) logo acima do longo comentário de bloco.
Greg Price
6

Eu tenho a fonte para o Linux 2.6.27.8 à mão, pois estou desenvolvendo o driver no momento em um destino ARM incorporado.

O arquivo ... linux-2.6.27.8-lpc32xx/net/ipv4/raw.cna linha 934 contém, por exemplo

    seq_printf(seq, "%4d: %08X:%04X %08X:%04X"
            " %02X %08X:%08X %02X:%08lX %08X %5d %8d %lu %d %p %d\n",
            i, src, srcp, dest, destp, sp->sk_state,
            atomic_read(&sp->sk_wmem_alloc),
            atomic_read(&sp->sk_rmem_alloc),
            0, 0L, 0, sock_i_uid(sp), 0, sock_i_ino(sp),
            atomic_read(&sp->sk_refcnt), sp, atomic_read(&sp->sk_drops));

quais saídas

[wally@zenetfedora ~]$ cat /proc/net/tcp
  sl  local_address rem_address   st tx_queue rx_queue tr tm->when retrnsmt   uid  timeout inode                                                     
   0: 017AA8C0:0035 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 15160 1 f552de00 299
   1: 00000000:C775 00000000:0000 0A 00000000:00000000 00:00000000 00000000     0        0 13237 1 f552ca00 299
...

na função raw_sock_seq_show()que faz parte de uma hierarquia de funções de manipulação de procfs . O texto não é gerado até que read()seja feito um pedido do /proc/net/tcparquivo, um mecanismo razoável, pois as leituras do procfs são certamente muito menos comuns do que atualizar as informações.

Alguns drivers (como o meu) implementam a função proc_read com um único sprintf(). A complicação extra na implementação dos drivers principais é lidar com saídas potencialmente muito longas que podem não caber no buffer intermediário do espaço do kernel durante uma única leitura.

Eu testei isso com um programa usando um buffer de leitura de 64K, mas ele resulta em um buffer de espaço do kernel de 3072 bytes no meu sistema para proc_read retornar dados. São necessárias várias chamadas com ponteiros avançados para obter mais do que a quantidade de texto retornada. Não sei qual o caminho certo para tornar os dados retornados consistentes quando mais de uma E / S é necessária. Certamente cada entrada /proc/net/tcpé auto-consistente. Há alguma probabilidade de que as linhas lado a lado sejam instantâneas em momentos diferentes.

Wallyk
fonte
Sinto muito, eu não entendi muito. Então, você quer dizer que, se eu usar ifstream, será inseguro, mas se eu usá- readlo, será seguro? Ou ifstreamusa internamente read? E o que você sugere?
22411 Kiril Kirov
@ Kiril: Desculpe pela confusão. Esta é uma explicação de como os dados /proc/net/tcpsão formatados e é completamente independente de como alguém os lê.
wallyk
1
Sim! E seu palpite está correto de que diferentes linhas (in /proc/net/tcp) não vêm do mesmo instantâneo. Veja minha resposta para algumas explicações.
Greg Price
3

Com exceção de erros desconhecidos, não há condições de corrida /procque levem à leitura de dados corrompidos ou a uma mistura de dados antigos e novos. Nesse sentido, é seguro. No entanto, ainda existe a condição de corrida em que muitos dos dados dos quais você lê /procestão potencialmente desatualizados assim que gerados, e ainda mais quando você os lê / processa. Por exemplo, os processos podem morrer a qualquer momento e um novo processo pode receber o mesmo pid; os únicos IDs de processo que você pode usar sem condições de corrida são os seus próprios processos filhos '. O mesmo vale para informações de rede (portas abertas, etc.) e realmente para a maioria das informações /proc. Eu consideraria uma prática ruim e perigosa confiar em qualquer dado/procser preciso, exceto dados sobre seu próprio processo e potencialmente seus processos filhos. Obviamente, ainda pode ser útil apresentar outras informações /procao usuário / administrador para informações / registro / etc. propósitos.

R .. GitHub PARE DE AJUDAR O GELO
fonte
Estou fazendo isso para obter e usar algumas informações para meu próprio processo (para meu PID, usando getpid()). Então, tem que ser seguro.
19411 Kiril Kirov
1
Sim, eu consideraria isso completamente seguro.
R .. GitHub Pare de ajudar o gelo
Não concordo que os processos filhos sejam mais bem-comportados do que qualquer outro processo. No que diz respeito à /procinterface, todos eles têm os mesmos pontos fracos e fortes. De qualquer forma, o OP pergunta sobre informações relacionadas ao driver de dispositivo, não sobre processos.
wallyk
1
Se pid Nfor seu processo filho, você poderá garantir que pid Nainda se refira ao mesmo processo (possivelmente encerrado) até chamar uma waitfunção -family nele. Isso garante que não haja corridas.
R .. GitHub Pare de ajudar o gelo
O que há com o dilúvio de -1 e nenhuma explicação?
R .. GitHub Pare de ajudar o gelo
2

Quando você lê um arquivo / proc, o kernel está chamando uma função que foi registrada anteriormente para ser a função "read" para esse arquivo proc. Veja a __proc_file_readfunção em fs / proc / generic.c.

Portanto, a segurança da leitura proc é apenas tão segura quanto a função que o kernel chama para satisfazer a solicitação de leitura. Se essa função bloquear corretamente todos os dados que tocar e retornar para você em um buffer, será totalmente seguro ler usando essa função. Como os arquivos proc, como o usado para atender às solicitações de leitura para / proc / net / tcp, já existem há algum tempo e passaram por uma revisão minuciosa, eles são tão seguros quanto você poderia pedir. De fato, muitos utilitários comuns do Linux dependem da leitura do sistema de arquivos proc e da formatação da saída de uma maneira diferente. (Acima de tudo, acho que 'ps' e 'netstat' fazem isso).

Como sempre, você não precisa aceitar minha palavra; você pode olhar para a fonte para acalmar seus medos. A documentação a seguir do proc_net_tcp.txt informa onde as funções "read" do / proc / net / tcp live, para que você possa ver o código real que é executado ao ler esse arquivo proc e verificar por si mesmo que não há perigos de bloqueio.

Este documento descreve as interfaces / proc / net / tcp e / proc / net / tcp6.
Observe que essas interfaces foram descontinuadas em favor de tcp_diag. Essas interfaces / proc fornecem informações sobre conexões TCP ativas no momento e são implementadas por tcp4_seq_show () em net / ipv4 / tcp_ipv4.c e tcp6_seq_show () em net / ipv6 / tcp_ipv6.c, respectivamente.

heath
fonte