zoukankan      html  css  js  c++  java
  • Linux的应用层到底层驱动的调用过程

    应用层如何内核.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 ", __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 -- ", __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 ");
    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 -- ", __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 ");
    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 ");
    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 ");
    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 -- ", __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 centos7 安装 phpMyAdmin
    centos7安装显示中文
    js限制按钮每隔一段时间才能再次点击
    PHPMailer 发送邮件
    jquery click事件中的return false
    安装composer
    生成目录树查看目录结构
    VMware桥接模式下虚拟机ping主机不通
    编译U-Boot时command not found的解决方法
    强制删除文件(夹)的方法
  • 原文地址:https://www.cnblogs.com/zhangfeionline/p/6279836.html
Copyright © 2011-2022 走看看