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
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
Ändern Sie dann das Makefile (ändern Sie den obj-m-Modulnamen) und die Vorlage ist fertig.
gedit Makefile
„Das Folgende basiert auf der Datei testFileOpts.c, um verschiedene Geräte zu registrieren und die .c-Datei zu ändern:
gedit testFileOpts.c
#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;
}
Schritt 2: (Taste) Weisen Sie den Dateioperationssatzzeiger zu
Schritt 3: Kompilieren und laden Sie den Treiber
Versuchen Sie zunächst das Kompilieren:
Dann laden Sie den Treiber:
sudo insmod tesFileOpts.ko
„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;
}
Zusammenstellung:
gcc test.c
Die Standardausgabe ist a.out, führen Sie es unten aus:
„Es kann nicht ausgeführt werden, da Ubuntu Administratorrechte für das Gerät erfordert. So führen Sie es mit Administratorrechten aus:
Schauen Sie sich den Kernel-Ausdruck an (hier gibt es keinen Ausdruck, überprüfen Sie „Enter the Pit 1“):
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
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
Fallstrick 2: dmesg verfügt nicht über das Drucken von Close-Releases
Frage
prüfen
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
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