Qual é a motivação para converter um ponteiro em um inteiro?

5

Estou fazendo algumas alterações no código do kernel do Linux e notei que um ponteiro está sendo convertido em inteiro.

Confira buf abaixo ( código completo ):

snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size)
{
    ....
    return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);
}

Ele é coberto de void __user *buf a (unsigned long)buf . Posteriormente, no código buf é tratado como long e não como um ponteiro.

Embora nos níveis técnicos sejam ambos números na memória, até agora eu percebi conceitualmente como coisas diferentes. Existe um padrão de baixo nível quando isso é usado?

    
por TheMeaningfulEngineer 23.07.2015 / 10:03
fonte

1 resposta

8

Um caso de uso típico (em aplicativos hospedados de código de usuário) para converter algum ponteiro para intptr_t (de <stdint.h> padrão C99 ou C11 header) é computar algum código hash nesse ponteiro:

uint32_t hash_of_foo_ptr(struct foo_st*foo) {
   return (uint32_t) (((intptr_t)foo)*613) ^ ((intptr)foo%51043));
}

Por motivos históricos (por exemplo, o Linux veio antes de C99), o kernel Linux usa unsigned long em vez de uintptr_t (o tipo integral não assinado do mesmo sizeof como ponteiros) ou intptr_t

Além disso, o espaço do usuário para as transmissões no espaço do kernel (por exemplo, argumentos do syscalls) está no kernel do Linux em termos de long ou unsigned long (novamente, é melhor pensar em intptr_t ou% código%). Mesmo quando você transmite endereços do espaço do usuário para o espaço do kernel, é necessário pensar nos espaços de endereço e na memória virtual (e está se tornando complexo, já que o espaço do usuário e o código do kernel residem em diferentes espaços de endereço).

Uma citação sobre o tema de LDD3 :

Although, conceptually, addresses are pointers, memory administration is often better accomplished by using an unsigned integer type; the kernel treats physical memory like a huge array, and a memory address is just an indexinto the array. Furthermore, a pointer is easily dereferenced; when dealing directly with memory addresses, you almost never want to dereference them in this manner. Using an integer type prevents this dereferencing, thus avoiding bugs. Therefore, generic memory addresses in the kernel are usually unsigned long , exploiting the fact that pointers and long integers are always the same size, at least on all the platforms currently supported by Linux.

Note que o kernel do Linux não é codificado em C99 portável padrão academicamente hospedado. É um programa independente, codificado com alguns compiladores particulares C em mente ( GCC e talvez também no futuro Clang / LLVM ...).

O kernel do Linux está usando algumas extensões do GCC para C (por exemplo, uintptr_t -s , builtins, computado goto-s, etc ...), talvez embrulhado com macros. A maioria dessas extensões também é suportada pelo Clang / LLVM .

Novatos do Kernel & lkml são provavelmente bons lugares para perguntar sobre isso.

    
por 23.07.2015 / 10:33
fonte