Hinweise zur Linux-Treiberentwicklung (5): Prinzip und Demo des Dateioperationssatzes des Treibers, der die Benutzerschicht und die Kernelschicht verbindet

Wenn es sich bei dem Artikel um einen Originalartikel handelt, geben Sie beim Nachdruck bitte die Quelle des Originalartikels an.
Die Blog-Adresse dieses Artikels:https ://hpzwl.blog.csdn .net/article/details/134561660

Die Sammlung der Blogbeiträge zur Netzwerktechnologie von Red Fat Man: Eine Sammlung von Entwicklungstechnologien (einschließlich praktischer Qt-Technologie, Raspberry Pi, 3D, OpenCV, OpenGL, ffmpeg, OSG, Mikrocontroller, Software- und Hardwarekombination usw.) wird kontinuierlich aktualisiert. ..

Kolumne zur Linux-Systemtransplantation und Treiberentwicklung

Vorheriger Artikel: "Linux-Treiberentwicklungshinweise (4): Einführung in Gerätetreiber, Vertrautheit mit verschiedenen Gerätetreibern und Ubuntu-Entwicklung einer Demo für verschiedene Geräte
Nächster Artikel: Bleiben Sie dran...


Vorwort

  „Nachdem der Treiber geschrieben wurde, verwendet die Benutzerschicht Systemfunktionsaufrufe, um verwandte Treiber zu betreiben und eine Verbindung mit dem Systemkernel herzustellen. Dieser Artikel zielt hauptsächlich darauf ab, zu verstehen, wie der Treiber es der Benutzerprogrammierung ermöglicht, mit dem Kernel zu interagieren.


Verschiedene Gerätedateioperationen eingestellt

cd /usr/src/linux-headers-4.18.0-15
vi include/linux/fs.h

  Gesucht nach (vi verwendet „/“ direkt):
  

struct file_operations {
    
    
        struct module *owner;
        loff_t (*llseek) (struct file *, loff_t, int);
        ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
        ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
        ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
        ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
        int (*iterate) (struct file *, struct dir_context *);
        int (*iterate_shared) (struct file *, struct dir_context *);
        __poll_t (*poll) (struct file *, struct poll_table_struct *);
        long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
        long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
        int (*mmap) (struct file *, struct vm_area_struct *);
        unsigned long mmap_supported_flags;
        int (*open) (struct inode *, struct file *);
        int (*flush) (struct file *, fl_owner_t id);
        int (*release) (struct inode *, struct file *);
        int (*fsync) (struct file *, loff_t, loff_t, int datasync);
        int (*fasync) (int, struct file *, int);
        int (*lock) (struct file *, int, struct file_lock *);
        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
        unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
        int (*check_flags)(int);
        int (*setfl)(struct file *, unsigned long);
        int (*flock) (struct file *, int, struct file_lock *);
        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
        ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
                        loff_t, size_t, unsigned int);
        int (*setlease)(struct file *, long, struct file_lock **, void **);
        long (*fallocate)(struct file *file, int mode, loff_t offset,
                          loff_t len);
        void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
        unsigned (*mmap_capabilities)(struct file *);
#endif

        int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
                        u64);
        ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,
                        u64);
} __randomize_layout;

  Mit der Lesefunktion öffnen Sie beispielsweise den Treiber und verwenden das System zum Lesen. Wenn Sie das Handle des Gerätetreibers öffnen, wird die Lesefunktion aufgerufen. Der Rest kann durch Analogie abgeleitet werden, was relativ einfach zu verstehen ist .


Die Bedeutung des Linux-Dateioperationssatzes

Überblick

  Alles in Linux ist eine Datei, und es gibt entsprechende Vorgänge wie Öffnen, Schließen, Lesen und Schreiben. Diese Vorgänge werden durch das Handle nach dem Öffnen der Datei dargestellt. Dann basiert die Funktion auf dem Typ von B. das geöffnete Handle. Wenn es sich um einen Treiber für verschiedene Geräte handelt, ruft er die entsprechende Funktion im Zeichensatz der Betriebsdatei für verschiedene Geräte auf, um den Vorgang auszuführen.
  Beim Programmieren verwenden Sie open, um einen Geräteknoten zu öffnen (es kann eine Datei oder ein Gerät sein). Zu diesem Zeitpunkt wird die Geräteknoten-Handle-ID fd zurückgegeben (Fehler ist -1). , und dann wird fd verwendet. Das Entfernen verschiedener Vorgänge wie Lesen und Schreiben entspricht dem Aufrufen von Lesen und Schreiben des in diesem Gerätetreiber festgelegten Dateivorgangs.
  Die folgenden sind häufig verwendete Dateioperationen.

offene Funktion (Implementierungstest)

int (*open) (struct inode *, struct file *);

Lesefunktion (Implementierungstest)

ssize_t (*read) (Strukturdatei *, char __user *, size_t, loff_t *)

Schreibfunktion (Implementierungstest)

ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

