应用层如何内核.md

1.从应用层打通内核:驱动

首先来说是设备号的引入,我们通过 cat/proc/kallsyms |grep mydevice 可以查看设备号,当然我们也是可以自己创建设备号,这是源于我们在写内核模块的时候在程序中指定。设备号有了,他就可以标识我们具体的设备。那我们应用层如何操作那?其实我们应用层需要创建一个设备节点文件创建的方法是sudo mknod /dev/hello c 255 0 这样我们就可以创建一个指定设备号的设备节点文件。通过设备号,使设备节点文件和内核中的设备驱动程序关联了起来。这其实也就打通了。有了文件,我们应用层就可以用open啊,read啊去操作这个设备节点文件。真正体现了linux下一切皆文件啊!。

2.具体实现:

用户空间通过mknod创建了一个设备文件hello(设备号) 对应在内核空间创建一个inode结构体(包含有设备号)与之对应
应用程序打开这个设备文件: 操作inode结构体,取出inode里面的设备号 ==》 到内核的cdev链表中去查找这个设备号对应的cdev对象 ==》 找到
cdev对象里面的fops ===》 找到fops里面的open函数指针 ==》 这个函数指针指向我们自己实行的open函数。

3.实现一个驱动服务于多个设备:

按照以前的思路:我们现在如果是一个服务,服务于多个设备,那么问题就来了,显然我们是不会写多个驱动程序的,但是有些数据是我们必须要有多份的。比如我们现在拿最简单
的一个操作,我们在驱动程序里面注册俩个设备号,那么意味着我们在用户空间,有俩个设备节点文件,我们在应用程序中打开俩份,都采用写入的方式,写数据,如果内核中我们把
接受数据的缓冲区没有重新备份,公用一份,结果就是数据的覆盖。这些存储的数据显然是要区分的,那个设备就是那个设备,既然是服务于多设备,那设备号的创建也自然是创建多个
注册多个,这些都可以非常容易的指定,具体可以看代码以及cdev显然也是俩份,他们都在内核的链表中,但是注意cdev结构体中的fops指针都指向的是同一个,所以fops是使用的同一个
其实也很好理解fops里面都是些读写操作的函数指针,没必要区分。共用更加好。
,对于cdev还比较简单,我们在创建自己的cdev结构体的时候,指定参数就可以多创建。
最后我们干脆把这些都要用到的东西打包个结构体,叫做设备结构体。我们回顾上面,当我们又一个设备使用驱动程序的时候,我们指定一个相关数据接收,但是多个设备文件的时候
就出现了对应问题,那就是 :对应问题,我们如何指定设备号对应的保存数据的变量(更加准确的是设备结构体中的变量,因为我们封装了)。在open的时候,我们是非常清楚当前的
设备号的因为open的时候,系统函数给我们传递了struct inode * inodep, struct file * filep 俩个参数,第一个参数是inode号,第二个是filep(这个结构体保存了我,文件的状态,以及当前
decv。我们通过inodep->i_cdev 获取到当前的cdev的地址,然后通过container_of(ptr, type, member)根据结构体成员的地址从而获取到整个结构体的首地址。
拿到了设备结构体的首地址,然后通过filep->private_data = dev;将地址保存到file结构体中,借助file传递,因为file在读写函数中也调用了。到了读写函数中我们首先用一个对应的结构体指针把传来
的地址接收struct hello_device * dev = filep->private_data;哈哈哈,我们在读写函数中拿到了我们的数据,而且是依靠不同的decv,拿到不同的设备结构体。

完美@!

  1. #include<linux/module.h>
  2. #include<linux/init.h>
  3. #include<linux/fs.h>
  4. #include<linux/cdev.h>
  5. #include<asm/uaccess.h>
  6. /*2017年1月12日19:49:34 张飞online*/
  7. /* 许可证声明 */
  8. MODULE_LICENSE("GPL");
  9. #define NUM_OF_DEV 2
  10. int major =255;
  11. int minor =0;
  12. //设备结构体:将自己用的数据封装
  13. struct hello_device {
  14. dev_t devno;//设备号
  15. struct cdev mycdev;// cdev
  16. char data[64];
  17. }hello_dev[NUM_OF_DEV];
  18. //hello_dev[0] //设备1
  19. //hello_dev[1] //设备2
  20. /*##############################################
  21. *应用程序的open调用驱动的 hello_open 依靠 设备号和cdev中的iops,具体实现
  22. 可以看上面的文字
  23. *
  24. ##############################################*/
  25. staticint hello_open(struct inode * inodep,struct file * filep)
  26. {
  27. struct hello_device * dev;
  28. printk("-- %s called -- major = %d minor = %d\n", __FUNCTION__, imajor(inodep), iminor(inodep));
  29. /*inodep->i_cdev指明设备结构体中的mycdev地址,最终获取到当前cdev对应的我们自己定义的
  30. 设备结构体的地址,这是核心:就是依靠当前cdev获取当前我们的结构体地址
  31. */
  32. dev = container_of(inodep->i_cdev,struct hello_device, mycdev);
  33. /*
  34. 用file中的私有数据传出,因为读写函数也传入了file,而且file也是一 一对应的,当前文件的file
  35. */
  36. filep->private_data = dev;
  37. return0;
  38. }
  39. //应用程序的close调用驱动的hello_release
  40. staticint hello_release(struct inode * inodep,struct file * filep)
  41. {
  42. printk("-- %s called --\n", __FUNCTION__);
  43. return0;
  44. }
  45. staticssize_t hello_read(struct file * filep,char __user * buf,size_t size,loff_t* off)
  46. {
  47. /*
  48. 获取到了当前file,然后用结构体指针承接,实现修改
  49. */
  50. struct hello_device * dev;
  51. dev = filep->private_data;
  52. //check param
  53. if(size <0|| size >64)
  54. return-EINVAL;
  55. if(copy_to_user(buf, dev->data, size))
  56. {
  57. //为真,表示失败
  58. printk("cp err\n");
  59. return-1;
  60. }
  61. else{
  62. return size;
  63. }
  64. }
  65. staticssize_t hello_write(struct file * filep,constchar __user * buf,size_t size,loff_t* off)
  66. {
  67. struct hello_device * dev = filep->private_data;
  68. //check param
  69. if(size <0|| size >64)
  70. return-EINVAL;
  71. if(copy_from_user(dev->data, buf, size))
  72. {
  73. return-1;
  74. }
  75. else{
  76. return size;
  77. }
  78. }
  79. struct file_operations hello_fops ={
  80. .owner = THIS_MODULE,
  81. .open = hello_open,
  82. .release = hello_release,
  83. .read = hello_read,
  84. .write = hello_write,
  85. };
  86. staticint cdev_setup(struct cdev * cdev,dev_t devno)
  87. {
  88. int ret;
  89. cdev_init(cdev,&hello_fops);
  90. cdev->owner = THIS_MODULE;
  91. ret = cdev_add(cdev, devno,1);
  92. if(ret <0)
  93. return-1;
  94. return0;
  95. }
  96. /* 模块加载函数,当向内核中插入这个模块的时候会被调用 */
  97. int hello_init(void)
  98. {
  99. int ret;
  100. //做初始化的动作
  101. printk("-- %s called --\n", __FUNCTION__);
  102. //1. 注册设备号
  103. hello_dev[0].devno = MKDEV(major, minor);
  104. hello_dev[1].devno = MKDEV(major, minor+1);
  105. //注册来两个设备号,255 0 和255 1
  106. ret = register_chrdev_region(hello_dev[0].devno, NUM_OF_DEV,"hello-device");
  107. if(ret <0)
  108. {
  109. printk("register_chrdev_region err\n");
  110. goto err1;
  111. }
  112. //2. cdev结构体插入内核链表
  113. ret = cdev_setup(&hello_dev[0].mycdev, hello_dev[0].devno);
  114. if(ret <0)
  115. {
  116. printk("cdev_add err\n");
  117. goto err2;
  118. }
  119. ret = cdev_setup(&hello_dev[1].mycdev, hello_dev[1].devno);
  120. if(ret <0)
  121. {
  122. printk("cdev_add err\n");
  123. goto err3;
  124. }
  125. // 初始化用户数据
  126. strcpy(hello_dev[0].data,"000000000000000000000000000000000000");
  127. strcpy(hello_dev[1].data,"111111111111111111111111111111111111");
  128. return0;
  129. /*
  130. 这里是删除内核链表中的cdev 和 释放设备号,借助goto,巧妙释放全部
  131. */
  132. err3:
  133. cdev_del(&hello_dev[0].mycdev);
  134. err2:
  135. unregister_chrdev_region(hello_dev[0].devno, NUM_OF_DEV);
  136. err1:
  137. return-1;
  138. }
  139. /* 模块的卸载函数,当从内核中把本模块删除的时候被调用 */
  140. void hello_exit(void)
  141. {
  142. //做和init_module相反的动作
  143. printk("-- %s called --\n", __FUNCTION__);
  144. cdev_del(&hello_dev[1].mycdev);
  145. cdev_del(&hello_dev[0].mycdev);
  146. unregister_chrdev_region(hello_dev[0].devno, NUM_OF_DEV);
  147. return;
  148. }
  149. //声明hello_init为模块的加载函数
  150. module_init(hello_init);
  151. module_exit(hello_exit);
  152. MODULE_AUTHOR("farsight");
  153. MODULE_DESCRIPTION("This is a simple moudles");

Linux的应用层到底层驱动的调用过程的更多相关文章

  1. android电池管理系统从上层的java到底层驱动的调用(转载)

    1.概述 随着移动智能设备的快速发屏,电池的续航能力在很大情况下诱导了大众消费者的购买选择,android系统对电源管理的合理与否直接影响到电池的续航能力,而电池系统作为其中的一部分,主要用于对电池状 ...

  2. Linux 应用层open调用驱动层open过程

    内核版本:3.0.8 open.close.read.write.ioctl等等都是类似. ====================================================== ...

  3. Linux系统调用怎么和内核或底层驱动交互的

    学习Linux系统下驱动程序开发已有大半年时间,心中一直有个疑惑:那就是诸如open.write.read等系统调用是怎么和内核或底层驱动建立起联系的呢?今天将自己的一些粗略的理解总结如下.      ...

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

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

  5. Linux输入子系统(一) _驱动编码

    输入设备都有共性:中断驱动+字符IO,基于分层的思想,Linux内核将这些设备的公有的部分提取出来,基于cdev提供接口,设计了输入子系统,所有使用输入子系统构建的设备都使用主设备号13,同时输入子系 ...

  6. linux驱动基础系列--Linux 串口、usb转串口驱动分析

    前言 主要是想对Linux 串口.usb转串口驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如字符设备驱动.平台驱动等也不进行详细说明原理.如果有任何错误地方,请指出, ...

  7. Android 开发之 ---- 底层驱动开发(一) 【转】

    转自:http://blog.csdn.net/jmq_0000/article/details/7372783 版权声明:本文为博主原创文章,未经博主允许不得转载. 驱动概述 说到 Android ...

  8. Android驱动入门-Led控制+app+ndk库+底层驱动

    硬件平台: FriendlyARM Tiny4412 Cortex-A9 操作系统: UBUNTU 14.04 LTS 时间:2016-09-20  21:56:48 本次实验使用的是 安卓APP + ...

  9. Android 开发之 ---- 底层驱动开发(一)

    驱动概述 说到 android 驱动是离不开 Linux 驱动的.Android 内核采用的是 Linux2.6 内核 (最近Linux 3.3 已经包含了一些 Android 代码).但 Andro ...

随机推荐

  1. orleans 2.0 教程之-----官方文档翻译,给大家学习ol一个参考

    本人也是英文盲,翻译不对的地方请谅解.由于翻译内容较多,会慢慢更新 orleans简称ol,一些专用词不做翻译.先决条件,读这表文章之前需要了解:actor,es,cqrs 参考链接: https:/ ...

  2. 自建脚手架之配置中心--LightConf的实现

    常规项目开发过程中, 通常会将配置信息位于在项目resource目录下的properties文件文件中, 配置信息通常包括有: jdbc地址配置.redis地址配置.活动开关--等等.因此每次上线或者 ...

  3. 【大数据系统架构师】0.1 Java编程基础

    1. 初识Java 2. Java语法 快速入门点我 2.1 数据类型和运算符 2.2 流程控制语句 2.3 数组 2.4 类和对象 2.5 OOP三大特性 2.6 集合框架与泛型 2.7 反射机制 ...

  4. tcp连接过程中的三次握手和四次挥手

    在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手握手建立连接 @第一次握手: 建立连接是,客户端A发送SYN包到服务器B,并进入SYN_SEND状态,等待B确认. @第二次握手: 服务 ...

  5. Github 三种克隆模式

    1.我称为平常模式,用于项目的本地克隆使用.(无权限.无加密.ssh protocol) git clone http://github.com/username/exampleproject 2.我 ...

  6. numpy.histogram 官方手册

    numpy.histogram numpy.histogram(a, bins=10, range=None, normed=False, weights=None, density=None) Co ...

  7. 在DZ 中 showmessage 中可以再次执行 JS

    showmessage ( '登录', '', array (), array (                         'showdialog' => 0,              ...

  8. AJAX中success函数的执行顺序

    1,问题,在上图中,数据传输正常,但是一直输出为空的p,再输出66, 2,原因,默认开启了异步加载, 3,解决方法,开启同步,或者在success中添加函数.

  9. ionic4 refresh组件位置变更:Ignored attempt to cancel a touchmove event with cancelable=false

    io3 中 refresh组件位置可不定: io4 中 须置顶,否则报错,此外complete方法的调用位置改为target属性 参考:https://github.com/ionic-team/io ...

  10. [Maven]Codehaus的Maven Repository地址

    In ~/.m2/settings.xml you can update the URL to be used for specific repositories. For example: < ...