Qual è la motivazione per lanciare un puntatore in un numero intero?

5

Sto facendo alcune modifiche nel codice del kernel di Linux e ho notato che un puntatore è stato convertito in un intero.

Controlla buf di seguito ( codice 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);
}

Viene coperto da void __user *buf a (unsigned long)buf . Successivamente nel codice buf viene trattato come long e non come puntatore.

Anche se a livello tecnico sono entrambi numeri in memoria, finora ho percepito concettualmente come cose diverse. C'è un pattern di basso livello quando viene usato?

    
posta TheMeaningfulEngineer 23.07.2015 - 10:03
fonte

1 risposta

8

Un tipico caso d'uso (in applicazioni con codice utente ospitato) per lanciare un puntatore su intptr_t (da <stdint.h> standard C99 o C11 header) è calcolare un codice hash su quel puntatore:

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

Per ragioni storiche (ad esempio Linux è venuto prima del C99), il kernel Linux usa unsigned long invece di uintptr_t (il tipo integrale non firmato dello stesso sizeof come puntatori) o intptr_t

Inoltre, lo spazio utente per le trasmissioni nello spazio del kernel (ad esempio gli argomenti delle sysc) è nel kernel di Linux in termini di long o unsigned long (anche in questo caso, è meglio pensare in termini di intptr_t o% codice%). Anche quando trasmetti gli indirizzi dallo spazio utente allo spazio del kernel, devi pensare agli spazi degli indirizzi e alla memoria virtuale (e sta diventando complesso, dal momento che lo spazio utente e il codice del kernel vivono in spazi di indirizzi diversi).

Una citazione sull'argomento da 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.

Si noti che il kernel Linux non è codificato in C99 portatile standard accademicamente ospitato. È un programma indipendente, codificato con alcuni compilatori particolari C in mente ( GCC e forse anche in futuro Clang / LLVM ...).

Il kernel Linux sta usando alcune estensioni GCC a C (es. uintptr_t -s , builtins, goto-s computati, ecc ...), magari avvolti con macro. Molte di queste estensioni sono supportate anche da Clang / LLVM .

Kernel newbies & lkml è probabilmente un buon posto per chiederlo.

    
risposta data 23.07.2015 - 10:33
fonte

Leggi altre domande sui tag