Inhaltsverzeichnis
Fahrermodell" >Fahrermodell
Implementieren Sie Lesen und Schreiben" >Implementieren Sie Lesen und Schreiben
Ioctl implementieren" >Ioctl implementieren
实例" >实例
创建设备文件" >创建设备文件
Heim System-Tutorial LINUX Linux-Zeichengerätetreiber-Framework: Prinzipien und Methoden

Linux-Zeichengerätetreiber-Framework: Prinzipien und Methoden

Feb 09, 2024 pm 09:06 PM
linux linux教程 linux系统 linux命令 shell脚本 overflow eingebettetes Linux Erste Schritte mit Linux Linux-Lernen

Zeichengeräte sind ein häufiger Gerätetyp in Linux-Systemen. Sie übertragen Daten in Bytes, z. B. Tastatur, Maus, serielle Schnittstelle usw. Der Zeichengerätetreiber ist die grundlegendste Art der Linux-Treiberentwicklung. Er umfasst Konzepte wie Gerätenummer, Gerätedatei, Dateioperationsstruktur und CDev-Struktur. In diesem Artikel stellen wir die Prinzipien und Methoden des Linux-Zeichengerätetreiber-Frameworks vor, einschließlich der Registrierung und Abmeldung von Gerätenummern, der Erstellung und Löschung von Gerätedateien, der Initialisierung und Freigabe von CDev-Strukturen, der Implementierung und Registrierung von Dateioperationsfunktionen usw. und der Bereitstellung Beispiele zur Veranschaulichung der Verwendung und Vorsichtsmaßnahmen.

Linux-Zeichengerätetreiber-Framework: Prinzipien und Methoden

Fahrermodell

Alles in Linux ist eine Datei, daher ist die Operationsmethode-Schnittstelle in struct file_operations gekapselt. Wenn wir einen Treiber schreiben, müssen wir die entsprechende Schnittstelle implementieren, damit der Treiber in Linux weit verbreitet ist Kernel.“ Der Registrierungs- und Rückrufmechanismus wird zum Schreiben von Treibern verwendet. Der sogenannte Registrierungsrückruf. Ein einfaches Verständnis besteht darin, dass wir beim Öffnen einer Gerätedatei tatsächlich den entsprechenden Inode über VFS finden und die Registrierung im Inode ausführen, wenn Wir haben die Gerätedatei zuvor erstellt und sind mit anderen Funktionen identisch. Damit der von uns geschriebene Treiber normal von der Anwendung ausgeführt werden kann, müssen wir zunächst die entsprechende Methode implementieren und dann die entsprechende erstellen Gerätedatei.

#include  //for struct cdev
#include   
 //for struct file
#include     //for copy_to_user
#include          
   //for error number

static int ma = 0;
static int mi = 0;
const int count = 3;

/* 准备操作方法集 */
/* 
struct file_operations {
    struct module *owner;   //THIS_MODULE
    
    //读设备
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    //写设备
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

    //映射内核空间到用户空间
    int (*mmap) (struct file *, struct vm_area_struct *);

    //读写设备参数、读设备状态、控制设备
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

    //打开设备
    int (*open) (struct inode *, struct file *);
    //关闭设备
    int (*release) (struct inode *, struct file *);

    //刷新设备
    int (*flush) (struct file *, fl_owner_t id);

    //文件定位
    loff_t (*llseek) (struct file *, loff_t, int);

    //异步通知
    int (*fasync) (int, struct file *, int);
    //POLL机制
    unsigned int (*poll) (struct file *, struct poll_table_struct *);
    。。。
};
*/

ssize_t myread(struct file *filep, char __user * user_buf, size_t size, loff_t* offset)
{
    return 0;
}

struct file fops = {
    .owner = THIS_MODULE,
    .read = myread,
    ...
};

/* 字符设备对象类型 
struct cdev {
    struct kobject kobj;     
    struct module *owner;        //模块所有者(THIS_MODULE),用于模块计数
    const struct file_operations *ops;    //操作方法集(分工:打开、关闭、读/写、...)
    struct list_head list;
    dev_t dev;                            //设备号(第一个)
    unsigned int count;            //设备数量
};
*/

