Linux字符设备驱动剖析

以下内容整理于Linux字符设备驱动剖析,如有侵权请告知删除 。

一、应用层的程序

应用程序一般都是open打开设备文件,read、write、ioctl设备文件,最后close设备文件退出。

int main(int argc ,char *argv[])  
{  unsigned char val[1] = 1;  int fd =open("/dev/LED",O_RDWR);//打开设备  write(fd,val,1);//写入设备,这里代表LED全亮  close(fd);//关闭设备  return 0;  
}  

 

二、/dev目录与文件系统

/dev/NULL和/dev/console是在制作根文件系统的时候静态创建的,其他设备文件都是系统加载根文件系统和各种驱动初始化过程中自动创建的,当然也可以通过命令行手动mknod设备文件。

三、设备文件的创建

(1)/dev目录下的设备文件基本上都是通过mdev来动态创建的。mdev是一个用户态的应用程序,位于busybox工具箱中。其创建过程包括:

  • 驱动初始化或者总线匹配后,会调用驱动的probe接口。该接口会调用device_create(设备类, 设备号, 设备名),在“/sys/class/设备类”目录生成唯一的设备属性文件(包括设备号和设备名等信息),并且发送uvent事件(KOBJ_ADD和环境变量,如路径等信息)到用户空间(通过socket方式)。
  • mdev是一个work_thread线程,收到事件后会分析“/sys/class/设备类”的对应文件,最终调用mknod动态来创建设备文件。
  • 设备文件内容主要是设备号(这个设备文件对应的inode,会记录文件的属性是一个设备(其他属性还包括目录,一般文件,符号链接等))。
  • 应用程序open最重要的一步就是(通过文件系统接口)获得该设备文件的内容,即设备号。

(2)如果初始化过程中没有调用device_create接口来创建设备文件,则需要手动通过命令行调用mknod接口来创建设备文件。

(3)mknod接口分析

四、open设备文件

(1)open设备文件,是为了获取(该设备驱动的)file_operations操作集。

  • 该接口集是struct file的成员,open返回file数据结构指针:
  • struct file   
    {  const struct file_operations *f_op;  unsigned int f_flags;//可读,可写等  …  
    };  
(2)以下是led设备驱动的操作接口。open("/dev/LED",O_RDWR)就是为了获得led_fops。
static const struct file_operations led_fops = {  .owner =THIS_MODULE,  .open =led_open,  .write = led_write,  
};  
  • 仔细看应用程序int fd =open("/dev/LED",O_RDWR),open的返回值是int,并不是file,其实是为了操作系统和安全考虑。
  • fd位于应用层,而file位于内核层,它们都同属进程相关概念。
  • 在linux中,同一个文件(对应于唯一的inode)可以被不同的进程打开多次,而每次打开都会获得file数据结构。
  • 每个进程都会维护一个已经打开的file数组,fd就是对应file结构的数组下标。因此,file和fd在进程范围内是一一对应的关系。

(3)open接口分析

通过系统调用后对应调用sys_open,其是vfs层的接口Sys_open(/dev/led)

  • SYSCALL_DEFINE3(open,const char __user *, filename, int, flags, int, mode)
  •   do_sys_open(AT_FDCWD,/dev/tty, flags, mode);
  •     fd = get_unused_fd_flags(flags);
  •     struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);
  •       path_init(dfd, pathname, LOOKUP_PARENT, &nd);//path_init返回时nd->dentry即为搜索路径文件名的起点
  •       link_path_walk(pathname, &nd);//link_path_walk一步步建立打开路径的各个目录的dentry和inode
  •       do_last(&nd, &path, open_flag, acc_mode, mode, pathname);
  •         filp = nameidata_to_filp(nd);//通过inode节点创建file
  •            __dentry_open()
  •              f->f_op =fops_get(inode->i_fop);

在__dentry_open()函数中有 

[cpp] view plain copy
  1. if (!open && f->f_op)  
  2.     open = f->f_op->open;  
  3. if (open) {  
  4.     error = open(inode, f);  
  5.     if (error)  
  6.         goto cleanup_all;  
  7. }  

  • 其中inode->i_fop在mknod的init_special_inode调用中被赋值为def_chr_fops。
  • open(inode, f)即调用到chrdev_open。其可以看出是字符设备所对应的文件系统接口,我们姑且称其为字符设备文件系统。

[cpp] view plain copy
  1. const struct file_operations def_chr_fops = {  
  2.   .open = chrdev_open,  
  3. };  

(4)继续分析chrdev_open

  • Kobj_lookup(cdev_map,inode->i_rdev, &idx)即是通过设备的设备号(inode->i_rdev)在cdev_map中查找设备对应的操作集file_operations。
  • 关于如何查找,我们在理解字符设备驱动如何注册自己的file_operations后再回头来分析这个问题。

五、字符设备驱动的注册

(1)字符设备对应的结构体cdev

