Um descritor de arquivo é um inteiro usado para referenciar um arquivo, entre todos os arquivos abertos por um determinado processo. Geralmente, isso é implementado pelos kernels considerando o descritor de arquivo como um índice em uma tabela.
O restante da minha resposta se aplica ao Linux.
No Linux, cada descritor de arquivo válido é associado a um struct file
. Essa estrutura contém um ponteiro para o inode (os dados e metadados do arquivo), a posição atual do processo no arquivo, uma lista de operações que são, na verdade, ponteiros para funções implementadas pelo sistema de arquivos em que o arquivo reside, etc. p>
Para buscar a estrutura file
do descritor de arquivo, o kernel do Linux continua da seguinte maneira. Eu tomo aqui o exemplo da chamada do sistema read
.
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
struct fd f = fdget_pos(fd);
ssize_t ret = -EBADF;
if (f.file) {
loff_t pos = file_pos_read(f.file);
ret = vfs_read(f.file, buf, count, &pos);
if (ret >= 0)
file_pos_write(f.file, pos);
fdput_pos(f);
}
return ret;
}
A primeira operação é fdget_pos
. Ele toma como parâmetro o descritor de arquivo do chamador no userspace e busca o file
correspondente. Ele retorna um struct fd
definido da seguinte forma:
struct fd {
struct file *file;
unsigned int flags;
};
Isso é basicamente um struct file
, com alguns sinalizadores para lembrar quais operações serão necessárias para recuperar a estrutura.
Agora, como o fdget_pos
funciona? Na verdade, é intrincado de formas estranhas, mas tudo se resume a duas operações básicas (com mais verificações que não mostro aqui por simplicidade):
O primeiro consiste em buscar a tabela de arquivos do processo. Esta tabela está disponível a partir de um ponteiro na estrutura do processo do chamador (acessível através de current
):
struct files_struct *files = current->files;
A próxima operação consiste em verificar a validade do descritor de arquivo:
if (fd < files->fdt->max_fds) // first of all, if the file descriptor is too big, then it cannot be valid
return files->fdt->fd[fd]; // otherwise, we return the pointer stored in the table of file descriptors (may be NULL)
return NULL;
O ponteiro pode ser eliminado antes que a função retorne (se um thread do processo fizer um read
e outro close
no mesmo descritor de arquivo ao mesmo tempo, por exemplo). O kernel cuida disso.
Se o ponteiro struct file
retornado por fdget_pos
for NULL
, significa que o descritor de arquivo passado para a chamada do sistema é inválido. Nesse caso, a chamada do sistema retorna o código de erro EBADF
("bad file descriptor").
Para resumir, os descritores de arquivos são apenas índices em uma tabela por processo de descritores de arquivos. No entanto, não é suficiente apenas desreferencia-los, pois a entrada na tabela de arquivos pode ser NULL
. Além disso, o kernel deve fazer verificações adicionais para lidar com condições de corrida no descritor de arquivo.