static int __init chrdev_init(void)
{
    ...
    /* 构造cdev设备对象 */
    struct cdev *cdev_alloc(void);

    /* 初始化cdev设备对象 */
    void cdev_init(struct cdev*, const struct file_opeartions*);

    /* 申请设备号,静态or动态*/
    /* 为字符设备静态申请第一个设备号 */
    int register_chrdev_region(dev_t from, unsigned count, const char* name);

    /* 为字符设备动态申请第一个设备号 */
    int alloc_chrdev_region(dev_t* dev, unsigned baseminor, unsigned count, const char* name);

    ma = MAJOR(dev)     //从dev_t数据中得到主设备号
    mi = MINOR(dev)     //从dev_t数据中得到次设备号
    MKDEV(ma,1) //将主设备号和次设备号组合成设备号,多用于批量创建/删除设备文件

    /* 注册字符设备对象cdev到内核 */
    int cdev_add(struct cdev* , dev_t, unsigned);
    ...
}

static void __exit chrdev_exit(void)
{
    ...
    /* cdev_del()、cdev_put()二选一 */
    /* 从内核注销cdev设备对象 */
    void cdev_del(struct cdev* );

    /* 从内核注销cdev设备对象 */
    void cdev_put(stuct cdev *);

    /* 回收设备号 */
    void unregister_chrdev_region(dev_t from, unsigned count);
    ...
}
Nach dem Login kopieren

Ein Wort: Wenn Sie eine statische Anwendung für Gerätenummern verwenden, besteht das größte Problem darin, keinen Konflikt mit bekannten Gerätenummern zu verursachen. Der Kernel hat bereits notiert, welche Hauptgerätenummern im Dokument **"Documentation/devices.txt"** enthalten sind Daraus ist ersichtlich, dass unter den 2^12 Hauptgerätenummern der Bereich 240-255 und 261-2^12-1 ist. Dies kann auch erklären, warum, wenn wir es dynamisch anwenden Die Gerätenummer beträgt oft 250. Darüber hinaus können wir anhand dieser Datei auch erkennen, dass „die Hauptgerätenummer einen Gerätetyp darstellt“, Zeichen-/Blockgeräte selbst jedoch in viele Kategorien unterteilt werden können, sodass der Kernel jeder Kategorie eine Hauptgerätenummer zuweist.
Linux-Zeichengerätetreiber-Framework: Prinzipien und Methoden

Implementieren Sie Lesen und Schreiben

Jeder Prozess unter Linux verfügt über einen eigenen unabhängigen Prozessraum. Selbst wenn die Kerneldaten einem Benutzerprozess zugeordnet sind, wird die PID der Daten automatisch in die PID des Benutzerprozesses konvertiert Daten können nicht direkt aus dem Kernel- und Benutzerbereich kopiert werden, und es sind spezielle Funktionen/Maros zum Kopieren von Daten erforderlich:

long copy_from_user(void *to, const void __user * from, unsigned long n)

long copy_to_user(void __user *to, const void *from, unsigned long n)
Nach dem Login kopieren

Diese beiden Funktionen können die Daten im Kernelraum in den Benutzerprozessraum des Benutzerprozesses kopieren, der die Funktion zurückruft. Mit diesen beiden Funktionen können Lese- und Schreibvorgänge im Kernel das Kopieren von Daten zwischen dem Kernelraum und dem Benutzerraum realisieren.

ssize_t myread(struct file *filep, char __user * user_buf, size_t size, loff_t* offset)
{
    long ret = 0;
    size = size > MAX_KBUF?MAX_KBUF:size;
    if(copy_to_user(user_buf, kbuf,size)
        return -EAGAIN;
    }
    return 0;
}
Nach dem Login kopieren

Ioctl implementieren

ioctl ist eine von Linux speziell für Steuergeräte auf Benutzerebene entwickelte Schnittstelle. Welche Funktionen unser Gerät durch Befehle erreichen soll, ist in der Operationsmethode festgelegt Der entsprechende Funktionszeiger ist long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);, wobei die Befehle und Parameter vollständig vom Treiber angegeben werden. Normalerweise wird der Befehl in eine Header-Datei geschrieben, um sicherzustellen, dass die Anwendungsschicht und die Treiberschicht demselben Kommunikationsprotokoll entsprechen in der Abbildung gezeigt) ) Befehl

