学习Linux系统下驱动程序开发已有大半年时间,心中一直有个疑惑:那就是诸如open、write、read等系统调用是怎么和
内核或底层驱动建立起联系的呢?今天将自己的一些粗略的理解总结如下。  
   
       学过Linux系统下驱动程序开发的都知道,大部分的基础性的驱动操作都包括3个重要的内核数据结构,称为file_operations,
file,和inode。我们需要对这些结构有个基本了解才能够做大量感兴趣的事情。

1、struct file_operations是一个把字符设备驱动的操作和设备号联系在一起的纽带,是一系列指针的集合,每个被打开的文件
都对应于一系列的操作,这就是file_operations,用来执行一系列的系统调用。

2、struct file代表一个打开的文件,在执行file_operation中的open操作时被创建,这里需要注意的是与用户空间inode指针的区
别,一个在内核,而file指针在用户空间,由c库来定义。
      3、struct inode被内核用来代表一个文件,注意和struct file的区别,struct inode一个是代表文件,struct file一个是代表打开的文件
            struct inode 包括很重要的两个成员:
            dev_t       i_rdev     设备文件的设备号
            struct cdev *i_cdev 代表字符设备的数据结构,struct inode结构是用来在内核内部表示文件的。同一个文件可以被打开好多
次,所
以可以对应很多struct file,但是只对应一个struct inode.

应用层调用open函数,首先会发出open系统调用,然后进入 内核,调用sys_open函数,打开文件系统中的/dev/fs文件,这个文件要么
你是用mknod建立的,要么就直接在内核中用devfs方式来建立的,无论你用哪种方式建立,最终都会读取其文件属性,如果发现其是设备
文件,就会调用LINUX内核中的设备管理部分,根据其属性的主设备号(在建立设备节点时已经把主设备号写入文件属性当中,如果你仔细
看过mknod的用法就明白了),查找内核中相关联的file_operations,最终找到你的test_open函数。
       所以说 "在LINUX中设备即是文件 ",设备驱动首先会走文件系统这一条路(比如最先的 "/dev/fs "),然后根据设备文件的属性最终找
到相关联的file_operations,从而调用你的设备驱动例程.假如发现这个文件不是设备文件而只是磁盘文件,就继续走文件系统,高速缓冲
与磁盘调度这一条路了。

sys_open(内核函数)就直接到VFS层了,open过程中会得到设备文件的inode(通过传进来的文件名参数),(参考我的博文里
关于inode介绍),其file_operations结构体会赋给file结构体(含此成员fops)中相应成员,且当open方法不为空调用之。不过特殊文件
的inode里的file_operations都是一样的,比如字符设备文件inode之file_operations只定义了一个open方法,open方法根据inode中的设备
号在已注册的字符设备驱动中查找cdev,这个cdev里的fops才是驱动提供的。它会赋给file结构体中相应成员(file_operations结构体)
(覆盖了之前的一次赋值),其open方法不为空则调用之。

现在我们就演示一下用户使用write函数将数据写到设备里面这个过程到底是怎么实现的: 
    1,insmod驱动程序。驱动程序申请设备名和主设备号,这些可以在/proc/devieces中获得。 
    2,从/proc/devices中获得主设备号,并使用mknod命令建立设备节点文件。这是通过主设备号将设备节点文件和设备驱动程序联系在
一起。设备节点文件中的file属性中指明了驱动程序中fops方法实现的函数指针。 
    3,用户程序使用open打开设备节点文件,这时操作系统内核知道该驱动程序工作了,就调用fops方法中的open函数进行相应的工作。
open方法一般返回的是文件标示符,实际上并不是直接对它进行操作的,而是由操作系统的系统调用在背后工作。 
    4,当用户使用write函数操作设备文件时,操作系统调用sys_write函数,该函数首先通过文件标示符得到设备节点文件对应的inode指针
和flip指针。inode指针中有设备号信息,能够告诉操作系统应该使用哪一个设备驱动程序,flip指针中有fops信息,可以告诉操作系统相应
的fops方法函数在那里可以找到。 
    5,然后这时sys_write才会调用驱动程序中的write方法来对设备进行写的操作。

其中1-3都是在用户空间进行的,4-5是在核心空间进行的。用户的write函数和操作系统的write函数通过系统调用sys_write联系在了一起。 
    注意:

总的来说:设备文件通过设备号绑定了设备驱动,fops绑定了应用层的write和驱动层的write。当应用层写一个设备文件的时候,系统
找到对应的设备驱动,再通过fops找到对应的驱动write函数。

