Usando um descritor de arquivo em uma chamada do sistema [closed]

0

Vamos supor que eu queira usar um descritor de arquivo em uma chamada de sistema (o número fd seria fornecido através de um parâmetro). O que é esperado se um programa de espaço do usuário usar essa chamada de sistema? Onde o sistema operacional procuraria esse fd específico? Nos descritores de arquivos do processo atual ou em outro lugar?

Abaixo, tentei ilustrar isso.

+--------------+     +----++--------------+
| Kernel space |     | fd ||  User space  |
|              |     |list||              |
|   handler <---------------- syscall(fd) |
|              |     |    ||              |
+--------------+     +----++--------------+
    
por Iulian Paun 28.03.2017 / 17:43

1 resposta

2

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.

    
por 29.03.2017 / 13:55

Tags