O que acontece quando eu fecho () um descritor de arquivo?

16

Estou tentando obter uma imagem completa com descritores de arquivo. Digamos que eu possua process1, que inicialmente possui esses descritores de arquivo:

 _process1_
|          |
| 0 stdin  |
| 1 stdout |
| 2 stderr |
|__________|

Então eu fecho o descritor de arquivo 1:

close(1);

O descritor de arquivo 1 converte (pontos) na estrutura FILE stdout na Tabela de Arquivos Abertos do kernel .

Com o código acima, o descritor de arquivo 1 é excluído da tabela do processo, que se torna:

 _process1_
|          |
| 0 stdin  |
| 2 stderr |
|__________|

Mas o que acontece no kernel? A stdoutestrutura FILE é desalocada? Como isso é possível se stdout é um arquivo especial (o monitor) e provavelmente está sendo usado por outros processos? E as estruturas FILE que são apenas arquivos normais (por exemplo, .txt)? E se esse arquivo estiver sendo usado por outro processo?

Pithikos
fonte

Respostas:

13

O descritor de arquivo 1 se traduz na estrutura FILE stdout na Tabela de Arquivos Abertos do Kernel.

Isso é um mal entendido. A tabela de arquivos do kernel não tem nada a ver com estruturas de arquivos no espaço do usuário.

De qualquer forma, o kernel possui dois níveis de indireção. Existe a estrutura interna que representa o próprio arquivo, que é referência contada. Há uma "descrição de arquivo aberto" que é contada como referência. E há o identificador de arquivo, que não é contado como referência. A estrutura do arquivo aponta o caminho para o próprio inode. A descrição do arquivo aberto contém itens como o modo aberto e o ponteiro do arquivo.

Quando você chama close, sempre fecha o identificador do arquivo. Quando um identificador de arquivo é fechado, a contagem de referência em sua descrição de arquivo aberto é diminuída. Se chegar a zero, a descrição do arquivo aberto também será liberada e a contagem de referência no próprio arquivo será diminuída. Somente se for zero, a estrutura de arquivos do kernel será liberada.

Não há chance de um processo liberar um recurso que outro processo está usando porque os recursos compartilhados são contados como referência.

David Schwartz
fonte
Tenho uma ligeira dificuldade em entender a terminologia em sua resposta. Eu estou supondo que o ponteiro do arquivo significa "deslocamento do arquivo". É isso que você quis dizer? Também o que você quis dizer com identificador de arquivo ?
28714 Geek
Correto, por "deslocamento de arquivo", quero dizer o deslocamento no qual uma leitura ou gravação subsequente ocorreria. Um "identificador de arquivo" é um link entre um processo e uma descrição de arquivo aberto - é o que você recebe quando obtém openêxito.
David Schwartz
6

Nesse caso, não acontecerá muita coisa. stdin, stdout e stderr tendem a ser clones do mesmo descritor de arquivo. O contador de referência para o descritor de arquivo será decrementado por um. O mesmo descritor de arquivo geralmente é mantido pelo shell no qual o programa foi executado, portanto, o descritor de arquivo precisa ser mantido.

O kernel mantém contagens de referência para todos os arquivos (inodes) abertos. Enquanto a contagem de referência for maior que zero, o arquivo será mantido. Eu esperaria que um contador separado fosse mantido para identificadores de arquivos abertos. Quando isso atinge zero, o kernel pode liberar a memória usada pelo identificador de arquivo.

Quando todas as referências ao arquivo (entradas de diretório e identificadores de arquivo) forem removidas, o código do sistema de arquivos marcará o inode para reutilização. Quaisquer blocos que o arquivo tenha são disponibilizados para alocação. Muitos sistemas de arquivos limparão os ponteiros de bloco no inode quando ele for liberado. Isso dificulta a recuperação de um arquivo excluído. As atualizações no disco podem ser armazenadas em buffer e concluídas posteriormente.

BillThor
fonte
11
Duas perguntas: (1) os descritores de arquivo são realmente recontados? Quando você controla-d a cat > some.file, o gato recebe um EOF no stdin, mas o shell não. (2) Por que contar a referência? Por que não alguma forma de coleta de lixo? O GC não é muito melhor no espaço do usuário?
precisa
Expandindo a resposta do BillThor: Em casos normais, stdin, stdout e stderr são apenas identificadores de arquivos abertos para um dispositivo TTY. Portanto, se você fechar o identificador de arquivo, esse dispositivo TTY ainda estará lá e poderá ser reaberto novamente mais tarde.
28411 Patrick
11
@BruceEdiger: (1) quando o shell executa o cat > some.fileque realmente está fazendo, bifurcando, abrindo 'some.file' e atribuindo-o ao descritor de arquivo 1, e o faz exec("cat"). Quando um processo é exec () 'd, ele herda os descritores de arquivo aberto.
28411 Patrick
@BruceEdiger (2) A contagem de referência é uma forma perfeitamente fina de coleta de lixo quando usada em estruturas de dados que não contêm ponteiros para (ou cadeias de ponteiros terminando em) outras estruturas de dados do mesmo tipo. Além disso, isso está acontecendo no espaço do kernel (não que isso importe muito).
Gilles 'SO- deixa de ser mau'