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

来源:互联网 发布:阿里云 数据库好用吗 编辑:程序博客网 时间:2024/04/20 05:39

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

 

1.内核与linux设备驱动的作用与关系

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

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

 

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

 

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

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

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

 

图结构如下:

----------------------------------------------------------------------

       POSIX  LIBS     图形库                       -------->用户态(usr sys)

---------------------------SWI----------------------------------------

系统调用:用户态进入内核态的唯一途径

                                                                      -------->内核态(svc)

        --------------------------------------------------

        |                   系统调用接口                  |

        --------------------------------------------------

-----------------------------------------------------------------

|      网络协议     内存管理     文件系统                |     注意:

|      进程管理     设备控制                                 |     内核并不会主动提供

------------------------------------------------------------------    服务,只有当用户态

                                                                                       或者硬件有要求时,

       -------------------------------------------------------           内核才会执行相应的

       |                    驱动接口                                |           操作。

       -------------------------------------------------------

 

----------------------------------------------------------------------

 内核可以根据用户要求操作硬件设备。
 设备也可以通过中断通知内核。

      -------------------------------------------------------      

       |                        硬件设备                           |

       -------------------------------------------------------

 

 

举一个相对比较邪恶的类比,某牛人跟我说的:

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

----------------------------------------------------------------------

       在门缝收到的某卡片,提供了电话号码                       -------->客户

---------------------------打电话----------------------------------------

打电话:客户寻求某服务的途径

                                                                             -------->服务商

        --------------------------------------------------

        |                   接电话处                    |

        --------------------------------------------------

-----------------------------------------------------------------

|         提供上门服务,只要一个电话,       |     注意:

|                   30分钟内即到。                            |     服务商并不会主动提

------------------------------------------------------------------    供服务,只有当客户

                                                                                        或者有要求时,服务商

       -------------------------------------------------------           才会接送工作者到指定

       |                    联系服务工作者                      |         地点。

       -------------------------------------------------------

 

----------------------------------------------------------------------

 服务商可以根据用户要求接送指定工作者。
 服务工作者有需要时也可以要求服务商帮忙安排工作。

      -------------------------------------------------------      

       |                        某服务工作者                   |

       -------------------------------------------------------

 

 

===============================================

2.内核代码树介绍

    linux-2.6.29

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

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

    |-firmware     : 固件

    |-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:

 

===============================================

3.内核补丁:

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

    打补丁需要注意:

       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     //撤销补丁

  

=============================================== 

4.内核中的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.make menuconfig

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

1.2从内核顶层目录的Makefile决定编译的体系结构(ARCH).编译工具  (CROSS_COMPILE)和需要进去编译的目录。

1.3根据总MakefileARCH变量,进入相应体系结构的目录,读取arch/$ (ARCH)/Makefile,决定对应的体系结构下还有哪些目录需要编译。

1.4根据arch/$(ARCH)/Makefile,一个一个地递归进入指定的目录下调用该目录下的makefile,并根据目录下的Kconfig生成配置界面并由用户决定将该文件编译成模块还是编译进内核。

1.5配置完毕后保存退出,会更改原来的.config的内容。

    2.make

1.1将生成的.config去掉注释,新建一份配置文件,文件名为include/config/auto.conf

1.2根据配置文件的要求,将需要编译的文件的各个子目录下生成一个.o或者.a文件,然后由总Makefile指定的连接脚本arch/$(ARCH)/kernel/vmlinux.lds生成vmlinux,并通过压缩变成bzImage,或者按要求在对应的子目录下编译成模块。     

 

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

注:我使用的内核是被修改过的,可能有些地方和原内核不一样,如我内核里面$(ARCH)写成$(SRCARCH)。还有在文件中的行数和原内核不一致,但这些不影响分析,搜索一下就出来了。

    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/

#另外还有一个体系相关的arch目录

529 include $(srctree)/arch/$(SRCARCH)/Makefile

 

    这样,就根据了体系结构决定了需要进去编译的目录了。

2.在总Makefile中包含的目录还是不够的,内核还需要根据对应的CPU体系架构,决定还需要将哪些子目录将要编译进内核,在总Makefile中进去读取相应体系结构的Makefilearch/$(SRCARCH)/Makefile

    在总Makefile和体系架构下的arch/(SRCARCH)/Makefile中包含的子目录会根据该目录下的Makefile的要求编译成模块还是编译进内核,当然也可以不编译。

 

如在 arch/arm/Makefile 下:

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)

 

 

    其中,y表示编译成模块,m表示编译进内核(上面没有,因为默认情况下ARM全部编译进内核),但$(CONFIG_OPROFILE)又是什么呢?

    这些是根据用户在make menuconfig中设置后,生成的值赋给了CONFIG_OPROFILE。这是由各子目录下的Kconfig提供选项功用户选择并配置。如arch/arm/Kconfig

    另外有些配置会根据arch/$(ARCH)/Kconfig文件通过Kconfig的语法source读取各个包含的子目录Kconfig来生成一个配置界面。每个Makefile目录下都有一个对应Kconfig文件,用于生成配置界面来给用户决定内核如何配置。

总结Kconfig的作用:

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

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

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

 

假设我现在配置内核

1.最简单的方法,直接修改子目录的Makefile

如在我要取消s3c2440的时钟(当然这是必须要开的,只是举例)

可以直接修改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

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

 

obj-$(CONFIG_CPU_S3C2440)   += clock.o改为

obj-   += clock.o

也可以编译成模块:

obj-m  += clock.o

2.当然有更方便的通过图形界面,make menuconfig,接下来我实现一下如何将一个选项加入到图形配置界面中。

 

    2.1.进入内核目录

       cd linux-2.6.29

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

    mkdir driver/test1

2.3.test1目录下随便写个C文件

    cd driver/test1

    vim test1.c

   

  1 void foo()

  2 {

  3         ;

  4 }

 

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

    vim Makefile

obj-$(CONFIG_TEST1) += test1.o

   

    CONFIG_TEST1是决定test1是否编译进内核或者编译成模块的。这就通过Kconfig由用户在make menuconfig中选择。

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

    vim Kconfig

   

  1 menu "test1 driver here"

  2 config TEST1

  3     bool "xiaobai test1 driver"

  4     help

  5         This is test1

  6 endmenu

 

 

    说白了,就是在图形配置的driver下多了一个配置选项,用户配置后将CONFIG_TEST1的值存放在.config中,Makefile通过读取.config的去注释版autoconf读取到CONFIG_TEST的值,再进行编译。但是,以上几步还不能达到目的,因为虽然在总Makefile中已经包含了目录driver,但是driver目录的Makefile中并没有包含test目录。因此需要在driver/Makefile中添加:

2.6.vim driver/Makefile

    再最后加上一句:

      

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中添加语句。

2.7.vim arch/arm/Kconfig

 

   

1230 menu "Device Drivers"

1231

1232 source "drivers/base/Kconfig"

…..............................................

1328 source "drivers/staging/Kconfig"

1329

1330 source "drivers/test1/Kconfig"

1331

1332 endmenu

 

 

大功告成!

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

 

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

 

==============================================

5.编译内核

    编译内核很简单,只需要配置完毕后执行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  

 

=======================================================================

    以上都是个人理解,如内核的Makefile配置和编译时一个复杂的过程,我简单地描述了一下,并不保证一定正确。

    错误地方,请指正。