设备类型    序列号     方向      数据尺寸
8bit        8bit    2bit    13/14bit
Nach dem Login kopieren

设备类型字段为一个幻数,可以是0~0xff之间的数,内核中的”ioctl-number.txt“给出了一个推荐的和已经被使用的幻数(但是已经好久没人维护了),新设备驱动定义幻数的时候要避免与其冲突。
序列号字段表示当前命令是整个ioctl命令中的第几个,从1开始计数。
方向字段为2bit,表示数据的传输方向,可能的值是:**_IOC_NONE_IOC_READ_IOC_WRITE_IOC_READ|_IOC_WRITE**。
数据尺寸字段表示涉及的用户数据的大小,这个成员的宽度依赖于体系结构,通常是13或14位。

内核还定义了**_IO()_IOR()_IOW()_IOWR()**这4个宏来辅助生成这种格式的命令。这几个宏的作用是根据传入的type(设备类型字段),nr(序列号字段)和size(数据长度字段)和方向字段移位组合生成命令码。

内核中还预定义了一些I/O控制命令,如果某设备驱动中包含了与预定义命令一样的命令码,这些命令会被当做预定义命令被内核处理而不是被设备驱动处理,有如下4种:

  • FIOCLEX:即file ioctl close on exec 对文件设置专用的标志,通知内核当exec()系统带哦用发生时自动关闭打开的文件
  • FIONCLEX:即file ioctl not close on exec,清除由FIOCLEX设置的标志
  • FIOQSIZE:获得一个文件或目录的大小,当用于设备文件时,返回一个ENOTTY错误
  • FIONBIO:即file ioctl non-blocking I/O 这个调用修改flip->f_flags中的O_NONBLOCK标志

实例

//mycmd.h
...
#include 
#define CMDT 'A'
#define KARG_SIZE 36
struct karg{
    int kval;
    char kbuf[KARG_SIZE];
};
#define CMD_OFF _IO(CMDT,0)
#define CMD_ON  _IO(CMDT,1)
#define CMD_R  
 _IOR(CMDT,2,struct karg)
#define CMD_W   _IOW(CMDT,3,struct karg)
...
//chrdev.c
static long demo_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    static struct karg karg = {
        .kval = 0,
        .kbuf = {0},
    };
    struct karg *usr_arg;

    switch(cmd){
    case CMD_ON:
        /* 开灯 */
        break;
    case CMD_OFF:
        /* 关灯 */
        break;
    case CMD_R:
        if(_IOC_SIZE(cmd) != sizeof(karg)){
            return -EINVAL;
        }
        usr_arg = (struct karg *)arg;
        if(copy_to_user(usr_arg, &karg, sizeof(karg))){
            return -EAGAIN;
        }
        break;
    case CMD_W:     
        if(_IOC_SIZE(cmd) != sizeof(karg)){
            return -EINVAL;
        }
        usr_arg = (struct karg *)arg;
        if(copy_from_user(&karg, usr_arg, sizeof(karg))){
            return -EAGAIN;
        }
        break;
    default:
        ;
    };
    return 0;
}
Nach dem Login kopieren

创建设备文件

插入的设备模块,我们就可以使用cat /proc/devices命令查看当前系统注册的设备,但是我们还没有创建相应的设备文件,用户也就不能通过文件访问这个设备。设备文件的inode应该是包含了这个设备的设备号,操作方法集指针等信息,这样我们就可以通过设备文件找到相应的inode进而访问设备。创建设备文件的方法有两种,手动创建自动创建手动创建设备文件就是使用mknod /dev/xxx 设备类型 主设备号 次设备号的命令创建,所以首先需要使用cat /proc/devices查看设备的主设备号并通过源码找到设备的次设备号,需要注意的是,理论上设备文件可以放置在任何文件加夹,但是放到**”/dev”才符合Linux的设备管理机制,这里面的devtmpfs是专门设计用来管理设备文件的文件系统。设备文件创建好之后就会和创建时指定的设备绑定,即使设备已经被卸载了,如要删除设备文件,只需要像删除普通文件一样rm**即可。理论上模块名(lsmod),设备名(/proc/devices),设备文件名(/dev)并没有什么关系,完全可以不一样,但是原则上还是建议将三者进行统一,便于管理。

