2012-03-15 12 views
14

Projektuję sterownik urządzenia, który po prostu czyta i zapisuje do bufora znaków. Moje pytanie dotyczy jednak dwóch funkcji w strukturze file_operations: read i write. Naprawdę nie rozumiem, czym naprawdę jest. Wiem, że dla operacji odczytu i zapisu, *offp jest przesunięcie pliku, co oznacza bieżącą pozycję odczytu/zapisu pliku, jednak nie jestem nawet pewien, co to znaczy pisać lub czytać do/z pliku urządzenia.Zrozumienie loff_t * offp dla file_operations

Z tego, co zebrałem i tak piszę i czytam z mojego urządzenia, to tworzę strukturę, która reprezentuje moje urządzenie, które nazywam my_char_struct, co pokazano poniżej.

struct my_char_structure{ 
    struct cdev my_cdev; 
    struct semaphore sem; 
    char *data; 
    ssize_t data_size; 
    unsigned int access_key; 
    unsigned long size; 
}; 

To jest statyczną strukturę, która jest inicjowana i wskazał, kiedy mój kierowca jest insmod jako takie.

static dev_t dev_num; 
static struct my_char_structure Dev; 

int start_mod(void){ 
    //Because we are dealing with a fictitious device, I want 
    //the driver to create my two devices with arbitrarily 
    //assigned major numbers. 
    struct my_char_structure *my_dev = &Dev; 
    int err; 

    alloc_chrdev_region(&dev_num, FIRST_MINOR, COUNT, DEVICE_NAME); 

    sema_init(&(my_dev->sem),1); 

    cdev_init(&(my_dev->my_cdev), &fops); 
    my_dev->my_cdev.owner = THIS_MODULE; 
    my_dev->my_cdev.ops = &fops;// fops is my file operations struct 

    err = cdev_add(&my_dev->my_cdev, dev_num, COUNT); 
    if(err<0) 
     printk(KERN_ALERT "There was an error %d.",err); 
    printk(KERN_ALERT " insmod to major number %d",MAJOR(dev_num)); 

    return 0; 
} 

module_init(start_mod); 

gdy urządzenie jest otwarte, po prostu zrobić wskaźnik do pliku otwartego do punktu do tej statycznej struktury, która wcześniej skonfigurować podczas module_init(start_mod) jako takiego ...

int dev_open(struct inode *in_node, struct file *filp){ 
    static struct my_char_structure *my_dev; 
    my_dev = container_of(in_node->i_cdev, struct my_char_structure, my_cdev); 
    printk(KERN_ALERT "The device number is %d",iminor(in_node)); 
    if(!my_dev) 
     printk(KERN_ALERT "something didn't work. my_dev not initialized."); 
    filp->private_data = my_dev; 
    return 0; 
} 

co moje odczytywanie i zapisywanie metod to modyfikowanie początkowej struktury Dev, którą wskazałem przy pomocy moich otwartych plików. Cokolwiek I copy_to_user z mojej struktury jest tym, co użytkownik uważa za napisany na urządzeniu i czymkolwiek, co użytkownik uważa za napisane. Ale poza zmianą mojej początkowej struktury Dev, idea pozycji pliku lub przesunięcia nie ma sensu, chyba że odnosi się do wskaźnika buforowanej pamięci w jądrze dla jakiejś arbitralnej struktury lub typu. Jest to jedyna interpretacja, którą mam dla przesunięcia pliku ... czy to prawda? Czy to właśnie odnosi się do loff_t *offp?

write(struct file *filp, const char __user *buff, size_t count, loff_t *offp) 
read(struct file *filp, char __user *buff, size_t count, loff_t *offp) 

(biorąc pod uwagę moje rozumienie jest poprawne) Gdy niektóre file_operation takich jak odczytu/zapisu nazywa i nie zaszło *offp osobiście, co jest loff_t * offp początkowo ustawiony?

Jeśli w ostatnim file_operation offp = some_arbitrary_address (ponieważ tak powiedziałem), czy to, co offp byłoby ustawić, gdy ta operacja jest wywoływana ponownie?

Co się stanie, jeśli uruchomię inne operacje file_opens, czy ustawi to, co pozostawiła ostatnia operacja file_operation, czy będzie przechowywać zakładkę z której operacji file_open używał i zastąpić * offp tym, na co plik file_open miał go?

Pojęcie urządzenia char jest dla mnie zbyt abstrakcyjne, gdy wydaje się, że samo urządzenie nie zapisuje nawet informacji takich jak plik, a raczej sterownik, który zapisuje te informacje. Mam nadzieję, że wyjaśniłem moją mglistość i wyjaśnię wszystko, co wydaje mi się niejednoznaczne.

Odpowiedz

19

„loff_t” jest pozycja „long offset”, tj poszukują że jednoczy szalona różnorodność off_t, off64_t, i tak dalej, tak że kierowcy mogą po prostu użyć loff_t i nie martwić się o niego.

