0


驱动开发(六):应用层通过文件系统与内核层交互

驱动开发相关文章:

驱动开发(一):驱动代码的基本框架

驱动开发(二):创建字符设备驱动

驱动开发(三):内核层控制硬件层

驱动开发(四):Linux内核中断

驱动开发(五):Linux内核定时器

驱动开发(六):应用层通过文件系统与内核层交互

驱动开发(应用):设计一个社区饮水机控制系统

应用层与内核层的交互方式

在Linux驱动开发中,应用层可以通过以下几种方式来控制内核层:

  1. 文件系统接口:应用程序可以通过文件系统接口来与内核驱动进行通信。应用程序可以通过打开、读取、写入和关闭文件等操作来发送控制命令或获取设备状态。
  2. 系统调用:应用程序可以通过系统调用来请求内核执行某些操作。例如,应用程序可以使用ioctl()系统调用来发送特定的命令给内核驱动。
  3. proc文件系统:proc文件系统提供了一种特殊的文件系统接口,允许应用程序通过读写虚拟文件来与内核进行通信。应用程序可以通过读写/proc文件系统中的特定文件来发送控制命令或获取设备状态。
  4. sysfs文件系统:sysfs文件系统是Linux内核提供的一种特殊文件系统,用于向用户空间提供硬件设备的信息。应用程序可以通过读写sysfs文件来控制设备的行为。
  5. netlink套接字:Netlink是一种用于内核与用户空间之间进行通信的机制。应用程序可以通过创建和使用Netlink套接字来与内核驱动进行通信。

这些方式都可以让应用层与内核层进行交互和控制,具体选择哪种方式取决于应用的需求和设备的特性。

通过文件系统交互

在Linux驱动开发中,通过文件系统进行应用层和内核层的交互涉及以下几个步骤:

  1. 定义设备节点:在内核驱动中,需要定义设备节点,即创建一个特殊的文件。这可以通过调用 register_chrdev()函数来完成。该函数会分配一个主设备号和次设备号,并创建对应的设备节点。
  2. 实现设备文件操作函数:在内核驱动中,需要实现设备文件操作函数,包括 open()read()write()release()等。这些函数会在应用程序对设备文件执行相应操作时被内核调用。
  3. 注册设备文件操作函数:通过调用 file_operations 结构体中的相应函数指针来注册设备文件操作函数,这样内核就会在对设备文件执行相应操作时调用相应的函数。
  4. 用户空间操作设备文件:在应用层,应用程序可以通过文件系统接口(如open()、read()、write()和close()等函数)来操作设备文件。例如,应用程序可以使用open()函数打开设备文件,使用write()函数向设备发送命令,使用read()函数读取设备的状态,并使用close()函数关闭设备文件。
  5. 内核层处理设备文件操作:当应用程序调用文件系统接口函数时,内核会相应地调用对应的设备文件操作函数。在这些设备文件操作函数中,可以对设备进行相应处理,如处理读写请求、控制设备的操作等。

通过以上步骤,应用层和内核层就可以通过文件系统进行交互了。应用程序通过操作设备文件,触发设备文件操作函数的调用,从而实现与内核驱动的通信和控制。

file_operations 结构体

  1. file_operations

结构体是Linux内核中用于注册设备文件操作函数的重要结构体。它定义在 <

  1. linux/fs.h

头文件中。

  1. file_operations

结构体可以在内核驱动中使用,将设备文件操作函数与对应的设备文件关联起来。

  1. file_operations

结构体的常用函数及注释如下:

  1. struct file_operations {
  2. struct module *owner; // 拥有此结构体的模块的指针,用于管理模块的引用计数
  3. loff_t (*llseek) (struct file *, loff_t, int);
  4. // 设置文件指针位置的函数,可用于在读写文件时跳过一定的字节数
  5. // 参数:
  6. // - struct file *:文件描述符指针
  7. // - loff_t:偏移量,表示需要移动的字节数
  8. // - int:位置标志,用于确定移动的起始位置(SEEK_SET,SEEK_CUR,SEEK_END)
  9. // 返回值:
  10. // - loff_t:调整后的文件指针位置
  11. ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
  12. // 读取设备文件内容的函数,在read系统调用中被调用
  13. // 参数:
  14. // - struct file *:文件描述符指针
  15. // - char __user *:用户空间缓冲区指针,用于存储读取的数据
  16. // - size_t:读取的数据大小
  17. // - loff_t *:文件指针位置的指针,用于更新读取后的文件指针位置
  18. // 返回值:
  19. // - ssize_t:实际读取的数据大小,若失败则返回错误码
  20. ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
  21. // 写入数据到设备文件的函数,在write系统调用中被调用
  22. // 参数:
  23. // - struct file *:文件描述符指针
  24. // - const char __user *:用户空间缓冲区指针,包含要写入的数据
  25. // - size_t:要写入的数据大小
  26. // - loff_t *:文件指针位置的指针,用于更新写入后的文件指针位置
  27. // 返回值:
  28. // - ssize_t:实际写入的数据大小,若失败则返回错误码
  29. int (*open) (struct inode *, struct file *);
  30. // 打开设备文件时被调用的函数,在open系统调用中被调用
  31. // 参数:
  32. // - struct inode *:文件的inode节点指针
  33. // - struct file *:文件描述符指针
  34. // 返回值:
  35. // - int:成功返回0,否则返回错误码
  36. int (*release) (struct inode *, struct file *);
  37. // 关闭设备文件时被调用的函数,在close系统调用中被调用
  38. // 参数:
  39. // - struct inode *:文件的inode节点指针
  40. // - struct file *:文件描述符指针
  41. // 返回值:
  42. // - int:成功返回0,否则返回错误码
  43. // 其他成员函数...
  44. };