Umfrage-/Auswahlfunktion (nicht in diesem Artikel beschrieben)

__poll_t (*poll) (struct file *, struct poll_table_struct *);

ioctl-Funktion (in diesem Artikel nicht beschrieben)

long (*unlocked_ioctl) (Strukturdatei *, unsigned int, unsigned long);

Schließfunktion (Implementierungstest)

int (*release) (struct inode *, struct file *);


Vorbereitung der Treibervorlage

  Kopieren Sie zunächst den vorherigen registerMiscDev-Treiber und ändern Sie seinen Namen in: testFileOpts:

cd ~/work/drive
cp -arf registerMiscDev testFileOpts
cd testFileOpts
make clean
mv registerMiscDev testFileOpts.c

  Fügen Sie hier eine Bildbeschreibung ein

   Ändern Sie dann das Makefile (ändern Sie den obj-m-Modulnamen) und die Vorlage ist fertig.

gedit Makefile  

  Fügen Sie hier eine Bildbeschreibung ein

  „Das Folgende basiert auf der Datei testFileOpts.c, um verschiedene Geräte zu registrieren und die .c-Datei zu ändern:

gedit testFileOpts.c

  Fügen Sie hier eine Bildbeschreibung ein

#include <linux/init.h>
#include <linux/module.h>

#include <linux/miscdevice.h>
#include <linux/fs.h>

struct file_operations misc_fops = {
    
    
  .owner = THIS_MODULE,
};

struct miscdevice misc_dev = {
    
    
    .minor = MISC_DYNAMIC_MINOR, // 这个宏是动态分配次设备号,避免冲突
    .name = "register_hongPangZi_testFileOpt", // 设备节点名称
    .fops = &misc_fops,  // 这个变量记住,自己起的,步骤二使用
};

static int registerMiscDev_init(void)
{
    
     
    int ret;
    // 在内核里面无法使用基础c库printf,需要使用内核库printk
    printk("Hello, I’m hongPangZi, registerMiscDev_init\n");	
    ret = misc_register(&misc_dev);
    if(ret < 0)
    {
    
    
        printk("Failed to misc_register(&misc_dev)\n");	
        return -1;
    } 
    return 0;
}

static void registerMiscDev_exit(void)
{
    
    
    misc_deregister(&misc_dev);
    printk("bye-bye!!!\n");
}

MODULE_LICENSE("GPL");

module_init(registerMiscDev_init);
module_exit(registerMiscDev_exit);

Verschiedene Geräte fügen Demo zur allgemeinen Operation „Set Open“ hinzu

  Beachten Sie, dass kein Fehler gemeldet wird und keine anderen Vorgänge ausgeführt werden, wenn die aufgerufene Funktion nicht geschrieben wird. Daher müssen nicht alle Funktionen geschrieben werden.

Schritt 1: Implementieren Sie die Öffnungsfunktion

// int (*open) (struct inode *, struct file *);
int misc_open(struct inode * pInode, struct file * pFile)
{
    
    
    printk("int misc_open(struct inode * pInode, struct file * pFile)");
    return 0;
}

  Fügen Sie hier eine Bildbeschreibung ein

Schritt 2: (Taste) Weisen Sie den Dateioperationssatzzeiger zu

  Fügen Sie hier eine Bildbeschreibung ein

Schritt 3: Kompilieren und laden Sie den Treiber

  Versuchen Sie zunächst das Kompilieren:
  Fügen Sie hier eine Bildbeschreibung ein

  Dann laden Sie den Treiber:

sudo insmod tesFileOpts.ko

  Fügen Sie hier eine Bildbeschreibung ein

  Fügen Sie hier eine Bildbeschreibung ein

  Fügen Sie hier eine Bildbeschreibung ein

  „Zu diesem Zeitpunkt ist die Registrierung des Geräteknotens erfolgreich.

Schritt 4: Öffnen Sie den Geräteknoten im Programm

  Dieser Schritt ist C-Programmierung. Verwenden Sie die Linux-Systemfunktion, um den Geräteknoten zu öffnen:
  Erstellen Sie eine neue Datei:

vi test.c

  Code eingeben:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    
    
  int fd;
  const char devPath[] = "/dev/register_hongPangZi_testFileOpt";
  fd = open(devPath, O_RDWR);
  if(fd < 0)
  {
    
    
    printf("fialed to open %s\n", devPath);
    return -1;
  } else{
    
    
    printf("Succeed to open %s\n", devPath);
  }
  return 0;
}

  Fügen Sie hier eine Bildbeschreibung ein

  Zusammenstellung:

