zoukankan      html  css  js  c++  java
  • Linux设备驱动程序简介[整理]

    系统调用是操作系统内核与应用程序之间的接口,设备驱动程序则是操作系统内核与机器硬件的接口。几乎所有的系统操作最终映射到物理设备,除了CPU、内存和少数其它设备,所有的设备控制操作都由该设备特殊的可执行代码实现,此代码就是设备驱动程序。操作系统内核需要访问两类主要设备:字符设备和块设备。与此相关主要有两类设备驱动程序,字符设备驱动程序和块设备驱动程序。Linux(也是所有UNIX)的基本原理之一是:系统试图使它对所有各类设备的输入、输出看起来就好象对普通文件的输入、输出一样。设备驱动程序本身具有文件的外部特征,它们都能使用象 open(),close(),read(),write()等系统调用。为使设备的存取能象文件一样处理,所有设备在目录中应有对应的文件名称,才可使用有关系统调用。 
    驱动程序是内核与硬件的接口,他把系统调用映射到具体设备对于实际硬件的特定操作上,关系如下图所示:
    Linux设备驱动程序简介[整理]
    通过这种方法,应用程序就可以像操作普通文件一样操作硬件设备,用户程序只需要关心这个抽象出来的文件,而一切同硬件打交道的工作都交给了驱动程序。比如我们要向屏幕输出一串字符串,我们只需要把字符串写到显卡所抽象出来的文件里,而真正把字符串传到显卡里的工作就是由驱动程序来完成的。
    .
    在Linux下,驱动程序是内核的一部分,运行在内核态下,你可以将驱动静态的和内核编译在一起,这样的缺点是内核会比较大,而且如果驱动出错,会导致整个系统崩溃;也可以以module的方式编译,在需要的时候动态的载入。如果你编译过内核,应该记得在make menuconfig中,选项前面是可以选择和的,就分别表示"编译到内核中"和"编译成模块"。

    下面介绍下模块,一个简单的“helloworld module”如下所示:

    #include      
    #include      
     int init_module(void)
    {
       printk("Hello world 1. "); 
       // A non 0 return means init_module failed; module can't be loaded.
       return 0;
    }
     
    void cleanup_module(void)
    {
      printk(KERN_ALERT "Goodbye world 1. ");
    }
    其中,init_module函数是加载模块时会被调用的,一般作一些初始化的工作;cleanup_module函数是卸载模块时会被调用的,做一些清理的工作。因为模块是运行在内核态的,你自然不能使用库函数,因此要打印信息,需要使用printk函数而不是printf函数。另外,可以使用任意函数名(只要同内核函数名不冲突)来替换init_module和cleanup_module这两个函数名,但必须使用module_init(初始化函数名),module_exit(卸载时函数名)这两个宏来声明一下,也就是说,下面这个模块和上面的模块是等价的:

    #include      
    #include      
     int helloworld(void)
    {
       printk("<1>Hello world 1. ");
        // A non 0 return means init_module failed; module can't be loaded.
       return 0;
    }
     
    void goodbyeworld(void)
    {
      printk(KERN_ALERT "Goodbye world 1. ");
    }
    module_init(helloworld);
    module_exit(goodbyeworld);

    我们通过make编译生成一个模块文件".ko"之后,使用"insmod"命令来加载模块,那么“insmod”具体做了什么呢?
    下面来介绍下insmod这个工具:
    insmod是linux下加载模块的工具,路径一般是/sbin/insmod,当调用这个工具后,它的工作基本如下:

    在用户空间打开待安装的module
    调用query_module()系统调用询问无法落实的符号在内核或其他模块中的地址
    链接操作,落实模块中的符号引用
    调用create_module()系统调用在内核中创建module数据结构,并申请所需的内核空间
    调用init_module()系统调用将链接好的module映像装入内核空间,然后调用模块中的init_module()函数(注意:这里面的两个init_module函数不一样,一个是系统调用,一个是你写在模块里面的函数

    从已加载模块中卸载模块使用的是“rmmod”,"rmmod"所做工作如下:

    调用delete_module()系统调用释放模块的module结构,同时释放模块所占的内核空间
    调用模块中的cleanup_module()的函数
    .

    那么,一般情况下,驱动程序会在init_module()和cleanu_module()函数中做些什么呢?
    init_module(): 向内核登记本模块中一些包含着函数指针的数据结构(如:file_operations)
    cleanup_module(): 向内核撤销曾经的数据结构的登记,使内核在模块卸载后不至于再企图访问这些数据结构

    Linux设备驱动程序简介[整理]


    内核通过 printk() 输出的信息具有日志级别,日志级别是通过在 printk() 输出的字符串前加一个带尖括号的整数来控制的,如 printk("<6>Hello, world!/n");。内核中共提供了八种不同的日志级别,在 linux/kernel.h 中有相应的宏对应,以Ubuntu10.04为例具体位置在usr/src/linux-headers-2.6.32-21/include/linux/kernel.h中:
    #define KERN_EMERG "<0>"
    #define KERN_ALERT "<1>"
    #define KERN_CRIT "<2>"
    #define KERN_ERR "<3>"
    #define KERN_WARNING "<4>"
    #define KERN_NOTICE "<5>"
    #define KERN_INFO "<6>"
    #define KERN_DEBUG "<7>"

    参考如下网址:
    http://www.spongeliu.com/110.html
    http://blog.chinaunix.net/uid-23860671-id-150493.html
    http://blog.csdn.net/yeqishi/article/details/5800198

  • 相关阅读:
    Linux学习进阶路线图
    Ubuntu打开终端的方法三种
    Linux下显示IP地理位置信息的小工具-nali
    kail2 linux 安装vmware tools
    Ubuntu下apt-get命令详解
    Eclipse安卓开发环境
    纪念逝去的计算器之计算表达式结果
    今年暑假要AC
    结课博客作业
    第七次课程作业
  • 原文地址:https://www.cnblogs.com/maxma/p/9169772.html
Copyright © 2011-2022 走看看