[cpp] view plain copy
  1. struct cdev   
  2. {  
  3.   struct kobject kobj; // 每个 cdev 都是一个 kobject  
  4.   struct module* owner; // 指向实现驱动的模块  
  5.   const struct file_operations *ops; // 操纵这个字符设备文件的方法  
  6.   struct list_head list; //对应的字符设备文件的inode->i_devices 的链表头  
  7.   dev_t dev; // 起始设备编号  
  8.   unsigned int count; // 设备范围号大小  
  9. };  

(2)led设备驱动初始化和设备驱动注册

  • cdev_init是初始化cdev结构体,并将led_fops填入该结构。
  • cdev_add函数
[cpp] view plain copy
  1. int cdev_add(struct cdev *p, dev_t dev, unsigned count)  
  2. {  
  3.   p->dev = dev;  
  4.   p->count = count;  
  5.   return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);  
  6. }  
  • kobj_map函数
  • (1)kobj_map函数使用hash散列表来存储cdev数据结构(即存储设备的信息)。
  • (2)通过(注册设备的主设备号major)来获得cdev_map->probes数组的索引值i(i = major % 255);
  • (3)然后把一个类型为struct probe的节点对象加入到probes[i]所管理的链表中
  • (4)probes[i]->data即是cdev数据结构,而probes[i]->dev和range代表字符设备号和范围。
  • (5)其中参数cdev_map的类型如下:

六、再述open设备文件

   通过第五步的字符设备的注册过程,应该很容易理解Kobj_lookup查找led_ops的过程(这里不写)。

   至此,获得led设备驱动的led_ops。

(1)接着调用file->f_ops->open来调用led_open

  • 该函数中对led用到的GPIO进行ioremap,并设置GPIO方向、上下拉等硬件初始化。 

(2)最后chrdev_open一步步返回

  • 最后到do_sys_open函数中的“struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);”返回。
  • fd_install(fd, f),是在当前进程中将存有led_ops的file指针填入进程的file数组中,下标是fd。最后将fd返回给用户空间。而用户空间只要传入fd即可找到对应的file数据结构。

七、设备操作

   这里以设备写为例,主要是控制led的亮和灭。

   write(fd,val,1)系统调用后对应sys_write,其对应所有的文件写,包括目录、一般文件和设备文件。

   一般文件有位置偏移的概念,即读写之后,当前位置会发生变化,所以如要跳着读写,就需要fseek。对于字符设备文件,没有位置的概念。因此重点跟踪vfs_write的过程。

  • fget_light,在当前进程中通过fd来获得file指针;
  • vfs_write

  • 对于led设备,file->f_op->write即是led_write。在该接口中实现对led设备的控制。

八、再论字符设备驱动的初始化

综上所述,字符设备的初始化包括两个主要环节:

(1)字符设备驱动的注册

  • 即通过cdev_add向系统注册cdev数据结构,提供file_operations操作集和设备号等信息,最终file_operations存放在全局指针变量cdev_map指向的Hash表中,其可以通过设备号索引并遍历得到。

(2)创建属性文件、设备文件

  • 通过device_create(设备类,设备号,设备名)在“sys/class/设备类”中创建设备属性文件并发送uevent事件,而mdev利用该信息自动调用mknod在/dev目录下创建对应的设备文件,以便应用程序访问。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/461051.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php处理form多文件上传,ajax利用FormData、FileReader实现多文件上传php获取

