Descritores de arquivos em exec

5

Por padrão, os descritores de arquivos permanecem abertos nas funções exec. O benefício talvez seja compreensível para os descritores 0-2. Mas existe um caso de uso prático para manter outros descritores abertos? Existem aplicações reais que dependem desse fato?

    
por r.v 09.12.2015 / 21:17

5 respostas

8

Há um sinalizador que você pode definir em um descritor de arquivo (sobre open() : O_CLOEXEC ou mais tarde com fcntl() : FD_CLOEXEC) se você não quiser que o fd seja transmitido aos comandos executados.

Isso é o que você deve fazer para seus descritores de arquivos internos se for executar comandos.

Em shells, é isso que o ksh93 faz quando você faz exec 3< some-file , por exemplo. Para outros shells ou fds abertos com { cmd1; cmd2; } 3< file , você precisará fechar a mão se não quiser que cmd1 ou cmd2 acesse o fd: {cmd1 3<&-; cmd2; } 3< file . Essa é uma boa prática, mas nem sempre é seguida, já que geralmente não é crítico se você não fizer isso .

Agora, se o recurso é útil. Sim, vários comandos dependem disso.

Alguns comandos usam um descritor de arquivo como argumento que deve ter sido aberto por um chamador. Alguns exemplos que vêm à mente:

  • xterm com sua opção -S
  • qemu para várias coisas
  • flock (para bloquear um arquivo no fd do chamador)
  • o comando test também conhecido como [ para sua opção -t (OK, o utilitário test é construído na maioria das shells parecidas com Bourne atualmente, mas ainda há um comando test que pode ser executado).
  • dialog precisa de descritores de arquivo para entrada do usuário, saída e erro para o usuário e entrada e saída para o chamador, para que você possa usar fds extras para isso.
  • gpg ou openssl para o qual você pode especificar um descritor de arquivo para comunicar a frase secreta ou outras informações.

Existem vários utilitários helper (por exemplo, a necessidade de executar pode ser executar uma parte de um comando como um usuário ou grupo diferente usando um setuid / setgid executable) que dependem disso.

A substituição do processo depende disso:

Em, diff <(cmd1) <(cmd2) , 2 descritores de arquivo (para pipes) são passados para diff e diff os acessa abrindo-os através do especial / dev / fd / n passado como argumento.

Para shells que não têm substituição de processo, você faz o mesmo com coisas como:

cm1 | { cmd2 | diff /dev/fd/3 -; } 3<&0
    
por 09.12.2015 / 22:14
3

Por um lado, a implementação de pipelines UNIX depende da herança do descritor de arquivos.

Além disso, muitos programas e scripts no Unix criam bandos de processos que precisam compartilhar canais de comunicação (como arquivos de log (às vezes você precisa de mais níveis que stdout e stderr ) ou FIFOs para IPC).

Eles poderiam passar explicitamente os nomes dos sistemas de arquivos desses canais (possivelmente incluindo os modos de abertura recomendados como ass append , somente leitura ou leitura-gravação ) ou poderiam explicitamente passar essa informação através do ambiente.

Usar um filedescriptor compartilhado deve ser mais eficiente (o que é um argumento fraco), é mais fácil acertar, mas o mais importante, às vezes é indispensável, como quando:

  1. o processo filho é executado como um usuário diferente que não tem permissões para abrir o arquivo (isso é grande).
  2. o canal a compartilhar não tem um nome de sistema de arquivos (por exemplo, é um canal ou um fifo que tenha sido desvinculado (= excluído)).
por 09.12.2015 / 22:33
2

O TinyMUSH e provavelmente muitos de seus bancos de dados de irmãos e filhos usam essa funcionalidade do exec com grande efeito. Pode-se emitir um comando para reiniciar o servidor, possivelmente atualizando para um binário totalmente novo, deixando os usuários conectados.

Isso é feito escrevendo um pequeno banco de dados de informações sobre cada usuário conectado, incluindo o descritor de arquivo. A cópia recém-editada do TinyMUSH lê o banco de dados de reinicialização para restaurar seu conhecimento dos usuários conectados e continuar de onde parou.

Resultado final: novos recursos serão liberados com apenas uma breve pausa visível para os usuários.

O Nginx faz algo semelhante para fazer atualizações binárias sem perder conexões.

    
por 10.12.2015 / 04:18
1

Os conectores conectados podem ser transmitidos para um processo filho dessa maneira, portanto, por exemplo, um servidor de rede que aceita conexões de entrada pode passar a manipulação deles para outro processo completamente.

Veja o código fonte do inetd , para um exemplo onipresente.

    
por 09.12.2015 / 21:32
-1

Eu não acho que existam programas genéricos reais que passem além do descritor 2. Se isso acontecer, o programa exec () ed terá que assumir números de descritores de arquivo codificados, por exemplo,

write(3, ...);

ou

fp = fdopen(3, "r");

que é uma má prática de codificação. Apenas nos casos em que você controla rigidamente o programa pai e filho, e ninguém mais pode intervir, pode fazer sentido.

    
por 09.12.2015 / 21:51