精华内容
下载资源
问答
  • LINUX设备驱动程序

    千次下载 热门讨论 2010-10-08 23:42:17
    LINUX设备驱动程序 嵌入式开发资料
  • 深入Linux设备驱动程序内核机制 你懂的 好书 今年新书 驱动
  • Linux设备驱动程序和设备文件

    千次阅读 2018-01-14 18:25:10
    Linux设备驱动程序和设备文件 设备驱动程序 一个设备驱动程序是一个管理着系统与某种特定硬件之间交互作用的程序。驱动程序在设备可理解的硬件指令和内核使用的固定编程接口之间起转换作用。驱动程序层的存在有...

    Linux设备驱动程序和设备文件

    设备驱动程序

    一个设备驱动程序是一个管理着系统与某种特定硬件之间交互作用的程序。驱动程序在设备可理解的硬件指令和内核使用的固定编程接口之间起转换作用。驱动程序层的存在有助于内核合理地保持设备独立性。
    在大多数情况下,设备驱动程序是内核的组成部分,它们不是用户进程。不过,一个驱动程序可以从内核里,也可以从用户空间进行访问。对设备的用户级访问往往要通过位于/dev目录下的特殊设备文件。内核把对这些文件操作映射到对驱动程序代码的调用上面。

    设备文件

    基本概念

    大多数硬件设备都在/dev目录中有一个对应的设备文件,网络设备除外。在/dev中的每个文件都有与之相关的主设备号和一个次设备号。内核用这些设备号把对一个设备文件的引用映射到相应的驱动程序上。主设备号标明与文件相关的驱动程序(换句话说是设备类型)。次设备号常常是指定某种给定设备类型的特定实例,次设备号有时被称为单元号。
    ls -l可以看到一个设备文件的主设备号和次设备号:

    [root@vps ~]# ls -l /dev/vda
    brw-rw---- 1 root disk 253, 0 Oct 15 15:21 /dev/vda

    设备文件分两种类型:

    1. 块设备文件:
      一个块设备文件每次读取或者写入一块数据(一组字节,通常是521的倍数),我们熟知的磁盘就是块设备,在/dev中对应的设备文件就是块设备文件。块设备文件在用ls -l查看时文件类型为b。
    2. 字符设备文件:
      字符设备每次读取或者写入一个字节。磁盘和磁带可以是块设备也可以是字符设备,而终端和打印机不行。字符设备文件在用ls -l查看时文件类型为c。
    创建设备文件

    在Linux下,一般不需要手动创建设备文件,因为在Linux下设备文件的创建有专门的udev系统来管理,当系统有新的设备出现(或者消失),会动态地管理设备文件的创建和删除。守护进程udevd监听内核传来的有关设备状态变化的消息。根据/etc/udev/lib/udev两个目录的配置信息,在找到设备或者断开设备的时候,udevd能够自动采取相应措施。在默认情况下,它只创建/dev里的设备文件。手动创建设备文件用mknod命令来创建,语法为:

    mknod filename type major minor
    • filename:要创建的设备文件名;
    • type:设备类型,c代表一个字符设备,b代表一个块设备;
    • major:主设备号;
    • minor:次设备号;

    参考文献

    Unix/Linux系统管理手册|第13章
    CSDN博客|主设备号和次设备号

    展开全文
  • 精通LINUX设备驱动程序开发 中文扫描

    千次下载 热门讨论 2012-09-05 16:08:21
    精通LINUX设备驱动程序开发 Linux 中文 扫描 高清
  • Linux设备驱动程序教程将为您提供有关如何为Linux操作系统编写设备驱动程序的所有必要信息。 本文包含一个易于遵循的实用Linux驱动程序开发示例。 我们将讨论以下内容: 内核日志系统 如何使用角色设备 如何使用...

    翻译来自:
    https://www.apriorit.com/dev-blog/195-simple-driver-for-linux-os
    代码下载

    此Linux设备驱动程序教程将为您提供有关如何为Linux操作系统编写设备驱动程序的所有必要信息。 本文包含一个易于遵循的实用Linux驱动程序开发示例。 我们将讨论以下内容:

    • 内核日志系统
    • 如何使用角色设备
    • 如何使用内核中的用户级内存

    我们将使用Linux内核版本2.6.32。 我们可以使用更新的版本,但是它们的API可能已被修改,因此可能与我们的示例和构建系统中使用的API不同。 学习本教程后,您将熟悉为Linux操作系统编写设备驱动程序或内核模块的过程。

    概述

    Linux有一个单片内核。 因此,为Linux编写设备驱动程序需要与内核进行组合编译。 另一种方法是将驱动程序实现为内核模块,在这种情况下,您无需重新编译内核即可添加其他驱动程序。 我们将关注第二个选项:内核模块。

    在其基础上,模块是专门设计的目标文件。 使用模块时,Linux会将它们加载到其内核空间,从而将它们链接到内核。 Linux内核是使用C编程语言和Assembler开发的。 C实现了内核的主要部分,而Assembler实现了依赖于体系结构的部分。 不幸的是,这些是我们用于编写Linux设备驱动程序的唯一两种语言。 我们不能使用用于Microsoft Windows操作系统内核的C ++,因为Linux内核源代码的某些部分 - 特定的头文件 - 可能包含来自C ++的关键字(例如, delete或new ),而在Assembler中我们可能会遇到诸如’ : : ‘词汇。

    我们在内核上下文中运行模块代码。 这需要开发人员非常专注,因为它需要承担额外的责任:如果开发人员在实现用户级应用程序时出错,在大多数情况下这不会导致用户应用程序之外的问题; 但是如果开发人员在实现内核模块时出错,后果将是系统级别的问题。 幸运的是,Linux内核有一个很好的功能,可以抵御模块代码中的错误。 当内核遇到非严重错误(例如,空指针解除引用)时,您将看到oops消息(Linux操作期间无意义的故障称为oops ),之后将卸载故障模块,允许内核和其他模块像往常一样工作。 此外,您还可以在内核日志中找到准确描述此错误的记录。 但请注意,不建议在oops消息之后继续工作,因为这样做可能会导致不稳定和内核恐慌。

    内核及其模块本质上代表一个程序模块 - 因此请记住,单个程序模块使用单个全局命名空间。 为了最小化它,您必须观察模块导出的内容:导出的全局字符必须唯一命名(常用的解决方法是简单地使用将字符导出为前缀的模块的名称)并且必须剪切到最低限度。

    装载和卸载模块

    要创建一个简单的示例模块,我们不需要做太多工作。 这里有一些代码可以证明这一点:

    #include <linux/init.h>
    #include <linux/module.h>
    
    static int my_init(void)
    {
                           return  0;
    }
    
    static void my_exit(void)
    {
                           return;
    }
    
    module_init(my_init);
    module_exit(my_exit);

    这个模块唯一做的两件事就是加载和卸载自己。 要加载Linux驱动程序,我们调用my_init函数,并卸载它,我们调用my_exit函数。 module_init和module_exit宏通知内核有关驱动程序的加载和卸载。 my_init和my_exit函数必须具有相同的签名,这些签名必须完全如下:

    int init(void);
    void exit(void);

    如果模块需要某个内核版本并且必须包含有关版本的信息,我们需要链接linux / module.h头文件。 尝试加载为另一个内核版本构建的模块将导致Linux操作系统禁止其加载。 出现这种情况的原因是:内核API的更新经常被释放,当您调用其签名已更改的模块函数时,会对整个堆栈造成损害。 module_init和module_exit宏在linux / init.h头文件中声明。

    注册角色设备

    上面的示例模块非常简单; 现在我们将开始处理更复杂的事情。 然而,这个简短的Linux内核驱动程序教程的目的之一是展示如何使用登录内核以及如何与设备文件交互。 这些工具可能很简单,但它们可以为任何驱动程序派上用场,并且在某种程度上,它们使内核模式开发过程更加丰富。

    首先,这里有一些有关设备文件的有用信息。 通常,您可以在/ dev文件夹中找到设备文件。 它们促进了用户和内核代码之间的交互。 如果内核必须接收任何内容,您只需将其写入设备文件即可将其传递给提供此文件的模块; 从设备文件中读取的任何内容都来自提供此文件的模块。 我们可以将设备文件分为两组:字符文件和块文件。 字符文件是非缓冲的,而块文件是缓冲的。 正如其名称所暗示的那样,字符文件允许您逐个字符地读取和写入数据,而块文件允许您只写入整个数据块。 我们将讨论块文件超出本文的范围,并将直接获得字符文件。

    Linux系统有一种通过主设备号识别设备文件的方法, 主设备号识别服务设备文件或一组设备的模块,以及次要设备号 ,用于识别主设备号指定的一组设备中的特定设备。 在驱动程序代码中,我们可以将这些数字定义为常量,也可以动态分配它们。 如果已经使用了定义为常量的数字,系统将返回错误。 当动态分配一个数字时,该函数保留该数字以禁止其他任何数字使用它。

    指定设备的名称

    下面引用的函数用于注册字符设备:

    int register_chrdev (unsigned int   major,
                         const char *   name,
                         const struct   fops);
                         file_operations *

    在这里,我们指定要注册它的设备的名称和主要编号,之后将链接设备和file_operations结构。 如果我们为主参数指定零,该函数将自己分配一个主设备号(即它返回的值)。 如果返回的值为零,则表示成功,而负数表示错误。 两个设备编号均在0-255范围内指定。

    我们将设备名称作为name参数的字符串值传递(如果模块注册单个设备,则此字符串也可以传递模块的名称)。 然后,我们使用此字符串来标识/ sys / devices文件中的设备。 读取,写入和保存等设备文件操作由存储在file_operations结构中的函数指针处理。 这些函数由模块实现,并且指向标识该模块的module结构的指针也存储在file_operations结构中。 在这里你可以看到2.6.32内核版本结构:

    
    struct file_operations {
           struct module *owner;
           loff_t (*llseek) (struct file *, loff_t, int);
           ssize_t (*read) (struct file *, char *, size_t, loff_t *);
           ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
           int (*readdir) (struct file *, void *, filldir_t);
           unsigned int (*poll) (struct file *, struct poll_table_struct *);
           int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
           int (*mmap) (struct file *, struct vm_area_struct *);
           int (*open) (struct inode *, struct file *);
           int (*flush) (struct file *);
           int (*release) (struct inode *, struct file *);
           int (*fsync) (struct file *, struct dentry *, int datasync);
           int (*fasync) (int, struct file *, int);
           int (*lock) (struct file *, int, struct file_lock *);
        ssize_t (*readv) (struct file *, const struct iovec *, unsigned long,
              loff_t *);
        ssize_t (*writev) (struct file *, const struct iovec *, unsigned long,
              loff_t *);
        };

    file_operations结构

    如果file_operations结构包含一些不需要的函数,您仍然可以使用该文件而不实现它们。 指向未实现函数的指针可以简单地设置为零。 之后,系统将负责该功能的实现并使其正常运行。 在我们的例子中,我们将实现read函数。

    由于我们要确保只使用我们的Linux驱动程序操作单一类型的设备,因此我们的file_operations结构将是全局和静态的。 相应地,在它创建之后,我们需要静态填充它。 在这里你可以看到这是如何完成的:

    
    static struct file_operations simple_driver_fops = 
    {
        .owner   = THIS_MODULE,
        .read    = device_file_read,
    };

    THIS_MODULE宏的声明包含在linux / module.h头文件中。 我们将宏转换为指向所需模块的模块结构的指针。 稍后,我们将用原型编写函数体,但是现在我们只有指向它的指针,即device_file_read 。

    ssize_t device_file_read (struct file *, char *, size_t, loff_t *);

    file_operations结构允许我们编写几个函数来执行和撤销设备文件的注册。

    static int device_file_major_number = 0;
    static const char device_name[] = "Simple-driver";
    static int register_device(void)
    {
            int result = 0;
            printk( KERN_NOTICE "Simple-driver: register_device() is called." );
            result = register_chrdev( 0, device_name, &simple_driver_fops );
            if( result < 0 )
            {
                printk( KERN_WARNING "Simple-driver:  can\'t register character device with errorcode = %i", result );
                return result;
            }
            device_file_major_number = result;
            printk( KERN_NOTICE "Simple-driver: registered character device with major number = %i and minor numbers 0...255"
                 , device_file_major_number );
            return 0;
    }

    device_file_major _number是一个包含主设备号的全局变量。 当驱动程序的生命周期到期时,此全局变量将撤消设备文件的注册。

    printk Fucntion

    我们已经列出并提到了几乎所有的功能,最后一个是printk功能。 这个函数的声明包含在linux / kernel.h文件中,它的任务很简单:记录内核消息。 毫无疑问,请注意KERN_NOTICE和KERN_WARNING前缀,这些前缀出现在printk的所有列出的格式字符串中。 您可能已经猜到, NOTICE和WARNING表示消息的优先级。 级别从最无关紧要的KERN_DEBUG到关键的KERN_EMERG ,提醒内核不稳定。 这是printk函数和printf库函数之间的唯一区别。

    printk函数形成一个字符串,我们将其写入循环缓冲区, klog守护程序将其读取并将其发送到系统日志。 printk函数的实现允许从内核中的任何地方调用它。 最糟糕的情况是循环缓冲区溢出,这意味着最旧的消息不会记录在日志中。

    下一步是编写一个函数来恢复设备文件的注册。 如果成功注册了设备文件,则device_file_major_number的值将不为零。 这允许我们使用nregister_chrdev function撤销文件的注册,我们在linux / fs.h文件中声明了该nregister_chrdev function 。 主设备号是此函数的第一个参数,后跟包含设备名称的字符串。 register_chrdev和unresister_chrdev函数以类似的方式起作用。

    要注册设备,我们使用以下代码:

    
    void unregister_device(void)
    {
        printk( KERN_NOTICE "Simple-driver: unregister_device() is called" );
        if(device_file_major_number != 0)
        {
            unregister_chrdev(device_file_major_number, device_name);
        }
    }

    使用在用户模式下分配的内存

    我们要编写的函数将从设备中读取字符。 此函数的签名必须适合file_operations结构中的签名:

    ssize_t (*read) (struct file *, char *, size_t, loff_t *);

    让我们看看第一个参数,即指向file结构的指针。 此file结构允许我们获取有关我们正在使用的文件的必要信息,有关此当前文件的私有数据的详细信息,等等。 已读取的数据使用第二个参数(即缓冲区)分配给用户空间。 读取的字节数在第三个参数中定义,我们从第四个参数中定义的某个偏移量开始读取字节。 执行该函数后,必须返回已成功读取的字节数,之后必须刷新偏移量。

    用户在用户模式地址空间中分配特殊缓冲区。 而read函数必须执行的另一个操作是将信息复制到此缓冲区。 来自该空间的指针指向的地址和内核地址空间中的地址可以具有不同的值。 这就是我们不能简单地取消引用指针的原因。 使用这些指针时,我们有一组特定的宏和函数,我们在asm / uaccess.h文件中声明。 在我们的例子中,最合适的函数是copy_to_user() 。 它的名字不言而喻:它只是将特定数据从内核缓冲区复制到用户空间中分配的缓冲区。 此外,它还验证指针是否有效以及缓冲区大小是否足够大。 因此,可以相对容易地处理驱动器中的错误。 这是copy_to_user原型的代码:

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

    首先,该函数必须接收三个指针作为参数:指向缓冲区的指针,指向数据源的指针,以及指向复制的字节数的指针。 正如我们所提到的,错误返回的值不是零,并且在成功执行的情况下,该值将为零。 该函数包含_user宏,其任务是执行文档处理。 它有另一个有用的应用程序,它允许我们分析代码是否正确使用地址空间中的指针; 这是使用稀疏分析器完成的,稀疏分析器执行静态代码分析。 确保始终将用户地址空间指针标记为_user 。

    本教程仅包含没有实际设备的Linux驱动程序编程示例。 如果在读取设备文件后不需要返回除文本字符串以外的任何内容,那么这就足够了。

    这是实现read功能的代码:

    static const char    g_s_Hello_World_string[] = "Hello world from kernel mode!\n\0";
    static const ssize_t g_s_Hello_World_size = sizeof(g_s_Hello_World_string);
    static ssize_t device_file_read(
                            struct file *file_ptr
                           , char __user *user_buffer
                           , size_t count
                           , loff_t *position)
    {
        printk( KERN_NOTICE "Simple-driver: Device file is read at offset = %i, read bytes count = %u"
                    , (int)*position
                    , (unsigned int)count );
        /* If position is behind the end of a file we have nothing to read */
        if( *position >= g_s_Hello_World_size )
            return 0;
        /* If a user tries to read more than we have, read only as many bytes as we have */
        if( *position + count > g_s_Hello_World_size )
            count = g_s_Hello_World_size - *position;
        if( copy_to_user(user_buffer, g_s_Hello_World_string + *position, count) != 0 )
            return -EFAULT;    
        /* Move reading position */
        *position += count;
        return count;
    }

    构建内核模块的系统

    在我们为驱动程序编写代码之后,是时候构建它并查看它是否像我们期望的那样工作。 在早期的内核版本(例如2.4)中,构建模块需要来自开发人员的更多动作:编译环境需要单独准备,编译本身需要GCC编译器。 只有在那之后,开发人员才会收到一个* .o文件 - 一个可以加载到内核的模块。 幸运的是,这些时间早已过去,现在这个过程要简单得多。 今天,大部分工作都是由makefile完成的:它启动内核构建系统,并为内核提供有关构建模块所需组件的信息。 从单个源文件构建的模块需要makefile中的单个字符串。 创建此文件后,您只需要启动内核构建系统:

    obj-m := source_file_name.o

    如您所见,这里我们已将源文件名分配给模块,该文件将是* .ko文件。

    相应地,如果有多个源文件,则只需要两个字符串

    obj-m := module_name.o 
    module_name-objs := source_1.o source_2.o … source_n.o

    make命令初始化内核构建系统:

    要构建模块:

    make –C KERNEL_MODULE_BUILD_SYSTEM_FOLDER M=`pwd` modules

    要清理构建文件夹:

    make –C KERNEL_MODULES_BUILD_SYSTEM_FOLDER M=`pwd` clean

    模块构建系统通常位于 /lib/modules/uname -r/build中。 现在是时候准备模块构建系统了。 要构建第一个模块,请从构建系统所在的文件夹中执行以下命令:

    make modules_prepare

    最后,我们将我们学到的所有内容组合到一个makefile中:

    TARGET_MODULE:=simple-module
    # If we are running by kernel building system
    ifneq ($(KERNELRELEASE),)
        $(TARGET_MODULE)-objs := main.o device_file.o
        obj-m := $(TARGET_MODULE).o
    # If we running without kernel build system
    else
        BUILDSYSTEM_DIR:=/lib/modules/$(shell uname -r)/build
        PWD:=$(shell pwd)
    all : 
    # run kernel build system to make module
        $(MAKE) -C $(BUILDSYSTEM_DIR) M=$(PWD) modules
    clean:
    # run kernel build system to cleanup in current directory
        $(MAKE) -C $(BUILDSYSTEM_DIR) M=$(PWD) clean
    load:
        insmod ./$(TARGET_MODULE).ko
    unload:
        rmmod ./$(TARGET_MODULE).ko
    endif

    load目标加载构建模块, unload目标将其从内核中删除。

    在我们的教程中,我们使用了main.c和device_file.c中的代码来编译驱动程序。 生成的驱动程序名为simple-module.ko。

    加载和使用模块

    从源文件文件夹执行以下命令允许我们加载构建的模块:

    sudo make load

    执行此命令后,驱动程序的名称将添加到/proc/modules文件中,而模块注册的设备将添加到/proc/devices文件中。 添加的记录如下所示:

    Character devices: 1 mem 4 tty 4 ttyS … 239 Simple-driver …

    cat /proc/devices | grep  Simple-driver
    $ 239 Simple-driver

    前三个记录包含添加的设备的名称以及与之关联的主设备号。 次编号范围(0-255)允许在/ dev虚拟文件系统中创建设备文件。

    sudo mknod /dev/simple-driver c 239 0

    在我们创建设备文件之后,我们需要执行最终验证以确保我们所做的工作按预期工作。 要验证,我们可以使用cat命令显示内容:

    cat /dev/simple-driver
    $ Hello world from kernel mode!

    参考资料

    1. Linux设备驱动程序, Jonathan Corbet , Alessandro Rubini和Greg Kroah-Hartman的第3版: http://lwn.net/Kernel/LDD3/
    2. Peter Jay Salzman和Ori Pomeranz撰写的Linux内核模块编程指南: http://tldp.org/LDP/lkmpg/2.6/html/lkmpg.html
    3. Linux Cross Reference http://lxr.free-electrons.com/ident

    代码下载

    Demo代码下载

    尾文

    device_file.c有一点编译问题,这样修正。

    copy_to_user -> raw_copy_to_user

    查看内核日志

    dmesg

    玩~

    展开全文
  • LINUX设备驱动程序第三版.pdf免费下载链接(.pdf书籍的优点是便于直接在电脑中保存有电脑就可以阅读,如果觉得这本书给你提供到了很大的帮助,可以去书店补一本纸质版) 资源保存在腾讯微云上,下载不需要微云...

    LINUX设备驱动程序第三版.pdf免费下载链接(.pdf书籍的优点是便于直接在电脑中保存有电脑就可以阅读,如果觉得这本书给你提供到了很大的帮助,可以去书店补一本纸质版)

    资源保存在腾讯微云上,下载不需要微云客户端,有需要这个资源的同学,请帅气的拿走,如果链接失效请在评论区留言。
    (如果想要其他学习资源,请在评论区留言,我会继续上传。)

    下载次数超过限制,请保存到自己QQ号的微云再下载哦!!!!!
    链接:https://share.weiyun.com/5Lkhvpe 密码:h34mk6
    在这里插入图片描述
    在这里插入图片描述

    cout<<"点个赞吧!"<<endl;
    print("点个赞吧!\n");
    System.out.println("点个赞吧!");
    document.write("点个赞吧!");
    //如果觉得我的分享对您有所帮助,请您点个赞同吧!你们的每一个赞我都在用心感受,每一个赞都是我继续努力的动力。:-)
    
    展开全文
  • Linux设备驱动程序开发基础(PPT)

    千次下载 热门讨论 2008-04-10 23:38:26
    介绍Linux设备驱动程序开发相关知识,开发流程.驱动程序结构.
  • LINUX设备驱动程序第三版配套源码

    千次下载 热门讨论 2007-04-13 01:13:15
    LINUX设备驱动程序第三版配套源码
  • 深入Linux设备驱动程序内核机制

    千次阅读 2012-02-21 20:24:16
    深入Linux设备驱动程序内核机制 陈学松 著 ISBN978-7-121-15052-4 2012年1月出版 定价:98.00元 16开 540页 内 容 简 介 这是一本系统阐述Linux设备驱动程序技术内幕的专业书籍,它的侧重点不是讨论如何在...

    深入Linux设备驱动程序内核机制

    陈学松 著

    ISBN978-7-121-15052-4

    2012年1月出版

    定价:98.00元

    16开

    540页

    内 容 简 介

    这是一本系统阐述Linux设备驱动程序技术内幕的专业书籍,它的侧重点不是讨论如何在Linux系统下编写设备驱动程序,而是要告诉读者隐藏在这些设备驱动程序背后的那些内核机制及原理。作者通过对Linux内核源码抽丝剥茧般的解读,再辅之以精心设计的大量图片,使读者在阅读完本书后对驱动程序前台所展现出来的那些行为特点变得豁然开朗。

    本书涵盖了编写设备驱动程序所需要的几乎所有的内核设施,比如内核模块、中断处理、互斥与同步、内存分配、延迟操作、时间管理,以及新设备驱动模型等内容。为了避免读者迷失在某一技术细节的讨论当中,本书在一个比较高的层面上进行展开,以一种先框架再细节的结构安排极大地简化了读者的阅读与学习。

    本书不仅适合那些在Linux系统下从事设备驱动程序开发的专业技术人员阅读,也同样适合有志于从事Linux设备驱动程序开发或对Linux设备驱动程序及Linux内核感兴趣的在校学生等阅读。对于没有任何Linux设备驱动程序开发经验的初学者,建议先阅读那些讨论“如何”在Linux系统下编写设备驱动程序的入门书籍,然后再阅读本书来理解“为什么”要以这样或者那样的方式来编写设备驱动程序。

    推 荐 序

    这不是一本单纯的关于Linux设备驱动程序入门的书。它是给有一定的Linux设备驱动程序编写经验并且对众多Linux底层设备驱动内幕机制感兴趣的读者量身定制的。与市面上已经出版的Linux相关方面的图书的不同之处在于,本书并不着重于全面描述Linux内核,也不只是简单地告诉你如何去写一个Linux下的设备驱动程序。它是从设备驱动程序的视角出发,深入到Linux内核去剖析那些和驱动程序实现机制密切相关的技术内幕。比如让你理解为什么在这个地方驱动程序应该使用work queue而不是tasklet,为什么在中断处理例程里应该使用spin_lock而不是mutex_lock……因为只有当你对驱动程序中使用的各种内核实现有了清晰的认识,你才能在日常的工作当中随心所欲地驾驭它们,写出更高性能更安全的代码。知其然,更知其所以然,对于沉迷于技术领域的人而言,这种不断探索的好奇心是对技术工作能长期保持热情的一个基本特质。相对于市面上已经出版的相关书籍而言,本书具有以下两个鲜明的特色:

    细节揭秘

    目前市场上已经出版的Linux内核和驱动程序方面的书籍,大体上可分为两种。一种是侧重于内核本身,鉴于目前Linux的内核源码已经十分庞大,这些讲解内核的书有些本身非常全面,作者的写作态度也非常严谨,比如Deep Understanding Linux Kernel,还有新近出版的Professional Linux Kernel Architecture,后者几乎涵盖了新版Linux内核中绝大部分重要的构件,但也正因如此,这样的书籍就不可能在与驱动程序相关的机制上留下太多笔墨。另外还有一种是专门讲解Linux驱动方面的书籍,典型的有LinuxDevice Driver和Essential Linux Device Driver。这些书着重于介绍Linux驱动的基本概念和架构,但是对于想了解更多幕后的技术细节的读者来说,《深入Linux设备驱动程序内核机制》一书可提供更详细的资源和帮助。通常当你想深入理解一些一般书籍没有描述的机制时,你可能会采用在线搜索或查看源码的方式,但有时这不仅费时也未必能得到满意的答案。本书提供了另一途径让你更系统、有效地理解这些内核机制。我相信对于广大忙于在校学习、职场深造或课题攻关的读者来说,本书可提供很多有益的帮助。

    图片说理

    这本书另外一个很大的特点是,作者大量使用其精心设计的图片来帮助你清晰地理解一些复杂的概念、流程和架构。这在中文版原创的图书中是很难能可贵的,相对而言外文书在这方面做得就要好很多。形象直观的图片胜过大量的文字,也能节省读者大量的时间。可以看到,本书的作者在这一方面做了很大的努力去加以完善,在我看来,这是一个非常好的尝试。本书作者当前正在AMD上海研发中心从事Linux显卡驱动等系统软件方面的研发工作,能在繁忙的工作之余,通过对自己学习和实践经验的总结写下这样一本书,对增进国内读者的 Linux 系统开发能力将起到很大的作用。我相信,如果作者有足够的时间与精力的话,这本书还可以进一步完善,包括在某些技术方面可以有更精细的描述。

    AMD图形软件架构师  PMTS  俞辉

    2011年8月24日于加拿大

    前 言

    在Linux庞大的源码树中,设备驱动程序部分的代码已经占了相当大的比例。现实的工作中,大量的采用Linux系统的平台需要设备驱动程序才能把Linux的内核真正运行起来,同时通过编写Linux设备驱动程序,使得我们经由亲手编写具有特权等级的代码来一探Linux内核幕后的秘密成为可能。所以,无论是从日常工作的需要还是只为单纯满足对Linux内核机制好奇心的角度来说,学习并掌握Linux设备驱动程序的编写都是非常必要的,同时也是一件非常有趣且有意义的事情。

    初衷与定位

    这本书并不仅仅是单纯地讨论如何在Linux系统下编写一个设备驱动程序,因为关于这方面的内容,市面上已经有大量类似的图书可供参考。本书的总体思想是从内核的角度来看设备驱动程序,从设备驱动程序的角度深入到内核中,比如通过对spin_lock以及spin_lock_irq等内核源码的分析,来告诉你在什么场合下应该使用spin_lock,什么场合下又应该选择spin_lock_irq。还有,比如我们几乎每天都会在设备驱动程序所代表的内核模块中使用MODULE_LICENSE("GPL")这样的声明,这个声明是如此地平凡,以至于我们常常忽略它存在的价值。但是在某个夜深人静的夜晚,感觉长夜漫漫无心睡眠时,在你内心深处的某个地方是否会想过,这个声明对一个内核模块而言,它到底意味着什么,如果没有它,加载这样一个模块对系统又会造成什么样的影响,如此等等,读者都可以在阅读本书的过程中找到答案。

    很显然,只有当你清楚地理解了一个东西的内在机制,你才能更好地去使用它们,如果不幸在使用过程中出现问题,也才可以快速将其定位并最终予以解决。台湾著名技术作家侯捷曾引林雨堂先生在《朱门》中的一句话,“只用一样东西,不明白它的道理,实在不高明”,来描述他当时写作时的心境,其实这句话也同样适合我用来阐明写作本书的初衷之一。

    但是这并不意味着只有Linux系统下设备驱动程序的编写老手才适合阅读本书,因为我在本书写作过程中,一般会先给出一个总体的框架,然后在此基础上对Linux提供给设备驱动程序使用的每一个常见而重要的内核设施进行细致地分析,同时辅之以验证性质的代码来使得这种略嫌抽象的讨论具体化,以激发读者对技术探索的兴趣。所以即便是入门级的读者,也可以通过阅读本书来加深对Linux下编写设备驱动程序的理解。

    另外要说的是,读者不应该寄希望于阅读两三本书就可以掌握Linux下设备驱动程序编写的精髓,所有的书籍只能在大体上给你一个参考借鉴的作用,真正的理解还要靠读者自己去努力,诚所谓“纸上得来终觉浅,绝知此事要躬行”。

    编排与范围

    在本书的结构编排上,我努力使各章节独立起来,但是少量的向前或者向后引用还是必不可少的。但是总体上,我将最基本的篇章尽量放到前面,一些加强型或者高级点的话题尽量放到后边。在描述驱动程序内核机制方面,为了避免单纯的代码解释所带来的抽象感,我会使用具体的例子来将所能看到的驱动程序的前台行为和它的幕后机制串联起来,以帮助读者建立起全面立体的设备驱动程序架构蓝图。不过Linux对某些特性的支持因为考虑到各种平台和性能等诸多因素,其实现很可能会有多种不同的方法,比如从内核态驱动程序向用户空间导出信息的文件系统方面,就至少有proc和sysfs两种形式。因此本书在描述具体的例子时,一定是遵循其中的某种实现,在诸多实现机制的选择上,本书会从实用性和实时性角度出发,采用内核中最新引入或者是最有发展前景的实现,对于某些即将过时的实现机制(因为兼容或者代码维护工作量的关系,一些老的机制可能依然残留在新版的内核代码中),除非出于技术细节的对比或者从增加知识面的角度考虑会有所涉及,否则将不会作为本书的主线。

    在代码的引用上,为了突出功能主线部分和削减本书的篇幅,我会删除代码中用来增加调试信息、性能增强及防御性代码这些部分。对于系统体系架构相关的代码,我主要以x86与ARM平台为主,因为这两者是当前最流行的两种处理器架构。关于本书所参考的Linux内核源码的版本,在本书刚开始写作时参考的是2.6.35的版本,在写作的中后期,已经将内核版本更新到了2.6.39,在本书的修订阶段,我已经努力将之前完成的内容更新到了2.6.39。当然,因为作者时间精力所限,加之Linux内核本身就博大精深,内核版本也一直在不断更新变化中,所以书中肯定还会有这样那样潜在的错误,希望读者朋友们能不吝批评指正,以使我们得以共同提高。

    创作历程

    我有幸自参加工作以来,在Linux下从事设备驱动程序相关的开发工作已经有9年多的时间,这期间在Linux上所接触的平台既有x86,也有ARM,甚至包括少量的PowerPC。在我看来,学习某一操作系统下的设备驱动程序的编写,主要包含两个方面:一个是该操作系统本身对设备驱动程序框架的支持,也可以称之为设备驱动模型,另一个则是对要驱动的硬件的理解。对于后者,设备驱动程序开发者将要面对各种各样的硬件设备,了解它们的最好也最直接的方法当然是这样硬件的datasheet。前者则主要和操作系统息息相关,比如在Linux系统下开发设备驱动程序,必然要熟练掌握Linux为设备驱动的编写所提供的各种内核实施及相关的各种数据结构,本书的内容主要就是探讨Linux内核为设备驱动程序编写所提供的所有这些设施的幕后技术。

    本书最早的写作酝酿大约在2010年10月份前后,在此之前,或者是出于自己对以往积累的技术总结的需要,或者是出于将自己的一些技术心得与同行分享的目的,总之,我陆陆续续在一些论坛上发表了若干剖析Linux设备驱动程序内核机制的帖子,这些帖子最终使我萌发了用一本书来总结自己以往的Linux设备驱动程序开发经验的想法。我把最初的大约一章半的稿子发给了电子工业出版社,很快就得到了策划编辑张春雨先生的肯定,接下来也很顺利地通过了选题的论证,这之后就是一段极其漫长且非常辛苦的写作过程。时间是最大的挑战,由于白天需要工作,写作的时间只能是留给夜晚或者周末,在写作最紧张的时刻,经常要写到凌晨2点多。除了时间上的困难之外,如何将一个技术点用最透彻最简洁的语言描述清楚,如何对Linux内核中纷繁复杂的内容进行取舍,这些也都是非常耗费精力的事情。技术本身的理解也许并不困难,难在如何去把你心中掌握的东西清晰准确地以文字的方式表达出来,这不同于论坛的发帖,可以非常自由甚至随心所欲,写书的话,必须考虑它的完整性、逻辑性以及可读性,同时还要考虑将来潜在的读者群。尤其是如果你想认认真真写一本书的话,有时候甚至需要反复推敲一个技术点的表达方式。在写作灵感枯竭的时候,看着时间飞快掠过,而眼前的文档却没有留下几行字,那种强烈的挫折感与沮丧感真得会让人动摇自己的信念:自己是否还能坚持下去?!所以当这本书即将出版时,我还很有些恍惚,不敢相信自己居然磕磕绊绊地最终完成了这些书稿。

    意见反馈

    读者如果在阅读本书的过程中有任何意见或者建议,欢迎通过下面的E-mail与我取得联系:ricard_chen@yahoo.com。

    关于本书使用到的源代码,读者可在www.embexperts.com网站上下载。另外,关于本书后续的一些勘误、某些技术细节方面的讨论也会在该网站相应的版面上进行。

    致谢

    首先,我要感谢我的家人,如前所述,写书占去了我大量的业余时间,我的父母和怀孕的妻子在此期间承担了几乎所有的家务劳动,替我捣腾出不少的写作时间,感谢她们!我的宝贝女儿在今年8月15日健康出生,成为我的家庭中新的一员,这本书也正好可以作为父亲的见面礼送给她——可爱的萌萌同学。

    其次是电子工业出版社的张春雨与白涛编辑,从选题的论证到文字编辑,他们都付出了极其辛苦的劳动并且提出了很多有益的建议,那些逝去的不堪回首岁月里满眼尘封的E-mail见证了这一点!当然,还要感谢我现在所效力的AMD公司,因为它使得我不必为生活所迫去写一本书,对技术的热情与兴趣才是我最终得以坚持下来的最大因素。

    最后,在本书的审核方面,AMD的PMTS及显卡驱动软件架构师俞辉在百忙中为本书作序并审核了部分章节,AMD上海研发中心LinuxGraphic Base Driver团队的Lisa Wu及研发经理刘刚也为本书的写作提供了支持,诺基亚与西门子的研发经理胡兵全审核了本书第1章及第12章,EMC的PE Thomas审核了本书第3章及第4章。Marvell的资深软件工程师JamesLai亦审核了本书部分章节并有宝贵意见,在此一并表示感谢!

    陈学松

    2011年8月29日于上海

     

    展开全文
  • Linux设备驱动程序(LDD)第三版英文PDF(附书中源码)

    千次下载 热门讨论 2013-04-11 23:40:47
    国外经典Linux驱动书籍《Linux设备驱动程序(LDD)第三版英文PDF》,高清PDF并附有书中源码!
  • LINUX设备驱动程序第三版书中源码

    千次下载 热门讨论 2010-04-22 13:07:59
    linux device drivers sourcecode LINUX设备驱动程序第三版书中源码 书中的代码是在2.6.10版本下调试的。其它版本的内核在运行时要做修改。
  • linux设备驱动程序简介

    千次阅读 2011-08-19 08:36:00
    linux设备驱动程序的作用 设备 驱动程序就像一个个的“黑盒子”,使某个特定硬件响应一个定义良好的内部编程接口,这些操作完全隐藏了设备的工作细节。用户的操作通过一组标准化的调用执行,而这些调用独立于特定的...
  • linux设备驱动程序第三版(英文版)

    千次下载 热门讨论 2007-04-13 00:17:30
    linux设备驱动程序第三版(英文版),基于2.6的内核。 linux内核开发必备
  • 什么是设备驱动程序同一个应用软件可以在不同的硬件平台的上运行。同样的open函数可以操作不同的硬件设备,实现设备无关性。这些功能的实现都离不开设备确定函数的支持。设备驱动程序是操作系统内核的内容。应用程序...
  • 设备驱动程序
  • Linux设备驱动程序设计

    千次阅读 2006-08-29 12:35:00
    摘要:本文介绍了Linux设备驱动程序设计的相关基本知识,总结了2.4与2.6不同版本内核下Linux设备驱动程序的设计模版与编写方法,并给出了一个实例化的设备驱动程序样本。关键词:静态链接模块,可加载模块,字符设备...
  • linux设备驱动程序注册过程详解

    千次阅读 2017-01-11 14:16:40
    Linux的驱动程序注册过程,大致分为两个步骤: ... 下面以内核提供的示例代码pci-skeleton.c,详细说明一个pci设备驱动程序的注册过程。...所有的设备驱动程序都会有如下两行代码: 1922 module_init(netd
  • 设备驱动程序的作用  从一个角度看,设备驱动程序的作用在于提供机制,而不是策略。在编写驱动程序时,程序员应该特别注意下面这个基本概念:编写访问硬件的内核代码时,不要给用户强加任何特定策略。因为不同...
  • Linux设备驱动程序工作原理

    千次阅读 2019-01-08 00:01:17
    Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全... 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件...
  • Linux设备驱动程序分类转

    千次阅读 2008-10-29 17:08:00
    1.1 Linux设备驱动程序分类 Linux设备驱动程序在Linux的内核源代码中占有很大的比例,源代码的长度日益增加,主要是驱动程序的增加。在Linux内核的不断升级过程中,驱动程序的结构还是相对稳定。在2.0.xx到2.2.xx的...
  • Linux无线设备驱动
  • 实例浅议linux设备驱动程序的编写

    千次阅读 2014-02-08 15:32:16
    我是2012年2月份在亚马逊买了《Linux设备驱动程序》一书,期间断断续续的读了好几次,前几章都读烂了,最后终于在去年完整的读完了一遍,期间的感受就是难,主要难在对于一个初学者,不是那么容易去实践,可能也是...
  • 一个完整的设备驱动程序应该包含了:1module_init(gpio_init)函数,指定模块加载时做的事情 module_exit(gpio_exit)函数,指定模块卸载时做的事情2file_oprations结构体,指定open指针和read指针注意file_oprations...
  • 在做Linux设备驱动程序安装实验时,执行gcc -c mydev.c产生fatal error: linux/module.h: No such file or directory错误信息 编程时需要调用内核代码的头文件,这些文件在/usr/src目录下,一般以“linux”开头,在...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 204,723
精华内容 81,889
关键字:

linux设备驱动程序

linux 订阅