前台代码(注意,不需要用到form标签):a. html部分:b. js部分:c. 完整代码:function loadDoc(file,data,asynctrue){if(window.XMLHttpRequest){ // code for IE7, Firefox, Chrome, Opera, Safarixmlhttpnew XMLHttpReq…

Linux设备文件的创建和mdev

以下内容源于微信公众号嵌入式企鹅圈,有格式内容上的修改,如有侵权请告知删除。 本文将从代码级去理解Linux设备类和设备文件的创建过程。 一、设备类相关知识 设备类是虚拟的,并没有直接对应的物理实物,只是为了更好地管理同一类…

JDK源码 - BitSet的实现

java.util.BitSet是个很有趣的类&#xff0c;了解其内部实现对正确的使用非常重要。 对象构造&#xff1a; Java代码 private final static int ADDRESS_BITS_PER_WORD 6; private final static int BITS_PER_WORD 1 << ADDRESS_BITS_PER_WORD; private long[] wor…

Sharepoint学习笔记—ECM系列--根据位置设置的默认元数据值(Location-Based Metadata Defaults)...

如果有这样一个需求&#xff1a;客户在一个SharePoint 2010的站点的document library中创建了不同的文件夹FolderA和FolderB&#xff0c;对于上传到此文件夹的文件记录中有某一个列ColumnM,现在他实现当上传文件到不同的文件夹FolderA或FolderB时&#xff0c;列ColumnM使用不同…

博客园的CSRF

CSRF全称 Cross Site Request Forgery&#xff0c;跨站请求伪造。通俗理解&#xff1a;攻击者盗用当前用户身份&#xff0c;发请当前用户的恶意请求&#xff1a;如邮件&#xff0c;银行转账等。 CSRF原理 CSRF过程 登录网站A&#xff0c;生成本地Cookie信息&#xff1b;登录危…

开发板——在X210开发板上进行裸机开发的细节

以下内容是学习裸机开发过程中的一些细节内容的记录。 1、汇编语言函数细节 用汇编写的函数&#xff0c;末尾应该添加mov pc,lr语句。 2、裸机代码相关文件 3、关于链接地址 4、关于重定位的理解 &#xff08;1&#xff09;在sram内部重定位 这是在sram内部重定位&#xff0c;因…

linux上perl怎么传输参数,如何在perl子函数中传递参数?

慕村225694Perl 可以通过函数元型在编译期进行有限的参数类型检验。如果你声明sub mypush ()那么 mypush() 对参数的处理就同内置的 push() 完全一样了。函数声明必须要在编译相应函数调用之前告知编译器(编译器在编译函数调用时会对相应函数用 prototype来查询它的元型来进行参…

Struts2中ValueStack结构和总结

【ValueStack和ActionContext的关系】首先&#xff0c;从结构上来看ValueStack是ActionContext的一个组成部分&#xff0c;是对ActionContext功能的扩展。ActionContext是一个容器结构&#xff0c;是Struts2中用于数据存储的的场所&#xff0c;而ValueStack则是一个具备表达式引…

浅谈mysql数据库引擎

2019独角兽企业重金招聘Python工程师标准>>> 数据库是数据的集合&#xff0c;计算机中的数据库是存储器上一些文件的集合或者是内存数据的集合。Mysql,SQL server数据库都是可以存储数据&#xff0c;并提供数据查询&#xff0c;更新功能的数据库管理系统。Mysql数据…

linux ssh抓包,如何在SSH连接Linux系统的环境下使用wireshark抓包?

TSINGSEE青犀视频云边端架构EasyNVR、EasyDSS、EasyGBS等都是有两种操作系统的版本&#xff0c;一种是linux&#xff0c;一种是windows。而大多数开发者用户都会使用linux版本进行安装。对于安装部署出现的问题&#xff0c;TSINGSEE青犀视频团队研发的经常为客户远程调试&#…

ASP.NET后台调用前台JS函数的三种常见方法

为什么80%的码农都做不了架构师&#xff1f;>>> 第一种&#xff1a;使用普通的添加控件中的Attributes属性进行调用 例如&#xff0c;像一般的普通的按钮&#xff1a;Button1.Attributes.Add("onclick","MyFun();"); 此方法只能在Onload中或者…

嵌入式数据库sqlite在ARM上的的移植和使用

参考SQLite的编译、安装和使用_whz_zb的博客-CSDN博客&#xff0c;如有侵权&#xff0c;请告知删除。 参考&#xff1a;头文件路径问题 Linux下的头文件搜索路径 - 心哲 - 博客园 参考&#xff1a;进一步学习资源 SQlite - 标签 - likebeta - 博客园 一、源码获取 SQLite Do…

贪心法

贪心法的证明 —归纳证明&#xff1a; —贪心法使用的条件是&#xff1a;最优子结构和贪心选择正确性 —贪心算法是一步一步实现的&#xff0c; —在归纳证明的时候&#xff0c;贪心的第一步贪心选择策略的正确性就是归纳基础&#xff0c;因为以后都是一个子问题的选取&#xf…

第一季5:Hi3518EV200的环境搭建

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、内容总结 本文讲述如何安装交叉编译工具链&#xff0c;与编译源码得到uboot、kernel、rootfs镜像文件。 &#xff08;1&#xff09;安装交叉编译工具链&#xff0c;主要是通过执行osdrv/opensou…

Android动画的实现 上

在Android系统中也能经常见到动画&#xff0c;那么如何实现动画效果呢&#xff1f;本文就来为大家介绍动画的实现方式。 Android中动画的实现分两种方式&#xff0c;一种方式是补间动画Tween Animation&#xff0c;就是说你定义一个开始和结束&#xff0c;中间的部分由程序运算…

第一季2:HI3518EV200的初体验(检测板子是否正常工作)

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 一、检测步骤 1、设置PC主机有线网卡的ip地址设为192.168.1.10&#xff0c;关闭防火墙。 2、虚拟机桥接到有线网卡&#xff0c;并设置虚拟机的静态ip地址为192.168.1.141。 3、在uboot控制台设置ub…

第一季3:HI3518E方案整体架构介绍(硬件和软件支持)

以下内容源于朱有鹏嵌入式课程的学习与整理&#xff0c;如有侵权请告知删除。 1、硬件资源 &#xff08;1&#xff09;HI3518E单芯片提供&#xff1a;CPU DSP 内置64MB DDR ETHERNET MAC。 &#xff08;2&#xff09;外置16MB的SPI接口的Flash用来存放程序&#xff08;ubo…

(一)FlexViewer之整体框架解析

文章版权由作者李晓晖和博客园共有&#xff0c;若转载请于明显处标明出处&#xff1a;http://www.cnblogs.com/naaoveGIS/。 1.FlexViewer简介 FlexViewer框架为Esri提供的可以高效开发基于WEB的地理信息应用系统的一种完全免费的应用程序框架。目前有两种版本&#xff0c;一种…