2010-01-20 8 views
6

Próbuję przydzielić trochę pamięci dla znaku * w następujący sposób.Jak mogę przydzielić pamięć w jądrze Linuxa na ciąg znaków typu char *?

static ssize_t memo_write(struct file *filp, const char __user *buf, 
    size_t count, loff_t *f_pos){ 
    ssize_t retval = -ENOMEM; 
    printk("write function\n"); 

    if((data = kmalloc(strlen(buf), GFP_KERNEL)) == NULL) 
     printk("kmalloc fail\n"); 

    if(copy_from_user(data, buf, strlen(buf))){ 
     retval = -EFAULT; 
     goto out; 
    } 
    *f_pos += strlen(buf); 
    retval = strlen(buf); 

    out: 
     return retval; 
} 

„dane” jest zadeklarowana w pliku nagłówkowym jak

char *data; 

Kiedy wywołać funkcję zapisu, „kmalloc upaść” linia nie zostanie osiągnięta, który prowadzi mnie do uwierzyć, że udało kmalloc jednak dane nie są wyświetlane, gdy ponownie spróbuję odczytać zmienną "data".

Co bardziej mylące, jeśli pozbywam się w całości kawałka kmalloc, dane można odczytać ze sterownika. Mimo to problem polega na tym, że następuje po nim ładunek innych danych, ponieważ nie mam możliwości jego wczytania().

Czy poprawnie używam kmalloc? Prawdopodobnie nie. Jak powinienem to robić?

Dodatkowo moja funkcja odczytu wygląda następująco.

static ssize_t memo_read(struct file *f, char __user *buf, 
    size_t count, loff_t *f_pos){ 
    ssize_t retval = 0; 

    printk("read function\n"); 
    printk("data = %s\n", data); 

    if(*f_pos >= strlen(data)){ 
     printk("EOF\n"); 
     goto out; 
    } 

    if(copy_to_user(buf, data, strlen(data))){ 
     retval = -EFAULT; 
     goto out; 
    } 
    printk("copy_to_user success\n"); 
    *f_pos += strlen(data); 
    retval = strlen(data); 
    out: 
     return retval; 
} 

Dzięki.

+0

Dzięki zarówno dla tych wielkich odpowiedzi, to naprawdę wielka pomoc! Oznaczałbym je obie jako wybraną odpowiedź, gdybym mógł, chociaż wybiorę kawiarnię, ponieważ zawierał blokadę i ma mniejszą reputację: p Jeszcze raz dziękuję! – cheesysam

Odpowiedz

9

należy używać strlen_user() na wskaźniku w przestrzeni użytkownika, zamiast strlen() - i należy wywołać tylko raz, i zachować wynik ok (poza tym, że masz potencjał jądro wykorzystać, bo druga nitka Strefa może zmienić bufor podczas pracy nad nim).

Alternatywnie można użyć strncpy_from_user().

Poza tym kmalloc wygląda OK.


(Ale tak naprawdę, jak ephemient mówi, należy przemyśleć całe swoje podejście i używać count argumentu zamiast leczenia wejście jako ciąg).


Ponieważ nie można polegać na danych zapisanych w pliku jako ciągi znakiem NUL, trzeba zachować parametr data_len długości około obok data. Wtedy twoje read/write implementacje byłoby wzdłuż tych linii:

static char *data = NULL; 
static size_t data_len; 
static DEFINE_MUTEX(data_mutex); 

static ssize_t memo_read(struct file *f, char __user *buf, size_t count, loff_t *f_pos 
{ 
    ssize_t retval = 0; 
    char *start; 

    mutex_lock(&data_mutex); 

    if (!data) 
    { 
     retval = -EINVAL; /* Or whatever you want to do here... */ 
     goto out; 
    } 

    if (*f_pos >= data_len) 
     goto out; /* EOF */ 

    start = data + *f_pos; 
    retval = data_len - *f_pos; 

    if (retval > count) 
     retval = count; 

    if (copy_to_user(buf, start, retval)) 
    { 
     retval = -EFAULT; 
     goto out; 
    } 

    *f_pos += retval; 

out: 
    mutex_unlock(&data_mutex); 
    return retval; 
} 

static ssize_t memo_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) 
{ 
    ssize_t retval = -ENOMEM; 

    mutex_lock(&data_mutex); 

    if (data) 
     kfree(data); 

    data = kmalloc(count, GFP_KERNEL); 

    if (!data) 
     goto out; 

    if (copy_from_user(data, buf, count)) 
    { 
     kfree(data); 
     retval = -EFAULT; 
     goto out; 
    } 

    *f_pos = count; 
    retval = count; 
    data_len = count; 

out: 
    mutex_unlock(&data_mutex); 
    return retval; 
} 
+0