除了使用蹩脚的手动创建设备节点的方式,我们还可以在设备源码中使用相应的措施使设备一旦被加载就自动创建设备文件,自动创建设备文件需要我们在编译内核的时候或制作根文件系统的时候就好相应的配置:

Device Drivers --->
        Generic Driver Options --->
            [*]Maintain a devtmpfs filesystem to mount at /dev
            [*] Automount devtmpfs at /dev,after the kernel mounted the rootfs
Nach dem Login kopieren

OR
制作根文件系统的启动脚本写入

mount -t sysfs none sysfs /sys
mdev -s //udev也行
Nach dem Login kopieren

有了这些准备,只需要导出相应的设备信息到**”/sys”**就可以按照我们的要求自动创建设备文件。内核给我们提供了相关的API

class_create(owner,name);
struct device *device_create_vargs(struct class *cls, struct device 
*parent,dev_t devt, void *drvdata,const char *fmt, va_list vargs);

void class_destroy(struct class *cls);   
void device_destroy(struct class *cls, dev_t devt);
Nach dem Login kopieren

有了这几个函数,我们就可以在设备的**xxx_init()xxx_exit()**中分别填写以下的代码就可以实现自动的创建删除设备文件

    /* 在/sys中导出设备类信息 */
    cls = class_create(THIS_MODULE,DEV_NAME);

    /* 在cls指向的类中创建一组(个)设备文件 */
    for(i= minor;i"%s%d",DEV_NAME,i);
    }  
    /* 在cls指向的类中删除一组(个)设备文件 */
    for(i= minor;i
Nach dem Login kopieren

通过本文,我们了解了Linux字符设备驱动框架的原理和方法,它们可以用来实现对字符设备的驱动和控制。我们应该根据实际需求选择合适的方法,并遵循一些基本原则,如使用静态或动态分配的设备号,使用标准或自定义的设备文件名,使用正确或简化的cdev初始化方式等。字符设备驱动框架是Linux驱动开发中最重要的一部分,它可以实现对字符设备的读写操作,也可以提升驱动程序的可移植性和可维护性。希望本文能够对你有所帮助和启发。

Das obige ist der detaillierte Inhalt vonLinux-Zeichengerätetreiber-Framework: Prinzipien und Methoden. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Beste grafische Einstellungen
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. So reparieren Sie Audio, wenn Sie niemanden hören können
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Wie man alles in Myrise freischaltet
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Wie löste ich Berechtigungsprobleme bei der Verwendung von Python -Verssionsbefehl im Linux Terminal? Wie löste ich Berechtigungsprobleme bei der Verwendung von Python -Verssionsbefehl im Linux Terminal? Apr 02, 2025 am 06:36 AM

Verwenden Sie Python im Linux -Terminal ...

Vier Möglichkeiten zur Implementierung von Multithreading in C -Sprache Vier Möglichkeiten zur Implementierung von Multithreading in C -Sprache Apr 03, 2025 pm 03:00 PM

Multithreading in der Sprache kann die Programmeffizienz erheblich verbessern. Es gibt vier Hauptmethoden, um Multithreading in C -Sprache zu implementieren: Erstellen Sie unabhängige Prozesse: Erstellen Sie mehrere unabhängig laufende Prozesse. Jeder Prozess hat seinen eigenen Speicherplatz. Pseudo-MultitHhreading: Erstellen Sie mehrere Ausführungsströme in einem Prozess, der denselben Speicherplatz freigibt und abwechselnd ausführt. Multi-Thread-Bibliothek: Verwenden Sie Multi-Thread-Bibliotheken wie PThreads, um Threads zu erstellen und zu verwalten, wodurch reichhaltige Funktionen der Thread-Betriebsfunktionen bereitgestellt werden. Coroutine: Eine leichte Multi-Thread-Implementierung, die Aufgaben in kleine Unteraufgaben unterteilt und sie wiederum ausführt.

So öffnen Sie Web.xml So öffnen Sie Web.xml Apr 03, 2025 am 06:51 AM

Um eine Web.xml -Datei zu öffnen, können Sie die folgenden Methoden verwenden: Verwenden Sie einen Texteditor (z.

Kann der Python -Dolmetscher im Linux -System gelöscht werden? Kann der Python -Dolmetscher im Linux -System gelöscht werden? Apr 02, 2025 am 07:00 AM

In Bezug auf das Problem der Entfernung des Python -Dolmetschers, das mit Linux -Systemen ausgestattet ist, werden viele Linux -Verteilungen den Python -Dolmetscher bei der Installation vorinstallieren, und verwendet den Paketmanager nicht ...

Wofür wird der Linux am besten verwendet? Wofür wird der Linux am besten verwendet? Apr 03, 2025 am 12:11 AM

Linux wird am besten als Serververwaltung, eingebettete Systeme und Desktop -Umgebungen verwendet. 1) In der Serververwaltung wird Linux verwendet, um Websites, Datenbanken und Anwendungen zu hosten und Stabilität und Zuverlässigkeit bereitzustellen. 2) In eingebetteten Systemen wird Linux aufgrund seiner Flexibilität und Stabilität in Smart Home und Automotive Electronic Systems häufig verwendet. 3) In der Desktop -Umgebung bietet Linux reichhaltige Anwendungen und eine effiziente Leistung.