gcc test.c

  Fügen Sie hier eine Bildbeschreibung ein

  Die Standardausgabe ist a.out, führen Sie es unten aus:
  Fügen Sie hier eine Bildbeschreibung ein

  „Es kann nicht ausgeführt werden, da Ubuntu Administratorrechte für das Gerät erfordert. So führen Sie es mit Administratorrechten aus:
  Fügen Sie hier eine Bildbeschreibung ein

  Schauen Sie sich den Kernel-Ausdruck an (hier gibt es keinen Ausdruck, überprüfen Sie „Enter the Pit 1“):
  Fügen Sie hier eine Bildbeschreibung ein

  An diesem Punkt ist aus der Benutzerprogrammierschicht grundsätzlich klar, wie auf Geräteknoten zugegriffen und dann Funktionen der Kernelschicht aufgerufen werden.


Ergänzen Sie andere Funktionsdemo

Ergänzung lesen und schreiben

#include <linux/init.h>
#include <linux/module.h>

#include <linux/miscdevice.h>
#include <linux/fs.h>

// int (*open) (struct inode *, struct file *);
int misc_open(struct inode * pInode, struct file * pFile)
{
    
    
    printk("int misc_open(struct inode * pInode, struct file * pFile\n)");
    return 0;
}

// int (*release) (struct inode *, struct file *);
int misc_release(struct inode * pInde, struct file * pFile)
{
    
    
    printk("int misc_release(struct inode * pInde, struct file * pFile\n)");
    return 0;
}

// ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)
{
    
    
    printk("ssize_t misc_read(struct file * pFile, char __user * pUser, size_t size, loff_t *pLofft)\n");
    return 0;
}

// ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)
{
    
    
    printk("ssize_t misc_write(struct file * pFile, const char __user * pUser, size_t size, loff_t *pLofft)\n");
    return 0;
}

struct file_operations misc_fops = {
    
    
  .owner = THIS_MODULE,
  .open = misc_open,
  .release = misc_release,
  .read = misc_read,
  .write = misc_write,
};

struct miscdevice misc_dev = {
    
    
    .minor = MISC_DYNAMIC_MINOR, // 这个宏是动态分配次设备号,避免冲突
    .name = "register_hongPangZi_testFileOpt", // 设备节点名称
    .fops = &misc_fops,  // 这个变量记住,自己起的,步骤二使用
};

static int registerMiscDev_init(void)
{
    
     
    int ret;
    // 在内核里面无法使用基础c库printf,需要使用内核库printk
    printk("Hello, I’m hongPangZi, registerMiscDev_init\n");	
    ret = misc_register(&misc_dev);
    if(ret < 0)
    {
    
    
        printk("Failed to misc_register(&misc_dev)\n");	
        return -1;
    } 
    return 0;
}

static void registerMiscDev_exit(void)
{
    
    
    misc_deregister(&misc_dev);
    printk("bye-bye!!!\n");
}

MODULE_LICENSE("GPL");
module_init(registerMiscDev_init);
module_exit(registerMiscDev_exit);

Ändern Sie den Quellcode des test.c-Testtreibers

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
  int fd;
  char buf[32] = {0};

  const char devPath[] = "/dev/register_hongPangZi_testFileOpt";
  fd = open(devPath, O_RDWR);
  if(fd < 0)
  {
    printf("Failed to open %s\n", devPath);
    return -1;
  }else{
    printf("Succeed to open %s\n", devPath);
  }

  read(fd, buf, sizeof(buf));
  write(fd, buf, sizeof(buf));

  close(fd);
  printf("exit\n");
  fd = -1;
  return 0;
}

Ausgabe anzeigen

  Fügen Sie hier eine Bildbeschreibung ein


Tappen Sie in die Falle

Fallstrick 1: Der Kernel druckt die Open-Funktion nicht aus

Frage

  Das Programm öffnet einen separaten Knoten und die Öffnungsfunktion wird nicht gedruckt.

Grund

  Die Öffnungsfunktion ist nicht dem Dateioperationssatz zugewiesen.

lösen

  Fügen Sie hier eine Bildbeschreibung ein

Fallstrick 2: dmesg verfügt nicht über das Drucken von Close-Releases

Frage

  Fügen Sie hier eine Bildbeschreibung ein

prüfen

  Fügen Sie hier eine Bildbeschreibung ein

  Fügen Sie hier eine Bildbeschreibung ein

  Ich habe dmesg studiert, aber es kam nicht heraus. Das war nicht klar. Später fragte ich den Fahrerchef und er erinnerte mich daran, dass es sich möglicherweise um ein Zeilenumbruchproblem handelte. Ich fügte es später hinzu und es funktionierte.

Lösung

  Fügen Sie hier eine Bildbeschreibung ein
  Fügen Sie hier eine Bildbeschreibung ein


Vorheriger Artikel: "Linux-Treiberentwicklungshinweise (4): Einführung in Gerätetreiber, Vertrautheit mit verschiedenen Gerätetreibern und Ubuntu-Entwicklung einer Demo für verschiedene Geräte
Nächster Artikel: Bleiben Sie dran...


Die Blog-Adresse dieses Artikels:https://hpzwl.blog.csdn.net/article/details/134561660

Supongo que te gusta

Origin blog.csdn.net/qq21497936/article/details/134561660
Recomendado
Clasificación