Pomyślałem o tym, ale przypuszczalnie jest to w procederze 'write' ... dlaczego więc nie użyć argumentu 'count'? Nie powinno być gwarancji, że 'buf' jest zakończone NUL. – ephemient

+1

Całkiem prawdziwa - zaktualizowałem odpowiedź, aby się z tym zgodzić (ale nie usunąłem odpowiedzi, ponieważ uważam, że inne informacje, które dodałem, są niezależnie przydatne). – caf

+0

Teraz, gdy już o tym wspominałeś, użycie argumentu zliczania ma więcej sensu. – cheesysam

4

Nie zapomnij kfree(data) w swoich sprawach o błędach ...

w każdym przypadku, buf jest wskaźnik do pamięci użytkownika, więc nie nazywaj strlen(buf). Najpierw musisz: copy_from_user. Dlaczego nie

data = kmalloc(count); 
copy_from_user(data, buf, count); 

?


Twój obsługi odczytu zakłada, że ​​data jest znakiem NUL łańcuch. Kiedy korzystałeś z tablicy, mogło to być prawdą przypadkowo, ale nigdy nie zapewniasz tego w swoim programie obsługi zapisu. Domyślam się, że copy_to_user zawodzi.

Oto przykład pracy z „memo” modułu, który napisałem się właśnie teraz, używając kmalloc:

#include <linux/fs.h> 
#include <linux/miscdevice.h> 
#include <linux/module.h> 
#include <linux/types.h> 
#include <linux/uaccess.h> 

static char *data; 
static size_t len; 

static ssize_t 
memo_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 
{ 
     ssize_t copy_len = min(len - min(len, *ppos), count); 
     ssize_t retval; 

     if (copy_to_user(buf, data + *ppos, copy_len)) { 
       retval = -EFAULT; 
       goto out; 
     } 

     *ppos += copy_len; 
     retval = copy_len; 

out: 
     return retval; 
} 

static ssize_t 
memo_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) 
{ 
     ssize_t retval; 
     char *newdata; 

     newdata = kmalloc(count, GFP_KERNEL); 
     if (!newdata) { 
       retval = -ENOMEM; 
       goto out; 
     } 

     if (copy_from_user(newdata, buf, count)) { 
       retval = -EFAULT; 
       goto out; 
     } 

     kfree(data); 
     data = newdata; 
     newdata = NULL; 
     retval = len = count; 

out: 
     kfree(newdata); 
     return retval; 
} 

static const struct file_operations memo_fops = { 
     .owner = THIS_MODULE, 
     .llseek = no_llseek, 
     .read = memo_read, 
     .write = memo_write, 
}; 

static struct miscdevice memo_misc = { MISC_DYNAMIC_MINOR, "memo", &memo_fops }; 

static int __init memo_init(void) 
{ 
     int result; 

     result = misc_register(&memo_misc); 
     if (result < 0) 
       return -ENODEV; 

     return 0; 
} 

static void __exit memo_exit(void) 
{ 
     misc_deregister(&memo_misc); 
     kfree(data); 
     return; 
} 

module_init(memo_init); 
module_exit(memo_exit); 
MODULE_AUTHOR("ephemient"); 
MODULE_LICENSE("GPL"); 

Oczywiście brakuje blokowanie i inne środki ostrożności, ale mam nadzieję, że to pomaga.

+0

Próbowałem tego, jednak mam ten sam problem. Wszystko, co piszę do mojego sterownika, nie jest odzwierciedlone, kiedy używam "cat/dev/myDriver", aby zobaczyć, co tam jest. Nawet jeśli tak się stanie, jeśli całkowicie pozbyłem się tego, co się dzieje na wyspie. – cheesysam

+0

Prawdopodobnie istnieje również błąd w kodzie odczytu, działa on tylko przez przypadek - możesz zaktualizować swoje pytanie także za pomocą implementacji odczytu. – caf

+0

OK, dziękuję, proszę :) – cheesysam

Powiązane problemy