int open(const char *pathname, int flags, mode_t mode);  --系统调用
                                ||
                                \/
    long sys_open(const char __user *filename, int flags, int mode)  -- fs/open.c
    /*对应内核中的open接口函数*/
                                ||
                                \/
    long do_sys_open(int dfd, const char __user *filename, int flags, int mode) --fs/open.c
    /*用户空间的filename被拷贝到内核空间,获取当前可用的文件描述符*/
                                ||
                                \/
    static struct file *do_filp_open(int dfd, const char *filename, int flags, int mode) --fs/open.c 
                                ||
                                \/
    int open_namei(int dfd, const char *pathname, int flag,
                                                                                    int mode, struct nameidata *nd)
    /*获取该文件对应的nameidata结构.该函数执行完毕,接着调用下面函数。这两个函数是顺序被do_filp_open调用*/
                                ||
                                \/
    struct file *nameidata_to_filp(struct nameidata *nd, int flags) --fs/open.c
        /*将nameidata 结构转换为打开的struct file结构*/
                                ||
                                \/
    static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt,
                                 int flags, struct file *f,
                                                                     int (*open)(struct inode *, struct file *)) --fs/open.c
                                ||
                                \/
                f->f_op = fops_get(inode->i_fop);  --fs/open.c
    /*这里将系统调用中需要对应打开文件对应到内核中的file_operations结构体获取到,然后根据其函数指针就可以找到该结构体中对
该种文件操作的所有方法。scull对应的结构体是在scull_init的时候向内核注册的。
                                ||
                                \/
    open = f->f_op->open; 
    open(inode, f);    --fs/open.c
    /*以上两行代码分别完成了open系统调用时执行实际文件对应内核的open方法,即scull_open。

Linux系统调用怎么和内核或底层驱动交互的的更多相关文章

  1. 【原创】xenomai内核解析--双核系统调用(二)--应用如何区分xenomai/linux系统调用或服务

    版权声明:本文为本文为博主原创文章,转载请注明出处.如有错误,欢迎指正. 1. 引出问题 上一篇文章xenomai内核解析--双核系统调用(一)以X86处理器为例,分析了xenomai内核调用的流程, ...

  2. linux内存(三)内核与用户空间交互

    来自网址http://www.kerneltravel.net/jiaoliu/005.htm 用户程序和内核的信息交换是双向的,也就是说既可以主动从用户空间向内核空间发送信息,也可以从内核空间向用户 ...

  3. [转]Linux芯片级移植与底层驱动(基于3.7.4内核)

      1.   SoC Linux底层驱动的组成和现状 为了让Linux在一个全新的ARM SoC上运行,需要提供大量的底层支撑,如定时器节拍.中断控制器.SMP启动.CPU hotplug以及底层的G ...

  4. 关于Linux系统调用,内核函数【转】

    转自:http://blog.csdn.net/ubuntulover/article/details/5988220 早上听人说到某个程序的一部分是内核态,另一部分是用户态,需要怎么怎么.当时突然想 ...

  5. Linux内核调用I2C驱动_驱动嵌套驱动方法

    禁止转载!!!! Linux内核调用I2C驱动_以MPU6050为例 0. 导语 最近一段时间都在恶补数据结构和C++,加上导师的事情比较多,Linux内核驱动的学习进程总是被阻碍.不过,十一假期终于 ...

  6. linux系统调用、库函数和内核函数关系与区别

    看系统调用,还有库函数,以前一直不明白,总是以为 系统调用跟库函数是一样的,但是今天才知道是不一样的. 库函数也就是我们通常所说的应用编程接口API,它其实就是一个函数定义,比如常见read().wr ...

  7. linux内核SPI总线驱动分析(一)(转)

    linux内核SPI总线驱动分析(一)(转) 下面有两个大的模块: 一个是SPI总线驱动的分析            (研究了具体实现的过程) 另一个是SPI总线驱动的编写(不用研究具体的实现过程) ...

  8. 【转】 linux内核移植和驱动添加(三)

    原文网址:http://blog.chinaunix.net/uid-29589379-id-4708909.html 原文地址:linux内核移植和驱动添加(三) 作者:genehang 四,LED ...

  9. 42.Linux应用调试-初步制作系统调用(用户态->内核态)

    1首先来讲讲应用程序如何实现系统调用(用户态->内核态)? 我们以应用程序的write()函数为例: 1)首先用户态的write()函数会进入glibc库,里面会将write()转换为swi(S ...

随机推荐

  1. 170329、用 Maven 部署 war 包到远程 Tomcat 服务器

    过去我们发布一个Java Web程序通常的做法就是把它打成一个war包,然后用SSH这样的工具把它上传到服务器,并放到相应的目录里,让Tomcat自动去解包,完成部署. 很显然,这样做不够方便,且我们 ...

  2. 简述泛型、用Maven创建Web项目以及在Web项目上整合SpringMVC

    表设计 Timestamp列是否取消"根据当前时间戳自动更新" 是否null及默认值选择合理不合理 外键命名规范及更新和删除时的动作是否合理   泛型 类型参数 --允许在外部指定 ...

  3. [iOS微博项目 - 3.6] - 获取未读消息

    github: https://github.com/hellovoidworld/HVWWeibo   A.获取登陆用户未读消息 1.需求 获取所有未读消息,包括新微博.私信.@.转发.关注等 把未 ...

  4. googlr 黄金法则 监控

    googlr   黄金法则  监控

  5. PHP 错误日志

    display_errors 错误回显,一般常用语开发模式,但是很多应用在正式环境中也忘记了关闭此选项.错误回显可以暴露出非常多的敏感信息,为攻击者下一步攻击提供便利.推荐关闭此选项. display ...

  6. Python并行编程(十一):基于进程的并行

    1.基本概念 多进程主要用multiprocessing和mpi4py这两个模块. multiprocessing是Python标准库中的模块,实现了共享内存机制,可以让运行在不同处理器核心的进程能读 ...

  7. K-means聚类算法MATLAB

    以K-means算法为例,实现了如下功能 自动生成符合高斯分布的数据,函数名为gaussianSample.m 实现多次随机初始化聚类中心,以找到指定聚类数目的最优聚类.函数名myKmeans.m 自 ...

  8. ECMAScript 6 学习资料

    ECMAScript 6入门 http://es6.ruanyifeng.com/ 30分钟掌握ES6/ES2015核心内容(上) 30分钟掌握ES6/ES2015核心内容(下)

  9. ambari rest api (修改集群配置文件)

    1.找到你需要修改的配置的最新版本 curl -u admin:admin -H "X-Requested-By: ambari" -X GET http://AMBARI_SER ...

  10. Oracle 修改trace 文件路径

    修改trace 文件路径命令 适用于oracle 11G以后的版本 在集群环境中的2个节点都生效SQL> ALTER SYSTEM SET diagnostic_dest='/tmp' SCOP ...