1) Se o sistema suportar a abertura do mesmo arquivo a partir de vários processos ao mesmo tempo, por que não permitir que um processo seja aberto várias vezes também? Como os descritores de arquivo são herdados, você pode acabar com o mesmo arquivo duas vezes no mesmo processo, ou seja, se ele for herdado uma vez e depois de aberto pelo próprio processo. Verificar quais arquivos o processo abriu e retornar uma referência para o anterior seria um trabalho extra.
Além disso, há a questão se diferentes partes do processo usam o mesmo arquivo simultaneamente. Digamos que uma biblioteca use algum arquivo de configuração ao mesmo tempo em que o programa principal o utiliza. O descritor de arquivo está vinculado ao modo de acesso e à posição do ponteiro do arquivo. Se houvesse apenas uma cópia deles, coisas estranhas aconteceriam. Além disso, se o arquivo foi aberto duas vezes (para o mesmo fd), o que deve acontecer quando ele é fechado? Poderia haver outra camada de contagem de referência para decidir quando realmente fechar o arquivo, mas isso não ajudaria nos outros problemas.
2) Depende do seu código. Uma linguagem inteligente (ou seja, não C), pode refazer a variável que mantém o arquivo aberto e fechá-lo antes de você reabrir o arquivo. Difícil dizer sem ver o código.
Mas um pequeno teste, abrindo o mesmo arquivo duas vezes para a mesma variável em Perl, resulta no mesmo número de FD:
perl -e 'open F, "test.txt"; printf "%d ", fileno(F); open F, "test.txt"; printf "%d\n", fileno(F)'
3 3
A execução em strace
mostra que o arquivo é fechado imediatamente antes da reabertura.
Com duas variáveis diferentes, obtemos dois números FD:
perl -e 'open F, "test.txt"; printf "%d ", fileno(F); open G, "test.txt"; printf "%d\n", fileno(G)'
3 4
3) Tecnicamente, você poderia dizer que os números de arquivo padrão são uma convenção. Uma convenção codificada em POSIX e no padrão ISO C :
At program start-up, three streams shall be predefined and need not be opened explicitly: standard input (for reading conventional input), standard output (for writing conventional output), and standard error (for writing diagnostic output).
Mas uma convenção de qualquer maneira em que pode ser possível rodar um programa sem eles, sem a atenção do kernel. Ou talvez não: ler a especificação para as chamadas exec
, parece ser permitido para uma implementação abre algo para você :
If file descriptor 0, 1, or 2 would otherwise be closed after a successful call to one of the exec family of functions, implementations may open an unspecified file for the file descriptor in the new process image.
(Você pode, claro, fechá-los no próprio programa.)
Se você optar por não tê-los na inicialização, a compatibilidade será descartada:
If a standard utility or a conforming application is executed with file descriptor 0 not open for reading or with file descriptor 1 or 2 not open for writing, the environment in which the utility or application is executed shall be deemed non-conforming, and consequently the utility or application might not behave as described in this standard.
As páginas man do Linux explicam de alguma forma :
As a general
principle, no portable program, whether privileged or not, can
assume that these three file descriptors will remain closed across
an execve().