Wie ist Debian Hadoop -Kompatibilität? Wie ist Debian Hadoop -Kompatibilität? Apr 02, 2025 am 08:42 AM

Debianlinux ist bekannt für seine Stabilität und Sicherheit und wird in Server-, Entwicklungs- und Desktop -Umgebungen häufig verwendet. Während es derzeit einen Mangel an offiziellen Anweisungen zur direkten Kompatibilität mit Debian und Hadoop gibt, wird dieser Artikel Sie dazu veranlassen, Hadoop in Ihrem Debian -System bereitzustellen. Debian -Systemanforderungen: Bevor Sie mit der Hadoop -Konfiguration beginnen, stellen Sie bitte sicher, dass Ihr Debian -System die Mindestanforderungen von Hadoop erfüllt, einschließlich der Installation der erforderlichen Java -Laufzeitumgebung (JRE) und Hadoop -Pakete. Schritte zur Bereitstellung von Hadoop -Bereitstellungen: Download und Unzip Hadoop: Laden Sie die Hadoop -Version von der offiziellen Apachehadoop -Website herunter und lösen Sie sie

Ist Debian Strings kompatibel mit mehreren Browsern Ist Debian Strings kompatibel mit mehreren Browsern Apr 02, 2025 am 08:30 AM

"DebianStrings" ist kein Standardbegriff und seine spezifische Bedeutung ist noch unklar. Dieser Artikel kann seine Browserkompatibilität nicht direkt kommentieren. Wenn sich jedoch "DebianStrings" auf eine Webanwendung bezieht, die auf einem Debian -System ausgeführt wird, hängt seine Browserkompatibilität von der technischen Architektur der Anwendung selbst ab. Die meisten modernen Webanwendungen sind für die Kompatibilität des Cross-Browsers verpflichtet. Dies beruht auf den folgenden Webstandards und der Verwendung gut kompatibler Front-End-Technologien (wie HTML, CSS, JavaScript) und Back-End-Technologien (wie PHP, Python, Node.js usw.). Um sicherzustellen, dass die Anwendung mit mehreren Browsern kompatibel ist, müssen Entwickler häufig Kreuzbrowser-Tests durchführen und die Reaktionsfähigkeit verwenden

Muss ich einen Oracle -Client installieren, wenn ich mit GO eine Verbindung zu einer Oracle -Datenbank herstellen kann? Muss ich einen Oracle -Client installieren, wenn ich mit GO eine Verbindung zu einer Oracle -Datenbank herstellen kann? Apr 02, 2025 pm 03:48 PM

Muss ich einen Oracle -Client installieren, wenn ich mit GO eine Verbindung zu einer Oracle -Datenbank herstellen kann? Bei der Entwicklung in Go ist die Verbindung zu Oracle -Datenbanken eine übliche Anforderung ...

See all articles