• <tbody id="wslfv"><pre id="wslfv"></pre></tbody>
    <span id="wslfv"></span>
    <tbody id="wslfv"><pre id="wslfv"></pre></tbody>
    <th id="wslfv"><track id="wslfv"><rt id="wslfv"></rt></track></th>

    <li id="wslfv"><acronym id="wslfv"></acronym></li>
    更多課程 選擇中心

    嵌入式培訓
    達內IT學院

    400-111-8989

    讓新手快速從代碼層了解什么是Linux設備驅動

    • 發布:嵌入式培訓
    • 來源:嵌入式問答
    • 時間:2017-09-19 11:04

    所需知識

    - C 語言編程

    - 微處理器編程.對處理器的工作原理有一定的了解,如內存管理、中斷等。

    用戶空間和內核空間

    寫設備驅動時,了解“用戶空間”和“內核空間”之間的區別是非常重要的。- 內核空間。Linux內核簡單并高效地管理著機器的硬件,為用戶提供簡單并規范的編程接口。同樣地,內核,特別是內核中的驅動,是用戶/程序員與硬件之間的橋梁或接口。內核的任何例程或函數(比如模塊、驅動)都屬于內核空間。

    - 用戶空間。用戶程序,比如unix shell或其他的gui應用程序(比如kpresenter),都屬于用戶空間。顯然,這些應用程序都要與硬件打交道。但是它們不并直接操作硬件,而是通過內核提供的函數來實現。

    用戶空間與內核空間之間的接口函數

    內核為用戶空間提供了一系列的例程或函數,用戶的應用程序利用這些接口來與硬件交互。通常,在UNIX或Linux系統中,這種對話是通過函數或子程序來讀寫文件的。原因是從用戶的角度來看,UNIX設備就是文件。

    另一方面,在內核空間中Linux也提供了一系列的函數或子程序來完成底層與硬件的交互,并允許從內核向用戶空間傳遞信息。通常,每個用戶空間的(設備或文件允許使用的)函數,都能在內核空間中找到一個類似的函數,(允許信息從內核傳遞給用戶空間,反之亦然)

    內核空間與硬件設備之間的接口函數內核空間中有許多函數用于控制硬件或在內核與硬件之間交互信息。第一個驅動:在用戶空間加載和移除驅動現在將展示如何完成第一個驅動,在內核中將看作模塊

    新建一個文件nothing.c如下

    include

    MODULE_LICENSE(“Dual BSD/GPL”);

    2.6.x版本后的內核,編譯模塊會略微復雜一點。首先,需要有一份完整的、編譯過的內核源碼樹。在下面的文字中,將假設使用2。6。8版本的內核。

    其次,需要一個makefile文件,本例中的makefile文件名為Makefile,內容如下:

    obj-m := nothing.o

    與之前版本的內核不同,現在編譯模塊時使用的內核需要與模塊將要加載的內核相同。

    編譯上面的文件,可以使用命令:

    make -C /usr/src/kernel-source-2.6.8 M=pwd modules

    這個極其簡單的模塊就屬于內核空間,一旦其被加載,它就是內核空間的一部分。在用戶空間,可以使用下面的命令加載它,需要root權限:

    insmod nothing.ko

    insmod 這個命令用于為內核加載模塊。盡管現在我們已經加載了nothing.ko這個模塊,但是這個模塊畢竟沒有任何用處。

    可以通過查看系統里已加載的模塊來檢查是否已經成功加載了nothing.ko

    lsmod

    最后,需要卸載該模塊時使用下面的命令:rmmod nothing重新使用lsmod,可以發現nothing模塊已經不在了。“Hello world”驅動:在內核空間加載和移除驅動當一個模塊設備驅動加載到內核,將執行一些初始的工作,如重新設置設備,reservingRAM, reserving interrupts, reserving input/output ports, etc.

    這些工作得以在內核空間執行,必須要有兩個函數存在:module_init 和module_exit;它們對應于用戶空間的insmod和rmmod命令。總之,用戶命令insmod和rmmod使用了內核空間的函數module_init和module_exit.

    來看一個經典的程序 HELLO WORLD:

    //hello.c#include #include #inlucde

    MODULE_LICENSE("Dual BSD/GPL");

    static int hello_init(void)

    {

    printk("<1> Hello world!\n");

    return 0;

    }

    static void hello_exit(void)

    {

    printk("<1> Bye, cruel world!\n");

    }

    module_init(hello_init);

    module_exit(hello_exit);

    · 1

    · 2

    · 3

    · 4

    · 5

    · 6

    · 7

    · 8

    · 9

    · 10

    · 11

    · 12

    · 13

    · 14

    · 15

    · 16

    · 17

    · 18

    · 19

    · 20

    其中hello_init 和 hello_exit 函數可以取任意名,但為了加載和移除功能是更容易識別,它們作為參數傳遞給函數module_init 和 module_exit.

    printk函數與printf函數非常類似,但printk只工作在內核中。<1>表示打印信息為最高優先級(數字越低,優先級越高)。

    這樣,不僅可以在內核系統日志中看到該打印信息,還能在系統控制臺接收到該打印信息。

    可以用之前的命令來編譯這個模塊,此時只需要將模塊名加入到Makefile文件中即可:

    obj-m := nothing.o hello.o

    本文的其他部分,將Makefile作為給讀者的練習。一個完整的Makefile文件可以編譯本教程中的所有示例模塊。

    當模塊被加載或卸載時,通過printk打印的信息將會出現在系統控制臺。如果打印信息沒有出現在終端里,則可通過dmesg命令或查看系統日志文件(catvar/log/syslog)看到打印信息。

    一個完整的驅動“memory“:此驅動的初始部分接著將介紹如何構建一個完整的設備驅動:memory.c。可以從該設備中讀取一個字符,也可向其寫入一個字符。這個設備并沒有實際意義,只是因為它是一個完整的驅動程序,遂將其作為一個實例來說明。它很容易實現,因為它并不是一個正真的硬件設備的接口(除了電腦本身)。

    這個驅動中,要添加幾個在設備驅動程序中頻繁出現的#inclu#include

    include

    include

    include

    include

    include

    include

    include

    include

    include

    include

    MODULE_LICENSE(“Dual BSD/GPL”);

    int memory_open(struct inode *inode, struct file *filp);

    int memory_release(struct inode *inode, struct file *filp);

    ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t

    *f_pos);

    ssize_t memory_write(struct file *filp, char *buf, size_t count, loff_t

    *f_pos);

    void memory_exit(void);

    int memory_init(void);

    struct file_operations memory_fops = {

    read: memory_read,

    write: memory_write,

    open: memory_open,

    release: memory_release

    };

    module_init(memory_init);

    module_exit(memory_exit);

    int memory_major = 60;r *memory_buffer;

    件之后,聲明了幾個后面要定義的函數。在file_operations結構的定義中聲明了幾個通常用來操作文件的函數。這些在后面會詳細介紹到。接著,向內核聲明初始化和退出函數-加載和卸載模塊時使用的。最后,聲明驅動的全局變量:memory_major表示驅 動的主驅動號,memory_buffer指向一塊用于存儲驅動數據的內存區域。

    “memory”驅動:設備與其文件的連接在UNIX和Linux中,從用戶空間訪問設備與訪問文件相同。這些設備文件通常位于/dev目 錄下。

    將一個普通文件與設備文件關聯起來需要使用兩個數字:major number 和 minor number。

    內核使用major number將一個文件鏈接到它的驅動。而minor number是供設備內部使用。

    要做到這一點,一個文件(將用于訪問設備驅動程序)的創建必須使用root身份鍵入以下

    命令:

    mknod /dev/memory c 60 0

    上面這句命令中,c表示創建一個字符設備,該設備的主驅動號major number為60,次驅動號minor number為0。

    對于這個驅動,為了在內核空間將其鏈接到對應的/dev下的文件,需要使用 register_chrdev函數。調用該函數使用到三個參數:major number,一個字符串用于表示該模塊的名字,一個file_operations結構。

    在安裝模塊時它以下面的方式被調用:

    int memory_init(void)

    {

    int result;

    result = register_chrdev(memory_major, "memory", &memory_fops);

    if (result < 0) {

    printk("<1>memory: can't obtain major number %d\n",

    memory_major);

    return result;

    }

    memory_buffer = kmalloc(1, GFP_KERNEL);

    if (!memroy_buffer) {

    result = -ENOMEM;

    goto fail;

    }

    memset(memory_buffer, 0, 1);

    printk("<1> Inserting memory module\n");

    return 0;

    fail:

    memory_exit();

    return result;

    }

    注意kmalloc函數的使用。這個函數在內核空間中分配一塊用于設備驅動緩沖區的內存。它的使用方法與著名的malloc函數類似。最后,如果注冊主驅動號失敗或分配內存失敗, 這個模塊也將失敗。

    “memory”驅動:移除驅動

    為了在memory_exit函數中移除模塊,需要使用到unregsiter_chrdev函數。它將為內核釋放相應的主驅

    void memory_exit(void)

    {

    unregister_chrdev(memory_major, “memory”);

    if (memory_buffer) {

    kfree(memory_buffer);

    }

    printk("<1> Removing memory module\n");

    }

    移除驅動時還原一個干凈的內核,在這個函數中同時釋放了驅動的緩沖區。

    “memory”驅動:像打開文件一樣打開設備內核空間中與用戶空間中打開文件(fopen)相對應的是open:調用register_chrdev時使用到了一個file_operations結構,而open正是這個結構的成員。open函數的參數有:一個inode結構,它向內核傳遞有關主驅動號major number和次驅動號minor number的相 關信息;一個file結構,該結構中包括操作文件的多個不同函數。但本文并不對這些函數 作詳細介紹。

    當一個文件被打開,通常需要初始化驅動變量或重新設置這個設備。但在這個例子中這些沒有做這些工作。

    memory_open函數如下:

    int memory_open(struct inode *inode, struct file *filp)

    {

    return 0;

    }

    “memory”驅動:像關閉文件一樣關閉設備與用戶空間中關閉文件(fclose)相對應的是release:調用register_chrdev時使用到一個file_operations結構,release正是該結構的成員。在本例中,它對應 memory_release函數,與前面類似,它也有兩個參數:inode結構和file結構。

    當一個文件關閉,通常需要釋放已使用的內存和任何打開文件時關鏈到的變量。但是,同樣的因為本例十分簡單,這些工作這里都沒有做。

    memory_release函數如下:

    int memory_release(struct inode *inode, struct file *filp)

    {

    return 0;

    }

    “memory”驅動:讀設備

    同樣,對應于用戶空間讀文件的fread,這里用到read:它也是file_operations結構的成員。這里它對應memory_read函數。它的參數有:一個file結構;一個緩沖區buf,用戶空間從該緩沖區中讀數據;一個計數器count記錄傳輸的字節;最后,還有f_pos,用來指示從文件的哪里開始讀取。

    在本例中,memory_read函數使用函數copy_to_user從驅動緩沖區中發送一個字節給用戶

    ssize_t memory_read(struct *file filp, char *buf,

    size_t count, loff_t *f_pos)

    {

    copy_to_user(buf, memory_buffer, 1);

    if (*f_pos == 0) {

    *f_pos += 1;

    return 1;

    } else {

    return 0;

    }

    }

    置f_pos同時也會改變。如果從文件開頭讀起,f_pos會以1遞增,并且返回已正確讀到的字節數,即1。如果不是從文件開頭讀起,文件的結束標志0將被返回,因為文件中沒有數據。

    “memory”驅動:寫設備

    與fwrite類似,內核空間有write:它是file_operations結構的成員。本例中為memory_write,有下面幾個參數:一個file結構;buf緩沖區,供用戶空間寫入;count,計數器記錄寫入數據的字節數;f_pos,寫入的ssize_t memory_write(struct file *filp, char *buf,

    size_t count, loff_t *f_pos)

    {

    char *tmp;

    tmp = buf + count - 1;

    copy_from_user(memory_buffer, tmp, 1);

    return 1;

    }

    copy_from_user將數據從用戶空間傳送到內核空間。

    完整的“memory“驅動加入之前的所有代碼后,便組成了完整的memory驅動memory.c:在使用本驅動之前,當然需要先編譯它,方法與前面類似。加載模塊:

    insmod memory.ko

    很方便即可取消對設備的保護:

    chmod 666 /dev/memory

    如果一切順利,將有一個設備/dev/memory存在,且可以將其中寫字符串或字符,它將存儲字符串或多個字符中的最后一個。可以像這樣來操作:

    echo -n abcdef > /dev/memory

    使用cat來檢查這個設備的內容:

    cat /dev/memory

    已存的字符不會改變,除非再寫入覆蓋它或這個模塊被卸載。

    附:

    實例2 memory 驅動實驗:

    代碼 memory.c

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

    #include <linux/module.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/fs.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/proc_fs.h>#include <linux/fcntl.h>//#include

    #include <linux/uaccess.h>

    MODULE_LICENSE("Dual BSD/GPL");

    int memory_open(struct inode *inode, struct file *filp);int memory_release(struct inode *inode, struct file *filp);

    ssize_t memory_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);

    ssize_t memory_write(struct file *filp, char *buf, size_t count, loff_t *f_pos);void memory_exit(void);int memory_init(void);

    struct file_operations memory_fops = {

    read:memory_read,

    write:memory_write,

    open:memory_open,

    release:memory_release

    };

    module_init(memory_init);

    module_exit(memory_exit);

    int memory_major = 60;

    char *memory_buffer;

    int memory_init(void)

    {

    int result;

    result = register_chrdev(memory_major, "memory", &memory_fops);

    if (result < 0) {

    printk("<1>memory: can't obtain major number %d\n", memory_major);

    return result;

    }

    memory_buffer = kmalloc(1, GFP_KERNEL);

    if (!memory_buffer) {

    result = - ENOMEM;

    goto fail;

    }

    memset(memory_buffer, 0, 1);

    printk("<1>Inserting memory module\n");

    return 0;

    fail:

    memory_exit();

    return result;

    }

    void memory_exit(void)

    {

    unregister_chrdev(memory_major, "memory");

    if (memory_buffer)

    kfree(memory_buffer);

    printk("<1>Removing memory module\n");

    }

    int memory_open(struct inode *inode, struct file *filp)

    {

    return 0;

    }

    int memory_release(struct inode *inode, struct file *filp)

    {

    return 0;

    }

    ssize_t memory_read(struct file *filp, char *buf,

    size_t count, loff_t *f_pos)

    {

    copy_to_user(buf, memory_buffer, 1);

    if (*f_pos == 0) {

    *f_pos += 1;

    return 1;

    } else

    return 0;

    }

    ssize_t memory_write(struct file *filp, char *buf,

    size_t count, loff_t *f_pos)

    {

    char *tmp;

    tmp = buf + count - 1;

    copy_from_user(memory_buffer, tmp, 1);

    return 1;

    }

    · 1

    · 2

    · 3

    · 4

    · 5

    · 6

    · 7

    · 8

    · 9

    · 10

    · 11

    · 12

    · 13

    · 14

    · 15

    · 16

    · 17

    · 18

    · 19

    · 20

    · 21

    · 22

    · 23

    · 24

    · 25

    · 26

    · 27

    · 28

    · 29

    · 30

    · 31

    · 32

    · 33

    · 34

    · 35

    · 36

    · 37

    · 38

    · 39

    · 40

    · 41

    · 42

    · 43

    · 44

    · 45

    · 46

    · 47

    · 48

    · 49

    · 50

    · 51

    · 52

    · 53

    · 54

    · 55

    · 56

    · 57

    · 58

    · 59

    · 60

    · 61

    · 62

    · 63

    · 64

    · 65

    · 66

    · 67

    · 68

    · 69

    · 70

    · 71

    · 72

    · 73

    · 74

    · 75

    · 76

    · 77

    · 78

    · 79

    · 80

    · 81

    · 82

    · 83

    · 84

    · 85

    · 86

    · 87

    · 88

    · 89

    · 90

    · 91

    · 92

    · 93

    · 94

    · 95

    · 96

    · 97

    · 98

    · 99

    · 100

    · 101

    · 102

    · 103

    · 104

    · 105

    Makefile:

    obj-m := memory.o

    KERNELDIR := /lib/modules/(shelluname?r)/buildPWD:=(shell pwd)

    modules:

    (MAKE)?C(KERNELDIR) M=$(PWD) modules

    編譯:make

    生成文件中有 memory.ko, 該文件即要使用的目標模塊

    加載:sudo insmod ./memory.ko

    查看dmesg信息:dmesg | tail -n 1

    [10911.945739] Inserting memory module

    改變操作設備文件權限:sudo chmod 666 /dev/memory

    向驅動中寫入數據:echo -n abcdefg > /dev/memory

    查看驅動中保存的數據:

    [linux@ ~]cat/dev/memoryg[linux@ ]

    可見其為最后寫入的數據。

    卸載驅動:

    [linux@ ~]sudormmodmemory[linux@ ] dmesg | tail -n 2

    [10911.945739] Inserting memory module

    [11155.809076] Removing memory module

    —————————-實驗完畢

    分析

    上面代碼中主要有五個函數重點注意下:

    r

    egister_chrdev

    unregister_chrdev

    copy_to_user

    copy_from_user

    kmalloc

    /*

    * 成功:返回0

    * 失敗:-EINVAL表示申請的主設備號非法(可能是主設備號大于最大設備號)

    * -EBUSY 表示所申請的主設備號已為其它設備使用

    * 如果動態分配成功,則此函數將返回主設備號

    *

    */

    static inline int register_chrdev(

    unsigned int major, //設備驅動向內核申請主設備號,若為0則系統動態分配一個主設備號

    const char *name, //設備名

    const struct file_operations *fops //各調用的入口點

    );

    static inline void unregister_chrdev(

    unsigned int major,

    const char *name

    );

    arch/x86/lib/usercopy_32.c

    /**

    * copy_to_user: - Copy a block of data into user space.

    * @to: Destination address, in user space.

    * @from: Source address, in kernel space.

    * @n: Number of bytes to copy.

    *

    * Context: User context only. This function may sleep.

    *

    * Copy data from kernel space to user space.

    *

    * Returns number of bytes that could not be copied.

    * On success, this will be zero.

    */

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

    /**

    * copy_from_user: - Copy a block of data from user space.

    * @to: Destination address, in kernel space.

    * @from: Source address, in user space.

    * @n: Number of bytes to copy.

    *

    * Context: User context only. This function may sleep.

    *

    * Copy data from user space to kernel space.

    *

    * Returns number of bytes that could not be copied.

    * On success, this will be zero.

    *

    * If some data could not be copied, this function will pad the copied

    * data to the requested size using zero bytes.

    */

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

    在內核中動態開辟內存

    void *kmalloc(size_t size, int flags);

    size:要分配內存的大小

    flags:分配標志,以幾個方式控制kmalloc的行為

    預約申請免費試聽課

    填寫下面表單即可預約申請免費試聽!怕錢不夠?可就業掙錢后再付學費! 怕學不會?助教全程陪讀,隨時解惑!擔心就業?一地學習,可全國推薦就業!

    上一篇:嵌入式系統中RAM和ROM的疑惑及想法
    下一篇:淺談嵌入式編程的注意事項

    關于嵌入式C編程的經驗細談

    嵌入式網絡編程技巧解析

    淺談嵌入式編程的注意事項

    讓新手快速從代碼層了解什么是Linux設備驅動

    • 掃碼領取資料

      回復關鍵字:視頻資料

      免費領取 達內課程視頻學習資料

    • 視頻學習QQ群

      添加QQ群:1143617948

      免費領取達內課程視頻學習資料

    Copyright ? 2021 Tedu.cn All Rights Reserved 京ICP備08000853號-56 京公網安備 11010802029508號 達內時代科技集團有限公司 版權所有

    選擇城市和中心
    黑龍江省

    吉林省

    河北省

    湖南省

    貴州省

    云南省

    廣西省

    海南省

    欧美一级高清片,一级欧美免费大片视频,欧美三级在线电影免费 百度 好搜 搜狗
    <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <蜘蛛词>| <文本链> <文本链> <文本链> <文本链> <文本链> <文本链>