• Linux·设备文件devfs


    目录

    设备文件系统

    devfs

    udev(mdev)

    关于file和inode数据结构在内核中的探究


    设备文件系统

    Linux引入了虚拟文件系统,从而使设备的访问可以像访问普通文件系统一样。因此在内核中描述打开文件的数据inode中的rdev成员用来记录设备文件对应到的设备号。设备文件也由一个对应的file_operations 数据对象,用来描述设备的操作接口。设备文件系统最早是采用devfs实现的,但是后来因为种种原因在2.6以后的内核中已经将其废弃而转而使用udev,他来本质上是没有区别都是在设备添加到系统中时在/dev目录下产生设备文件(机制相同),但是不同的是策略devfs的策略是将设备文件的创建过程放在了内核空间,而udev的策略是由内核提供机制而用户空间提供策略从而完成设备的创建,所以现在设备文件管理由两个软件可用分别是PC平台上的udev和嵌入式平台上的mdev。


    devfs

    接口均已经废弃,不在详细探究。

    1. devfs_handle_t devfs_mk_dir(devfs_handle_t dir,const char* name,void* info);
    2. devfs_handle_t devfs_register(devfs_handle_t de,const char* name,unsigned int flag,uinsigned int major,unsigned int minor,umode_t mode,void* ops,void* info);
    3. devfs_handle_t devfs_unregister(devfs_handle_t de);

    udev(mdev)

    与devfs不同udev完全工作在用户空间,而内核通过netlink机制将,设备添加过程的相关信息通过netlink发送到用户空间的udev程序,netlink机制可以不理解是一种特殊的socket进程通讯方式,用户空间的udev接收到设备添加的信息后将完成设备文件的创建和设备文件操作接口等相关的配置和初始化操作。进而用户空间程序就能像访问普通文件一样访问设备了。


    关于file和inode数据结构在内核中的探究

    比较好奇设备文件在被多个用户进程打开后后续fops操作接口的file和inode是同一个还是个进程单独一个,因为file中还由一个private_data成员在驱动编程中是比较重要的所以接下来进行专门的验证。
    编写一个虚拟的设备驱动如下

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. //file open operation function
    14. static int char_drv_open(struct inode *inode , struct file *filp)
    15. {
    16. printk(KERN_EMERG "inode:%08x file:%08x\n",inode,filp);
    17. printk(KERN_EMERG "private_data:%08x\n",filp->private_data);
    18. printk(KERN_EMERG "inode:%08x\n",inode);
    19. filp->private_data = 0x5A5A5A5A;
    20. return 0;
    21. }
    22. //file read operation function
    23. static int char_drv_read(struct file *filp , char __user *buf , size_t cnt , loff_t *offt)
    24. {
    25. return 0;
    26. }
    27. //file write operation function
    28. static int char_drv_write(struct file *filp,const char __user *buf , size_t cnt , loff_t *offt)
    29. {
    30. return 0;
    31. }
    32. //file close operation function
    33. static int char_drv_release(struct inode *inode , struct file *filp)
    34. {
    35. return 0;
    36. }
    37. //file operation function struct
    38. static struct file_operations my_test_fop=
    39. {
    40. .owner = THIS_MODULE,
    41. .open = char_drv_open,
    42. .release = char_drv_release
    43. };
    44. /* 设备结构体 */
    45. struct test_dev
    46. {
    47. dev_t devid; /* 设备号 */
    48. struct cdev cdev; /* cdev */
    49. struct class *class; /* 类 */
    50. struct device *device; /* 设备 */
    51. int major; /* 主设备号 */
    52. int minor; /* 次设备号 */
    53. };
    54. #define NEWCHRLED_CNT 1 /* 设备号个数 */
    55. #define NEWCHRLED_NAME "newchrdev" /* 名字 */
    56. struct test_dev test_char_dev;
    57. //module init function
    58. static int __init char_drv_test_init(void)
    59. {
    60. //same hardware init
    61. //apply device num
    62. alloc_chrdev_region(&test_char_dev.devid, 0, NEWCHRLED_CNT,NEWCHRLED_NAME);
    63. test_char_dev.major = MAJOR(test_char_dev.devid); /* 获取主设备号 */
    64. test_char_dev.minor = MINOR(test_char_dev.devid); /* 获取次设备号 */
    65. printk(KERN_EMERG "major:%d minor:%d\n",test_char_dev.major ,test_char_dev.minor);
    66. //init dev struct
    67. cdev_init(&test_char_dev.cdev,&my_test_fop);
    68. //add dev to system
    69. cdev_add(&test_char_dev.cdev ,test_char_dev.devid ,NEWCHRLED_CNT );
    70. //build class
    71. test_char_dev.class = class_create(THIS_MODULE,"test2");
    72. if (IS_ERR(test_char_dev.class))
    73. {
    74. return PTR_ERR(test_char_dev.class);
    75. }
    76. //build device
    77. test_char_dev.device = device_create(test_char_dev.class,NULL ,test_char_dev.devid,NULL,"test2");
    78. if (IS_ERR(test_char_dev.device))
    79. {
    80. return PTR_ERR(test_char_dev.device);
    81. }
    82. return 0;
    83. }
    84. //module uninstall function
    85. static void __exit char_drv_test_exit(void)
    86. {
    87. /* 注销字符设备 */
    88. cdev_del(&test_char_dev.cdev);
    89. /* 删除 cdev */
    90. unregister_chrdev_region(test_char_dev.devid, NEWCHRLED_CNT);
    91. device_destroy(test_char_dev.class, test_char_dev.devid);
    92. class_destroy(test_char_dev.class);
    93. }
    94. //module function band
    95. module_init(char_drv_test_init);
    96. module_exit(char_drv_test_exit);
    97. //license and author
    98. MODULE_LICENSE("GPL");
    99. MODULE_AUTHOR("Smile");

    CPP 折叠 复制 全屏

    编写应用程序如下:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. int main(void)
    7. {
    8. int fd = open("/dev/test2",O_RDWR);
    9. if(fd<0)
    10. {
    11. perror("open");
    12. }
    13. getc(stdin);
    14. return 0;
    15. }

    结论
    经过实验验证,设备文件每打开一次,内核都会在内核空间创建file对象而inode是指向同一数据块。具体输出如下:

    1. //安装模块
    2. [ 466.459870] major:250 minor:0
    3. //第一次执行
    4. [ 534.073202] inode:f437da00 file:f02cea80
    5. [ 534.073205] private_data:00000000
    6. [ 534.073207] inode:f437da00
    7. //第二次执行
    8. [ 548.632708] inode:f437da00 file:f02bd000
    9. [ 548.632712] private_data:00000000
    10. [ 548.632713] inode:f437da00
  • 相关阅读:
    微信小程序发布遇到的一些问题记录
    以太网的概念
    spring配置双数据源
    建议收藏 | 可实操,数据中台选型示例
    融合鲸鱼算法的混合灰狼优化算法
    2-3-4树【数据结构与算法java】
    【html audio】播放音频
    C++之STL基础概念、容器、数据结构
    小白学Java
    Dynamsoft Barcode Reader SDK JAVA.9.2.X
  • 原文地址:https://blog.csdn.net/m0_64560763/article/details/126622598