学习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. LeetCode-Lowest Common Ancestor of a Binary Tre

    Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree. According ...

  2. quartz 调度启动失败,with (updlock,rowlock)

    原因是driverDelegateClass配置错误. org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTXorg.q ...

  3. 160308、java排序(形如1.1、1.2.1)

    package com.rick.sample;   import java.util.ArrayList; import java.util.Collections; import java.uti ...

  4. 160523、Oracle建立表空间和用户

    建立表空间和用户的步骤: 用户 建立:create user 用户名 identified by "密码"; 授权:grant create session to 用户名; gra ...

  5. 关于微信小程序的场景值

    微信小程序 “场景值”     对运营同学和产品比较有用一般开发者需在这里埋点,获取场景址,看一下小程序,用户一般从哪个路口进的有利于提升产品体验

  6. linux 命令行 执行 php

    w为监控响应功能做准备. ubuntu@VM-52-248-ubuntu:~$ php -f /var/www/html/wlinux.phpwwubuntu@VM-52-248-ubuntu:~$ ...

  7. H5开发APP入门

    一.MUI MUI是一个最接近原生APP体验的高性能前端框架.我们用它来排版布局. 官方网站:http://dev.dcloud.net.cn/mui/ 二.HTML5PLUS html5+是HBul ...

  8. 如何用Qt Creator输出helloworld

    0 引言:相比于MFC只要直接在VS上搭建,Qt的配置就相对复杂了,Qt新手上路,老司机绕道,记录下配置Qt的整个过程,直到最终用C++输出“hello world”. 搭建环境:Win10 + qt ...

  9. Spark2.0机器学习系列之12: 线性回归及L1、L2正则化区别与稀疏解

    概述 线性回归拟合一个因变量与一个自变量之间的线性关系y=f(x).       Spark中实现了:       (1)普通最小二乘法       (2)岭回归(L2正规化)       (3)La ...

  10. [笔记]Delphi 2007写DLL供VC调用实例

    考虑如下几种常用情况: - VC传入int,返回int- VC传入char *,返回int- VC传入char *,返回char *及int 为简化问题,传递的字符串参数只考虑ANSI格式,不考虑UN ...