2015-04-01 13 views
6

Sto scrivendo un driver del kernel linux e per ogni funzione che invia i dati nello spazio utente o legge i dati dallo spazio utente, sto usando copy_to_user() e copy_from_user(). La mia domanda è: devo usare queste chiamate se sto semplicemente copiando un tipo di dati di base come un u32 o un int?copy_to_user() e copy_from_user() per il tipo di dati di base

+0

C'è una certa confusione riguardo a quello che stai chiedendo. Sarebbe utile una dichiarazione di funzione di esempio, che mostri se stai passando un 'int' o un puntatore a' int'. Le due risposte pubblicate finora si basano su due diverse interpretazioni della tua domanda. –

risposta

7

Se la funzione riceve un puntatoreai dati spazio utente, è necessario utilizzare copy_from_user() copiare i punta-ai dati dallo spazio utente allo spazio del kernel (e viceversa).

Si noti che il valore del puntatore stesso viene passato per valore (come tutti i parametri C), quindi non è necessario fare un copy_from_user() per ottenere il valore del puntatore prima che sia possibile copy_from_user() i dati a cui punta.

Gli argomenti numerici funzionano allo stesso modo degli argomenti del puntatore; in termini C, sono entrambi scalari. Non è necessario utilizzare copy_from_user() per copiare il valore del parametro; questo è già stato copiato. Devi solo usarlo per copiare i dati puntati da un puntatore passato.

Quindi se si dispone di un parametro di tipo int, è possibile utilizzarlo direttamente. Se il tuo parametro punta a un int, l'oggetto int sarà nello spazio utente e devi utilizzare copy_to_user per copiare il valore di tale oggetto nello spazio del kernel.

+0

Non sono d'accordo con te. supponiamo che un puntatore nello spazio utente non sia allineato alle pagine e che sia necessario assemblare entrambe le parti del puntatore per completarlo? L'unica cosa che l'utente riceve già come parametro per la chiamata è il puntatore dell'utente, ma se punta a un altro puntatore utente, quel puntatore deve essere acquisito con user_copy. In ambienti a 64 bit, un puntatore dell'utente ha una larghezza di 8 byte e può essere diviso in un limite di pagina, quindi sarà necessario accedere alle tabelle di pagine virtuali per tradurlo dallo spazio utente al kernel. Inoltre, le pagine dello spazio utente possono essere scambiate, quindi fai attenzione anche a questo. –

+0

Quando dici che il puntatore non è allineato alla pagina, vuoi dire che l'oggetto puntatore stesso è disallineato o i dati a cui punta? I puntatori sono scalari e vengono sempre passati per valore. Se una chiamata di sistema ha, per esempio, un parametro 'void *', la funzione kernel che gestisce la chiamata di sistema riceve una * copia * del puntatore; l'allineamento dell'oggetto puntatore sul lato del chiamante è irrilevante. –

+0

Per i puntatori di tipi di dati semplici (come 'int *'), 'put_user' e' get_user' possono anche essere usati. – holgac

2

Quando un utente trasferisce i dati nello spazio del kernel, questi dati possono essere suddivisi su più pagine e tali pagine possono essere anche nella memoria sostituita. In questi casi, dovrai aspettare che il kernel si sposti nella pagina e accedere alla pagina in cui si trovano i dati. Nel caso di tipi di dati elementari (come int o puntatori) è anche vero che alcune architetture (in particolare x86 intel) non forzare l'utente ad allineare i dati in modo che anche un intero possa essere diviso attorno al bordo di una pagina. Puoi avere accesso alla prima parte del tuo numero intero, ma aspettare che il secondo sia scambiato dal gestore della memoria prima che l'intera cosa sia accessibile.

È possibile salvare alcuni roundtrip inserendo tutti i dati utente in una struttura il cui puntatore viene passato al kernel. È possibile copy_from_user come un blocco e salvare accessi (e correre in rischio di essere bloccato più volte)

Così, e come una conclusione, utilizzare le funzioni anche per i tipi di base, come ci sono un sacco di loro. Non assumere nulla su dove possono essere i dati dell'utente quando si esegue in modalità kernel. Puoi accedervi, ma gli indirizzi virtuali del kernel dei dati utente non hanno nulla a che fare con gli indirizzi virtuali visti in modalità utente.

+0

Luis: Penso che tu abbia interpretato male l'OP. Tutto ciò che l'OP sta cercando di fare è passare un singolo valore di un tipo di dati di base. L'OP sta davvero chiedendo se è possibile passare un dato base per valore invece che per riferimento. Come @Keith stava indicando, normalmente viene passato un puntatore e questo puntatore viene passato per valore in modo che sia possibile accedervi direttamente. Tuttavia, se si desidera trasferire il puntatore all'utente, è necessario utilizzare la funzione copy_from_user() o get_user() nel kernel. –

+0

Mi dispiace, ma come dice lui: * se sto semplicemente copiando un tipo di dati di base come un u32 o un int * che significa copiare, quindi suppongo che l'int sia passato per riferimento (per restituire un valore, ad esempio) Naturalmente che se sta passando un valore solo al kernel (ma non torna allo spazio utente), può farlo in questo modo. Non sto pensando a quello di ioctl, è qui che ha senso. Supponiamo che stia provando a "int a; scrivi (fc, & a, sizeof a); '... –

+0

Ovviamente puoi ** usare ** il puntatore passato allo spazio del kernel in un ** write (2) ** come dati, ma non ha molto senso fare un autista come questo. Dice * ... e per ogni funzione ... * –

Problemi correlati