Verstehen von loff_t *offp für file_operations

Lesezeit: 7 Minuten

Benutzer-Avatar
Dr.Knowitall

Ich entwerfe einen Gerätetreiber, der einfach einen Zeichenpuffer liest und schreibt. Meine Frage bezieht sich jedoch auf die beiden Funktionen in der file_operations Struktur read und write. Ich verstehe nicht wirklich was loff_t *offp wirklich ist. Ich kenne das sowohl für die Lese- als auch für die Schreiboperationen *offp ist der Dateioffset, der die aktuelle Lese-/Schreibposition der Datei bedeutet, aber ich bin mir nicht einmal sicher, was es bedeutet, in eine Gerätedatei zu schreiben oder daraus zu lesen.

Aus dem, was ich gesammelt habe, und so schreibe und lese ich von meinem Gerät, erschaffe ich eine Struktur, die mein Gerät darstellt, das ich anrufe my_char_struct was unten gezeigt 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 auf die verwiesen wird, wenn mein Treiber vorhanden ist insmod als solche.

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, erstelle ich einfach einen Zeiger für die geöffnete Datei, um auf die statische Struktur zu verweisen, die ich währenddessen eingerichtet habe module_init(start_mod) als solche …

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 diese anfängliche Struktur Dev, auf die ich mit meinen geöffneten Dateien verwiesen habe. Was immer ich copy_to_user Aus meiner Struktur ist das, was der Benutzer als auf das Gerät geschrieben betrachtet und was auch immer ich copy_from_user der Benutzer denkt, dass er schreibt. Abgesehen davon, dass ich meine ursprüngliche Struktur Dev geändert habe, macht die Idee der Dateiposition oder des Offsets keinen Sinn, es sei denn, sie bezieht sich auf einen Zeiger auf den gepufferten Speicher innerhalb des Kernels 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 was loff_t *offp bezieht sich hier auf?

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)

(vorausgesetzt, mein Verständnis ist richtig) Wenn eine Dateioperation wie Lesen / Schreiben aufgerufen wird und ich sie nicht eingestellt hatte *offp persönlich, was ist loff_t *offp anfangs eingestellt?

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

Was passiert, wenn andere file_opens-Operationen ausgeführt werden, wird es auf das gesetzt, was die letzte file_operation verlassen hat, oder wird es einen Überblick darüber behalten, welche file_open-Operation es verwendet hat, und *offp durch das ersetzen, wo file_open es hatte?

Das Konzept eines Zeichengeräts ist mir zu abstrakt, wenn es scheint, dass das Gerät selbst die Informationen nicht einmal wie eine Datei speichert, sondern der Treiber, der die Informationen speichert. Ich hoffe, ich habe meine Nebelhaftigkeit erklärt und werde alles klären, was mir zweideutig erscheint.

“loff_t” ist ein “langer Offset”, also eine Suchposition, die die verrückte Vielfalt vereint off_t, off64_tund so weiter, sodass Fahrer einfach loff_t verwenden können und sich keine Gedanken darüber machen müssen.

Der Zeiger selbst zeigt zu dem Zeitpunkt, an dem Sie in den Treiber einsteigen, auf den vom Benutzer bereitgestellten Offset (vorausgesetzt, der Benutzercode übernimmt den Treiberzugriff – technisch gesehen kann der Kernel seinen eigenen bereitstellen, aber der Benutzerfall ist derjenige, über den Sie nachdenken sollten). über lseek oder llseek oder lseek64usw. und dann durch gewöhnliche Lese- und Schreiboperationen. Betrachten Sie den Fall einer regulären Datei auf der Festplatte: Wenn Sie zuerst open der Datei erhalten Sie (als Benutzer) den Kernel, um eine Datenstruktur bereitzustellen, die Ihre aktuelle Position in der Datei verfolgt, sodass Sie, wenn Sie read oder write einige Bytes, die nächste read oder write macht dort weiter, wo Sie aufgehört haben.

Außerdem, wenn Sie dup den Dateideskriptor, oder machen Sie das Äquivalent durch (zB) fork und exec In Bezug auf das Ausführen einer Befehlsfolge wird diese Suchposition von allen erbenden Prozessen geteilt. Daher am Shell-Prompt der Befehl:

(prog1; prog2; prog3) > outputfile

erstellt dann eine Ausgabedatei dups der Deskriptor zu den drei Programmen, damit diese ausgegeben werden prog2 schreibt direkt nach der Ausgabe von in die Datei prog1und Ausgabe von prog3 folgt den anderen beiden – alles nur, weil alle drei separaten Prozesse dieselbe zugrunde liegende Kernel-Datenstruktur mit denselben internen teilen loff_t.

Dasselbe gilt für Gerätetreiberdateien. Wenn Ihre Lese- und Schreibfunktionen aufgerufen werden, erhalten Sie den vom Benutzer bereitgestellten “aktuellen Offset”, und Sie können (und sollten) ihn nach Bedarf aktualisieren … vorausgesetzt, es besteht Bedarf (z das Erscheinungsbild einer regulären Datei, einschließlich der Tatsache, dass sich Suchoffsets beim Lesen und Schreiben verschieben). Wenn das Gerät eine logische Anwendung des Seek-Offsets hat, können Sie das hier verwenden.

Natürlich gibt es noch viel mehr zu Gerätetreibern, weshalb es ganze Buchkapitel zu diesem Thema gibt (qv). 🙂

  • Wenn ich also den Offset in offset += bytes_read/write ändere, wird der Benutzerzeiger geändert, aber nicht automatisch? Ich denke, das klärt einige Dinge für mich auf. Ich habe das Buch über den Linux-Gerätetreiber in der 3. Auflage gelesen, das alle anderen als Einführung lesen, und es hatte dieses Diagramm, in dem der Offset-Zeiger auf eine seltsame Kernel-Abstraktion verwies (mangels eines besseren Wortes), die sie Dateiposition nannten. Danke für die Hilfe, das klärt die Dinge 🙂

    – Dr.Knowitall

    15. März 2012 um 3:39 Uhr

  • Ja. (Vermutlich meinst du *offp += nbytes.) Die Variable, die Sie ändern, ist eigentlich eine Kernel-Space-Sache, aber sie repräsentiert der Such-Offset des Benutzers. (Oder in einigen Fällen der Offset, der a pread oder pwrite Anruf oder sogar etwas anderes, aber meistens der des Benutzers lseek Offset.) Diese “seltsame Kernel-Abstraktion”, wie Sie es nennen, macht (prog1; prog2) > output Arbeit. Übrigens gibt es im *BSD-Kernel eine Funktion namens uiomove das aktualisiert automatisch den “Benutzer-I/O-Offset” für Sie; Linux ging einfach in die andere Richtung.

    – Torek

    15. März 2012 um 4:41 Uhr

Benutzer-Avatar
klares Licht

Toreks Antwort ist ausgezeichnet. Ich füge nur ein bisschen mehr Details/Kontext hinzu… Aus einem früheren Linux-Kernel (2.6.28) ist hier ein Beispiel für Offset, das in einem Systemaufruf verwendet wird… es kopiert den Offset vor dem Abrufen aus dem Benutzerbereich in eine temporäre Variable in den Kernel-Treiberaufrufmechanismus und kopiert sie dann zurück in die Benutzerdatei. Auf diese Weise wird der Offset, den der Treiber sieht, von der Benutzeransicht entkoppelt und erleichtert die Situationen, in denen der 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);
}

1335500cookie-checkVerstehen von loff_t *offp für file_operations

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy