zoukankan      html  css  js  c++  java
  • 【Linux开发】linux设备驱动归纳总结(一):内核的相关基础概念

    linux设备驱动归纳总结(一):内核的相关基础概念

    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    .linux设备驱动的作用

    内核:用于管理软硬件资源,并提供运行环境。如分配4G虚拟空间等。

    linux设备驱动:是连接硬件和内核之间的桥梁。

    linux系统按个人理解可按下划分:

    应用层:包括POSIX接口,LIBC,图形库等,用于给用户提供访问 内核的接口。属于用户态,ARM运行在用户模式(usr) 者系统模式(sys)下。

    内核层:应用程序调用相关接口后,会通过系统调用,执行SWI 令切换ARM的工作模式到超级用户(svc)模式下,根据用 户函数的要求执行相应的操作。

    硬件层:硬件设备,当用户需要操作硬件时,内核会根据驱动接口 操作硬件设备

    图结构如下:

    举一个相对比较邪恶的类比:

    在深圳的酒店经常会在门缝看到一些卡片,上面说可以通过打电话送货上门提供某中服务。


    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    .内核代码树介绍

    linux-2.6.29

    |-arch : 包含和硬件体系结构相关的代码

    |-block : 硬盘调度算法,不是驱动

    |-firmware : 固件,如BOIS

    |-Documentation: 标准官方文档

    |-dirver : linux设备驱动

    |-fs : 内核所支持的文件体系

    |-include :头文件。linux/module.h linux/init.h 常用库。

    |-init :库文件代码,C库函数在内核中的实现。

    init/main.c ->start_kernel->内核执行第一条代码

    |-ipc : 进程件通信

    |-mm :内存管理

    |-kernel : 内核核心部分,包括进程调度等

    |-net :网络协议

    |-sound : 所有音频相关

    其中,跟设备驱动有关并且经常查阅的文件夹有:

    init

    include : linux, asm-arm

    drivers:

    arch:

    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    .内核补丁:

    补丁一般都是基于某个版本内核生成的,用于升级旧内核。

    打补丁需要注意:

    1.对应版本的补丁只能用于对应版本的内核。

    2.如果在已打补丁的内核再打补丁,需要先卸载原来补丁。

    打补丁的方法:

    1.制作补丁:

    diff -Nur linux-2.6.30/ linux-2.6.30.1/ > linux-2.6.30.1.patch

    //N为新加的文件全部修改

    //linux-2.6.30 旧版本

    //linux-2.6.30.1 新版本

    //目标补丁

    2.打补丁:

    cd linux-2.6.30 //!!注意在原文件夹的目录中打补丁

    patch -p1 < ../linux-2.6.30.1.patch //-p1是忽略一级目录

    3.恢复:

    cd linux-2.6.30 //!!注意在原文件夹的目录中打补丁

    patch -R < ../linux-2.6.30.1.patch //撤销补丁


    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    .内核中的Makefile

    对于内核,Makefile分为5类:

    Documentation/kbuild/makefiles.txt描述如下:

    50 The Makefiles have five parts:

    51

    52 Makefile Makefile,控制内核的编译

    53 .config 内核配置文件,配置内核时生成, make menuconfig

    54 arch/$(ARCH)/Makefile 对应体系结构的Makefile

    55 scripts/Makefile.* Makefile共用的规则

    56 kbuild Makefiles 各子目录下的Makefile,被上层的Makefile调用。

    简单来说,编译内核会执行以下两步骤,它们分别干了以下的事情。

    1一般的,我们会拷贝一个对应体系结构的配置文件到主目录下并改名为 .config,这样就在make menuconfig生成的图形配置中 已经有了一些默认的配置,减少用户的劳动量。不过这一步不做也没关系的。

    2.make menuconfig

    2.1、由总Makefile决定编译的体系结构(ARCH). 编译工具(CROSS_COMPILE),并知道需要进去哪些内核根下的哪些目录进行编译。

    2.2、由arch/$(ARCH)/Makefile,决定arch/$(ARCH)下还有 的哪些目录和文件需要编译。

    2.3、知道了需要编译的目录后,递归的进入哪些目录下,读取每一个Kconfig的信息,生成了图形配置的界面。

    2.4、通过我们在图形配置界面中选项为[*][M]或者[]

    2.5、保存并退出配置,会根据配置生成一份新的配置文件.config,并在同时生成include/config/auto.conf(这是.config的去注释版)。文件里面保存着CONFIG_XXXX等变量应该取y还是取m

    3.make

    3.1、根据Makefile包含的目录和配置文件的要求,进去个子目录进行编译,最后会在各子目录下 生成一个.o或者.a文件,然后总Makefile指定的连接脚本arch/$(ARCH)/kernel/vmlinux.lds生成vmlinux,并通过 压缩编程bzImage,或者按要求在对应的子目录下编译成 模块。。

    但是,具体是怎么生成配置文件的呢?

    注:我使用的内核是2.6.29

    1.在总Makefile中,根据以下语句进入需要编译的目录

    470 # Objects we will link into vmlinux / subdirs we need to visit

    471 init-y := init/

    472 drivers-y := drivers/ sound/ firmware/

    473 net-y := net/

    474 libs-y := lib/

    475 core-y := usr/

    476 endif # KBUILD_EXTMOD

    639 core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/

    上面说明了,根目录下的initdriversoundfirmwarenetlibusr等目录,在编译时都会进去读取目录下的Makefile并进行编译。

    2.在总Makefile中包含的目录还是不够的,内核还需要根据对应的CPU体系架构,

    决定还需要将哪些子目录将要编译进内核。在总Makefile中有一个语句:

    529 include $(srctree)/arch/$(SRCARCH)/Makefile //在这里,我定义SRCARCH = arm

    可以看出,在总Makefile中进去读取相应体系 结构的Makefile->arch/$(SRCARCH)/Makefile

    arch/$(SRCARCH)/Makefile中指定arch/$(SRCARCH)路径下的哪些子目录需要被编译。

    在 arch/arm/Makefile 下:

    95 head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o

    187 # If we have a machine-specific directory, then include it in the build.

    188 core-y += arch/arm/kernel/ arch/arm/mm/ arch/arm/common/

    189 core-y += $(machdirs) $(platdirs)

    190 core-$(CONFIG_FPE_NWFPE) += arch/arm/nwfpe/

    191 core-$(CONFIG_FPE_FASTFPE) += $(FASTFPE_OBJ)

    192 core-$(CONFIG_VFP) += arch/arm/vfp/

    193

    194 drivers-$(CONFIG_OPROFILE) += arch/arm/oprofile/

    195

    196 libs-y := arch/arm/lib/ $(libs-y)

    上面看到,指定需要进入arch/arm/kernel/arch/arm/mm/arch/arm/common/ 等目录编译,至于core-y、 core-$(CONFIG_FPE_NWFPE)这些是什么东西呢?


    其中,y表示编译成模块,m表示编译进内核(上面没有,因为默认情况下ARM全部编译进 内核),但$(CONFIG_OPROFILE)又是什么呢? 这些是根据用户在make menuconfig中设置后,生成的值赋给了CONFIG_OPROFILE


    3.make menuconfig后的配置信息是怎么来的?

    这是由各子目录下的Kconfig提供选项功用户选择并配置。

    arch/arm/Kconfig 所有的配置都是根据arch/$(ARCH)/Kconfig文件通过Kconfig的语法source读取 各个包含的子目录Kconfig来生成一个配置界面。每个Makefile目录下都有一个 对应的Kconfig文件,用于生成配置界面来给用户决定内核如何配置,配置后会确定一个。 CONFIG_XXX的的值(如上面的CONFIG_OPROFILE),来决定编译进内核,还是编译成模块或者不编译。

    如在arch/arm/Kconfig下:

    595 source "arch/arm/mach-clps711x/Kconfig"

    596

    597 source "arch/arm/mach-ep93xx/Kconfig"

    598

    599 source "arch/arm/mach-footbridge/Kconfig"

    600

    601 source "arch/arm/mach-integrator/Kconfig"

    602

    603 source "arch/arm/mach-iop32x/Kconfig"

    604

    605 source "arch/arm/mach-iop33x/Kconfig"

    这些就是用来指定,需要读取以下目录下的Kconfig文件来生成一个使用make menuconfig时的配置界面。

    至于子目录下的Kconfig是怎么样的,待会介绍。

    总结Kconfig的作用:

    3.1.make menuconfig下可以配置选项;

    3.2..config中确定CONFIG_XXX的的值。


    4.只是读取以上的两个Makefile还是不够了,内核还会把包含的子目录一层一层的 读取它里面的MakefileKconfig


    上面啰啰嗦嗦地讲了这么久,无非就是想说,内核的编译并不是一个Makefile搞定的,需要通过根目录下的总Makefile来包含一下子Makefile(不管是根目录下的子目录还是/arch/arm中的子目录)。而Kconfig,为用户提供一个交互界面来选择如何配置并生成配置选项。


    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    五、子目录下的MakefileKconfig


    上面我一直介绍的都是两个比较大的Makefile——Makefile和 arch/$(ARCH)/Makefile。接下来看一下实例。


    一、makefile中,y表示编译进内核,m表示编译成模块,不写代表不编译。 所以,配置最简单的方法就是,直接修改子目录的Makefile 

    先看看arch/arm/Makefile

    /*arch/arm/mach-s3c2440/Makefile */

    12 obj-$(CONFIG_CPU_S3C2440) += s3c2440.o dsc.o

    13 obj-$(CONFIG_CPU_S3C2440) += irq.o

    14 obj-$(CONFIG_CPU_S3C2440) += clock.o //配置2440的时钟进入模块

    15 obj-$(CONFIG_S3C2440_DMA) += dma.o

    如果我要取消s3c2440的时钟(当然这是必须要开的,只是举例) 可以直接修改arch/arm/mach-s3c2440/Makefile obj-$( CONFIG_CPU_S3C2440) += clock.o改为

    obj- += clock.o

    如果你想编译成模块也可以修改成:

    obj-m += clock.o

    在这里 CONFIG_CPU_S3C2440的值默认是y,所以内核是要将时钟编译进内核的。也许有人会问,那我直接修改 CONFIG_CPU_S3C2440的值为m不就可以将时钟编译成模块了,何必修改Makefile这么麻烦呢?的确是这样,只要我们通过在”make menuconfig”的界面中配置后就能够改变 CONFIG_CPU_S3C2440的值。接下来看看如何实现。


    二、在一般的编译内核时,我们都是通过”make menuconfig”进入图形界面面配置的, 接下来我实现一下如何将一个选项加入到图形配置界面中。

    看看具体实现的步骤:

    以下的执行环境是在PC机上,我使用的内核是linux-2.6.29

    2.1.进入内核目录

    cd linux-2.6.29

    2.2. driver目录下模拟一个名为test1驱动的文件夹

    mkdir driver/test1

    2.3. 在目录下随便些一个C文件,只要不报错。

    vim test1.c

    我的test1.c如下:

    1 void foo()

    2 {

    3   ;

    4 }

    2.4vim Makefile //在目录下编写一个简单的Makefile

    Makefile文件编写如下:

    obj-$(CONFIG_TEST1) += test1.o

    CONFIG_TEST1是决定test1是否编译进内核或者编译成模块的。这就是通 过同一目录下的Kconfig来在配置界面中生成选项,由用户在make menuconfig中选择。

    2.5所以还要同一目录下写一个Kconfig

    vim Kconfig

    Kconfig修改如下:

    menu "test1 driver here" //这是在图形配置显示的

    config TEST1

    bool "xiaobai test1 driver" //这同样也是在图形配置显示的

    help

    This is test1 //这个也是在图形配置显示的。

    说白了,就是在图形配置的driver下多了一个配置选项,用户配置后将 CONFIG_TEST1的值存放在.config中,Makefile通过读取.config的去注 释版include/config/auto.conf读取到CONFIG_TEST的值,再进行编译。


    但是,以上几步还不能达到目的,因为虽然在总Makefile中已经包含了 目录driver,但是driver目录的Makefile中并没有包含test目录。因此 需要在driver/Makefile中添加:

    103 obj-$(CONFIG_PPC_PS3) += ps3/

    104 obj-$(CONFIG_OF) += of/

    105 obj-$(CONFIG_SSB) += ssb/

    106 obj-$(CONFIG_VIRTIO) += virtio/

    107 obj-$(CONFIG_STAGING) += staging/

    108 obj-y += platform/

    109 obj-$(CONFIG_TEST1) += test1/ //这是我添加的

    虽然Makefile中已经包含了,但这样还是不行。因为当需要配置ARM时, ARM结构下的Kconfig并没有包含testKconfig这样的话就不会出现在 图形配置界面中,因此在arch/arm/Kconfig中添加:

    1230 menu "Device Drivers" //要在Device Drivers这个选项里面添加

    1231

    1232 source "drivers/base/Kconfig"

    1233

    1234 source "drivers/connector/Kconfig"

    。。。。。。。。

    1330 source "drivers/test/Kconfig" //这是我添加的

    1331

    1332 endmenu

    大功告成!

    这样,make menuconfig界面写的Driver Devices下就多了一个 "test1 friver here"的目录,里面有一个配置选项"xiaobai test1 driver"

    Kconfig文件的语法在documentation/kbuild/kconfig-language.txt文件中 有详细的讲解,上面我只是简单实现了一下,都是皮毛。


    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    .内核和模块的编译

    编译内核很简单,只需要配置完毕后执行make命令,将指定的文件编译进内核

    bzImage或者编译成模块。

    make = make bzImage + make modules

    因此如果值编译内核,即只编译配置文件中-y选项,可以直接用命令

    make bzImage

    如果值编译模块,即只编译配置文件中的-m选项,可以之直接使用命令

    make modules

    模块可以编译当然也可以清除,使用命令

    make modules clean

    如果只想单独编译一个模块,可以使用命令

    make M=drivers/test/ modules //只单独编译drivers/test中的.ko

    make M=drivers/test/ modules clean //清除

    上面的是在内核目录下的操作,但当我写驱动时,我并不可能在内核目录下编

    写,但我编译时却要依赖内核中的规则和Makefile,所以就有了以下的方法,

    同时这也是一般的编写驱动时Makefile的格式。

    指定内核Makefile并单独编译

    make -C /root/linux-2.6.29 M=`pwd` module

    make -C /root/linux-2.6.29 M=`pwd` module clean

    //-C 指定内核Makefile的路径,可以使用相对路径。

    //-M 指定要编译的文件的路径,同样课使用相对路径。

    编译生成的模块可以指定存放的目录

    make -C /root/linux-2.6.29 M=`pwd` modules_install INSTALL_MOD_PATH=/nfsroot


    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


    七、总结


    说了这么久估计都说糊涂了,其实我只是想表达一下内核编译时大体上究竟是怎么样的一个过程。

    我以编译S3C2440的内核为例再说一遍:

    1一般我们会想将一份S3C2440的默认配置拷贝到内核跟目录下并改名为.config

    2.make menuconfig

    2.1、由总Makefile决定编译的体系结构(ARCH). 编译工具(CROSS_COMPILE),并知道需要进去哪些内核根下的哪些目录进行编译。

    2.2、由arch/$(ARCH)/Makefile,决定arch/$(ARCH)下还有 的哪些目录和文件需要编译。

    2.3、知道了需要编译的目录后,递归的进入哪些目录下,读取每一个Kconfig的信息,生成了图形配置的界面。

    2.4、通过我们在图形配置界面中选项为[*][M]或者[]

    2.5、保存并退出配置,会根据配置生成一份新的配置文件.config,并在同时生成include/config/auto.conf(这是.config的去注释版)。文件里面保存着CONFIG_XXXX等变量应该取y还是取m

    3.make

    3.1、根据Makefile包含的目录和配置文件的要求,进去个子目录进行编译,最后会在各子目录下 生成一个.o或者.a文件,然后总Makefile指定的连接脚本 arch/$(ARCH)/kernel/vmlinux.lds生成vmlinux,并通过 压缩编程bzImage,或者按要求在对应的子目录下编译成 模块

    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

  • 相关阅读:
    HDU3336 Count the string —— KMP next数组
    CodeForces
    51Nod 1627 瞬间移动 —— 组合数学
    51Nod 1158 全是1的最大子矩阵 —— 预处理 + 暴力枚举 or 单调栈
    51Nod 1225 余数之和 —— 分区枚举
    51Nod 1084 矩阵取数问题 V2 —— 最小费用最大流 or 多线程DP
    51Nod 机器人走方格 V3 —— 卡特兰数、Lucas定理
    51Nod XOR key —— 区间最大异或值 可持久化字典树
    HDU4825 Xor Sum —— Trie树
    51Nod 1515 明辨是非 —— 并查集 + 启发式合并
  • 原文地址:https://www.cnblogs.com/huty/p/8518612.html
Copyright © 2011-2022 走看看