it-swarm.com.de

Loff_t * offp für Dateioperationen verstehen

Ich entwerfe einen Gerätetreiber, der einfach einen Zeichenpuffer liest und schreibt. Meine Frage betrifft jedoch die beiden Funktionen in der file_operations-Struktur read und write. Ich verstehe nicht wirklich, was loff_t *offp wirklich ist. Ich weiß, dass *offp sowohl für die Lese- als auch für die Schreiboperation der Datei-Offset ist, der die aktuelle Lese-/Schreibposition der Datei angibt. Ich bin jedoch nicht einmal sicher, was es bedeutet, in eine Gerätedatei zu schreiben oder aus einer Gerätedatei zu lesen.

Aus dem, was ich gesammelt habe, schreibe und lese ich von meinem Gerät aus, dass ich eine Struktur erstelle, die mein Gerät darstellt, das ich my_char_struct nenne, und das unten angezeigt wird.

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

Dies ist eine statische Struktur, die initialisiert wird und darauf verweist, wenn mein Treiber insmod ist. 

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);

Wenn mein Gerät geöffnet ist, zeige ich einfach einen Zeiger für die geöffnete Datei, um auf die statische Struktur zu verweisen, die ich in module_init(start_mod) als solche eingerichtet habe.

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;
}

Meine Lese- und Schreibmethoden ändern die ursprüngliche Struktur Dev, auf die ich mit meinen geöffneten Dateien hingewiesen habe. Was auch immer ich copy_to_user von meiner Struktur halte, ist das, was der Benutzer als auf das Gerät geschrieben betrachtet, und was auch immer ich copy_from_user meint, der Benutzer denkt, dass sie schreiben. Abgesehen von der Änderung meiner ursprünglichen Struktur Dev ist die Idee der Dateiposition oder des Versatzes nicht sinnvoll, es sei denn, sie bezieht sich auf einen Zeiger auf einen gepufferten Speicher im Kernel für eine beliebige Struktur oder einen beliebigen Typ. Das ist die einzige Interpretation, die ich für den Datei-Offset habe ... ist das richtig? Ist das, worauf sich der loff_t *offp bezieht?

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)

(Wenn mein Verständnis richtig ist) Wenn eine file_operation wie Lesen/Schreiben aufgerufen wird und ich *offp nicht persönlich festgelegt habe, was ist loff_t * offp anfangs auf? 

Wenn in der letzten file_operation offp = some_arbitrary_address (weil ich es so gesagt habe), wird dann das offp gesetzt, wenn diese Operation erneut aufgerufen wird? 

Was passiert, wenn andere file_opens -Operationen ausgeführt werden, wird dies so festgelegt, wie die letzte file_operation als belassen war, oder wird ein Register der file_open -Operation, die verwendet wurde, gespeichert und * offp wird durch das ersetzt, in dem die file_open es hatte? 

Das Konzept eines Char-Geräts ist für mich zu abstrakt, wenn es so aussieht, als würde das Gerät selbst die Informationen nicht wie eine Datei speichern, sondern eher der Treiber, der die Informationen speichert. Ich hoffe, ich habe meine Benommenheit erklärt und werde alles klären, worüber ich mehrdeutig bin.

18
Dr.Knowitall

"loff_t" ist ein "langer Versatz", d. h. eine Suchposition, die die verrückte Vielfalt von off_t, off64_t usw. vereint, so dass Fahrer loff_t nur verwenden können, ohne sich darum zu kümmern.

Der Zeiger selbst zeigt zu dem Zeitpunkt, zu dem Sie in den Treiber gelangen, auf den vom Benutzer bereitgestellten Offset (vorausgesetzt, der Benutzercode wird für den Treiberzugriff verwendet. Technisch kann der Kernel seinen eigenen zur Verfügung stellen, aber der Benutzerfall ist der, über den man nachdenken muss). über lseek oder llseek oder lseek64 usw. und dann durch normale Lese- und Schreiboperationen. Betrachten Sie den Fall einer regulären Datei auf der Festplatte: Wenn Sie open zuerst die Datei aufrufen, erhalten Sie (als Benutzer) vom Kernel eine Datenstruktur, die Ihre aktuelle Position in der Datei aufzeichnet. Wenn Sie read oder write In einigen Bytes wird die nächste read oder write an der Stelle fortgesetzt, an der Sie aufgehört haben.

Wenn Sie dup den Dateideskriptor verwenden oder das Äquivalent von (z. B. fork und exec) ausführen, um eine Befehlsfolge auszuführen, wird diese Suchposition allen erbenden Prozessen gemeinsam verwendet. Daher bei der Shell-Eingabeaufforderung der Befehl:

(prog1; prog2; prog3) > outputfile

erstellt eine Ausgabedatei, dann dups der Deskriptor für die drei Programme, so dass die Ausgabe, die prog2 schreibt, unmittelbar nach der Ausgabe von prog1 in die Datei eingeht und die Ausgabe von prog3 den anderen beiden folgt Kerndatenstruktur mit demselben internen loff_t.

Gleiches gilt für Gerätetreiberdateien. Wenn Ihre Lese- und Schreibfunktionen aufgerufen werden, erhalten Sie den "aktuellen Offset", wie vom Benutzer angegeben, und Sie können (und sollten) ihn bei Bedarf aktualisieren das Aussehen einer regulären Datei, einschließlich der Tatsache, dass Such-Offsets beim Lesen und Schreiben verschoben werden). Wenn das Gerät eine logische Anwendung des Suchoffsets hat, können Sie dies hier verwenden.

Natürlich gibt es noch viel mehr Gerätetreiber, weshalb es ganze Buchkapitel zu diesem Zeug gibt (q.v.). :-)

20
torek

Toreks Antwort ist ausgezeichnet. Fügen Sie einfach ein wenig mehr Detail/Kontext hinzu ... Aus einem früheren Linux-Kernel (2.6.28) folgt hier ein Beispiel eines Versatzes, der in einem Systemaufruf verwendet wird. Er kopiert den Versatz aus dem Benutzerraum in eine temporäre Variable, bevor er abgerufen wird in den Kernel-Treiberaufrufmechanismus und kopiert ihn dann wieder in die Benutzerdatei. Auf diese Weise wird der Offset, den der Fahrer sieht, von der Benutzersicht entkoppelt, und er erleichtert die Situationen, in denen Offset im Systemaufruf NULL ist, sodass kein SEGVIO auftritt.

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);
}
0
clearlight