Sam wskaźnik, w momencie wejścia w kierowcę, wskazuje na przesunięcie dostarczone przez użytkownika (zakładając, że kod użytkownika wykonuje dostęp do sterownika - technicznie jądro może dostarczyć własne, ale przypadek użytkownika jest tym samym o tym myśleć) poprzez lseek lub llseek lub lseek64 itd., a następnie za pomocą zwykłych operacji odczytu i zapisu.Rozważmy przypadek zwykłego pliku na dysku: kiedy po raz pierwszy open plik, użytkownik (jako użytkownik) dostaje jądro, aby zapewnić strukturę danych, która śledzi aktualną pozycję w pliku, tak, że jeśli read lub write niektóre bajty, następny read lub write odbiera od miejsca, w którym zostało przerwane.

Ponadto, jeśli dup deskryptor pliku, czy równowartość przez (na przykład) fork i exec w zakresie prowadzenia sekwencję poleceń, które poszukują-pozycja jest wspólna dla wszystkich procesów dziedziczeniu. Stąd, w wierszu powłoki, polecenie:

(prog1; prog2; prog3) > outputfile 

tworzy plik wyjściowy, a następnie dup s deskryptor do trzech programów, tak że wyjściowy prog2 pisze idzie do pliku zaraz po wyjściu z prog1 i wyjście z prog3 następuje po dwóch pozostałych - wszystko dlatego, że wszystkie trzy oddzielne procesy mają tę samą wewnętrzną strukturę danych jądra z tym samym wewnętrznym loff_t.

To samo dotyczy plików sterownika urządzenia. Kiedy twoje funkcje odczytu i zapisu są wywoływane, otrzymujesz "bieżące przesunięcie" dostarczone przez użytkownika i możesz (i powinieneś) zaktualizować je w razie potrzeby ... zakładając, że jest taka potrzeba (np. Chcesz zapewnić użytkownikom pojawienie się zwykłego pliku, w tym fakt, że przesunięcia w poszukiwaniu przesuwają się podczas czytania i pisania). Jeśli urządzenie ma jakieś logiczne zastosowanie przesunięcia wyszukiwania, możesz użyć tego tutaj.

Oczywiście dla sterowników urządzeń jest znacznie więcej, dlatego na tym materiale znajdują się całe rozdziały książek (q.v.). :-)

+1

Kiedy więc zmieniam przesunięcie na offset + = bajt_read/write, wskaźnik użytkownika zostanie zmieniony, ale nie zrobi tego automatycznie? Myślę, że to dla mnie trochę wyjaśnia. Czytałem książkę wydania trzeciej wersji sterownika urządzenia linuksowego, którą wszyscy inni czytają jako intro i która miała ten diagram, w którym wskaźnik przesunięcia odwoływał się do jakiejś dziwnej abstrakcji jądra (z braku lepszego słowa), którą nazwali pozycją pliku. Dzięki za pomoc, ten rodzaj wyczyści rzeczy :) –

+2

Tak. (Przypuszczalnie masz na myśli '* offp + = nbytes'.) Zmienna, którą zmieniasz, jest w rzeczywistości rzeczą typu jądro, ale * reprezentuje * przesunięcie wyszukiwania użytkownika. (Lub, w niektórych przypadkach, przesunięcie dostarczone do wywołania "pread" lub "pwrite", lub nawet coś innego, ale najczęściej, przesunięcie użytkownika 'lseek'). To" dziwna abstrakcja jądra ", jak to nazywasz, jest tym, co sprawia, że ​​'output (prog1; prog2)> działa. Nawiasem mówiąc, w jądrach * BSD istnieje funkcja o nazwie "uiomove", która automatycznie aktualizuje "przesunięcie we/wy użytkownika"; Linux poszedł w drugą stronę. – torek

0

Torek's answer jest doskonała. Po prostu dodając nieco więcej szczegółów/kontekstu ... Z wcześniejszego jądra Linuksa (2.6.28), oto przykład przesunięcia w użyciu w wywołaniu systemowym ... kopiuje przesunięcie z przestrzeni użytkownika do zmiennej tymczasowej przed uzyskaniem do mechanizmu wywoływania sterownika jądra, a następnie kopiuje go z powrotem do pliku użytkownika. W ten sposób odsunięcie, jakie widzi kierowca, jest odłączone od widoku użytkownika i ułatwia sytuacje, w których offset jest NULL w wywołaniu systemowym, więc nie występuje SEGVIO.

SYSCALL_DEFINE4(sendfile64, int, out_fd, int, in_fd, loff_t __user *, offset, size_t, count) 
{ 
    loff_t pos; 
    ssize_t ret; 

    if (offset) { 
     if (unlikely(copy_from_user(&pos, offset, sizeof(loff_t)))) 
      return -EFAULT; 
     ret = do_sendfile(out_fd, in_fd, &pos, count, 0); 
     if (unlikely(put_user(pos, offset))) 
      return -EFAULT; 
     return ret; 
    } 

    return do_sendfile(out_fd, in_fd, NULL, count, 0); 
}