除了上述函数外,

  1. file_operations

结构体还包括其他一些函数指针,如

  1. poll

  1. ioctl

  1. mmap

等,用于实现更高级的设备文件操作。

在内核驱动中,可以通过定义一个自定义的

  1. file_operations

结构体,并将其与设备文件关联起来,以注册设备文件操作函数。这样,在应用程序通过文件系统接口操作设备文件时,相应的设备文件操作函数将会被内核调用,实现设备的处理和控制。例如,可以通过调用

  1. cdev_init()

函数将自定义的

  1. file_operations

结构体注册到字符设备驱动中。

应用程序将数据传递给驱动

从用户空间拷贝数据到内核空间

  1. #include <linux/uaccess.h>
  2. int copy_from_user(void *to, const void __user *from, int n)
  3. 功能:从用户空间拷贝数据到内核空间
  4. 参数:
  5. @to :内核中内存的首地址
  6. @from:用户空间的首地址
  7. @n :拷贝数据的长度(字节)
  8. 返回值:成功返回0,失败返回未拷贝的字节的个数

从内核空间拷贝数据到用户空间

  1. int copy_to_user(void __user *to, const void *from, int n)
  2. 功能:从内核空间拷贝数据到用户空间
  3. 参数:
  4. @to :用户空间内存的首地址
  5. @from:内核空间的首地址 __user需要加作用是告诉编译器这是用户空间地址
  6. @n :拷贝数据的长度(字节)
  7. 返回值:成功返回0,失败返回未拷贝的字节的个数

示例代码

驱动代码(hello.c)
  1. #include <linux/init.h>
  2. #include <linux/module.h>
  3. #include <linux/printk.h>
  4. #include <linux/fs.h>
  5. #include <linux/uaccess.h>
  6. #define CNAME "hello"
  7. int copy_size = 0;
  8. char kbuf[128] = {0};
  9. unsigned int major = 0;
  10. ssize_t mycdv_read(struct file *file, char __user *ubuf, size_t len, loff_t *loff)
  11. {
  12. if (len > sizeof(kbuf)) //len是用户需要的,有可能大于内核传的
  13. {
  14. len = sizeof(kbuf); //大于的时候,有多少就给它多少
  15. }
  16. copy_size = copy_to_user(ubuf, kbuf, len);
  17. if (copy_size)
  18. {
  19. printk(KERN_ERR "copy_to_user error\n");
  20. return copy_size;
  21. }
  22. return 0;
  23. }
  24. ssize_t mycdv_write(struct file *file, const char __user *ubuf, size_t len, loff_t *lo)
  25. {
  26. if (len > sizeof(kbuf))
  27. {
  28. len = sizeof(kbuf);
  29. }
  30. copy_size = copy_from_user(kbuf, ubuf, len);
  31. if (copy_size)
  32. {
  33. printk(KERN_ERR "copy_from_user error\n");
  34. return copy_size;
  35. }
  36. return 0;
  37. }
  38. int mycdv_open(struct inode *inode, struct file *file)
  39. {
  40. printk(KERN_ERR "mycdv open ok\n");
  41. return 0;
  42. }
  43. int mycdv_release(struct inode *inode, struct file *file)
  44. {
  45. printk(KERN_ERR "mycdv release ok\n");
  46. return 0;
  47. }
  48. const struct file_operations fops =
  49. {
  50. .read = mycdv_read,
  51. .write = mycdv_write,
  52. .open = mycdv_open,
  53. .release = mycdv_release,
  54. };
  55. static int __init hello_init(void)
  56. {
  57. major = register_chrdev(major, CNAME, &fops);
  58. if (major < 0)
  59. {
  60. printk(KERN_ERR "register chrdev ERR\n");
  61. return major;
  62. }
  63. return 0;
  64. }
  65. static void __exit hello_exit(void)
  66. {
  67. unregister_chrdev(major, CNAME);
  68. }
  69. module_init(hello_init);
  70. module_exit(hello_exit);
  71. MODULE_LICENSE("GPL");
应用层(main.c)
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <errno.h>
  5. #include <stdio.h>
  6. #include <unistd.h>
  7. #include <string.h>
  8. char buf[128]="this is my_cdev";
  9. int main(int argc, char const *argv[])
  10. {
  11. int fd;
  12. fd = open("./hello",O_RDWR);
  13. if(fd<0)
  14. {
  15. perror("open err");
  16. return -1;
  17. }
  18. write(fd,buf,sizeof(buf));
  19. memset(buf,0,sizeof(buf));
  20. read(fd,buf,sizeof(buf));
  21. printf("这是读出来的:%s\n",buf);
  22. close(fd);
  23. return 0;
  24. }

本文转载自: https://blog.csdn.net/a1547998353/article/details/139698495
版权归原作者 爱做梦的小涛 所有, 如有侵权,请联系我们删除。

“驱动开发(六):应用层通过文件系统与内核层交互”的评论:

还没有评论