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

驱动开发相关文章:

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

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

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

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

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

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

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

目录

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

通过文件系统交互

file_operations 结构体

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

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

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

示例代码

驱动代码(hello.c)

应用层(main.c)


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

在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 结构体

file_operations结构体是Linux内核中用于注册设备文件操作函数的重要结构体。它定义在 <linux/fs.h> 头文件中。file_operations结构体可以在内核驱动中使用,将设备文件操作函数与对应的设备文件关联起来。

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

struct file_operations {
    struct module *owner; // 拥有此结构体的模块的指针,用于管理模块的引用计数

    loff_t (*llseek) (struct file *, loff_t, int);
    // 设置文件指针位置的函数,可用于在读写文件时跳过一定的字节数
    // 参数:
    //   - struct file *:文件描述符指针
    //   - loff_t:偏移量,表示需要移动的字节数
    //   - int:位置标志,用于确定移动的起始位置(SEEK_SET,SEEK_CUR,SEEK_END)
    // 返回值:
    //   - loff_t:调整后的文件指针位置

    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    // 读取设备文件内容的函数,在read系统调用中被调用
    // 参数:
    //   - struct file *:文件描述符指针
    //   - char __user *:用户空间缓冲区指针,用于存储读取的数据
    //   - size_t:读取的数据大小
    //   - loff_t *:文件指针位置的指针,用于更新读取后的文件指针位置
    // 返回值:
    //   - ssize_t:实际读取的数据大小,若失败则返回错误码

    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    // 写入数据到设备文件的函数,在write系统调用中被调用
    // 参数:
    //   - struct file *:文件描述符指针
    //   - const char __user *:用户空间缓冲区指针,包含要写入的数据
    //   - size_t:要写入的数据大小
    //   - loff_t *:文件指针位置的指针,用于更新写入后的文件指针位置
    // 返回值:
    //   - ssize_t:实际写入的数据大小,若失败则返回错误码

    int (*open) (struct inode *, struct file *);
    // 打开设备文件时被调用的函数,在open系统调用中被调用
    // 参数:
    //   - struct inode *:文件的inode节点指针
    //   - struct file *:文件描述符指针
    // 返回值:
    //   - int:成功返回0,否则返回错误码

    int (*release) (struct inode *, struct file *);
    // 关闭设备文件时被调用的函数,在close系统调用中被调用
    // 参数:
    //   - struct inode *:文件的inode节点指针
    //   - struct file *:文件描述符指针
    // 返回值:
    //   - int:成功返回0,否则返回错误码

    // 其他成员函数...
};

除了上述函数外,file_operations结构体还包括其他一些函数指针,如pollioctlmmap等,用于实现更高级的设备文件操作。

在内核驱动中,可以通过定义一个自定义的file_operations结构体,并将其与设备文件关联起来,以注册设备文件操作函数。这样,在应用程序通过文件系统接口操作设备文件时,相应的设备文件操作函数将会被内核调用,实现设备的处理和控制。例如,可以通过调用cdev_init()函数将自定义的file_operations结构体注册到字符设备驱动中。

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

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

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

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

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

示例代码

驱动代码(hello.c)
#include <linux/init.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#define CNAME "hello"
int copy_size = 0;
char kbuf[128] = {0};
unsigned int major = 0;
ssize_t mycdv_read(struct file *file, char __user *ubuf, size_t len, loff_t *loff)
{
    if (len > sizeof(kbuf))    //len是用户需要的,有可能大于内核传的
    {
        len = sizeof(kbuf);    //大于的时候,有多少就给它多少
    }
    copy_size = copy_to_user(ubuf, kbuf, len);
    if (copy_size)
    {
        printk(KERN_ERR "copy_to_user error\n");
        return copy_size;
    }
    return 0;
}
ssize_t mycdv_write(struct file *file, const char __user *ubuf, size_t len, loff_t *lo)
{
    if (len > sizeof(kbuf))
    {
        len = sizeof(kbuf);
    }
    copy_size = copy_from_user(kbuf, ubuf, len);
    if (copy_size)
    {
        printk(KERN_ERR "copy_from_user error\n");
        return copy_size;
    }
    return 0;
}
int mycdv_open(struct inode *inode, struct file *file)
{
    printk(KERN_ERR "mycdv open ok\n");
    return 0;
}
int mycdv_release(struct inode *inode, struct file *file)
{
    printk(KERN_ERR "mycdv release ok\n");
    return 0;
}

const struct file_operations fops =
    {
        .read = mycdv_read,
        .write = mycdv_write,
        .open = mycdv_open,
        .release = mycdv_release,
};

static int __init hello_init(void)
{
    major = register_chrdev(major, CNAME, &fops);
    if (major < 0)
    {
        printk(KERN_ERR "register chrdev ERR\n");
        return major;
    }
    return 0;
}
static void __exit hello_exit(void)
{
    unregister_chrdev(major, CNAME);
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
应用层(main.c)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

char buf[128]="this is my_cdev";
int main(int argc, char const *argv[])
{
    int fd;
    fd = open("./hello",O_RDWR);
    if(fd<0)
    {
        perror("open err");
        return -1;
    }
    write(fd,buf,sizeof(buf));
    memset(buf,0,sizeof(buf));
    read(fd,buf,sizeof(buf));
    printf("这是读出来的:%s\n",buf);
    close(fd);
    return 0;
}

  • 25
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,下面是一个简单的内核应用层交互的源码,其中使用了字符设备驱动来实现: 首先是头文件部分: ``` #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/uaccess.h> ``` 然后定义一个设备结构体,并在模块初始化函数中注册设备: ``` static dev_t devno; static struct cdev mycdev; static struct class *myclass; static char buf[256]; static int len; static DEFINE_MUTEX(mutex); static ssize_t myread(struct file *file, char __user *buff, size_t count, loff_t *ppos) { int ret; mutex_lock(&mutex); if (*ppos >= len) ret = 0; else { if (count > len - *ppos) count = len - *ppos; if (copy_to_user(buff, buf + *ppos, count)) { ret = -EFAULT; goto out; } *ppos += count; ret = count; } out: mutex_unlock(&mutex); return ret; } static ssize_t mywrite(struct file *file, const char __user *buff, size_t count, loff_t *ppos) { int ret; mutex_lock(&mutex); if (count > sizeof(buf)) count = sizeof(buf); if (copy_from_user(buf, buff, count)) { ret = -EFAULT; goto out; } len = count; ret = count; out: mutex_unlock(&mutex); return ret; } static struct file_operations fops = { .owner = THIS_MODULE, .read = myread, .write = mywrite, }; static int __init mymodule_init(void) { int ret; ret = alloc_chrdev_region(&devno, 0, 1, "mydev"); if (ret) { printk(KERN_ERR "alloc_chrdev_region error\n"); return ret; } cdev_init(&mycdev, &fops); mycdev.owner = THIS_MODULE; ret = cdev_add(&mycdev, devno, 1); if (ret) { printk(KERN_ERR "cdev_add error\n"); unregister_chrdev_region(devno, 1); return ret; } myclass = class_create(THIS_MODULE, "myclass"); if (IS_ERR(myclass)) { printk(KERN_ERR "class_create error\n"); cdev_del(&mycdev); unregister_chrdev_region(devno, 1); return PTR_ERR(myclass); } device_create(myclass, NULL, devno, NULL, "mydev"); return 0; } static void __exit mymodule_exit(void) { device_destroy(myclass, devno); class_destroy(myclass); cdev_del(&mycdev); unregister_chrdev_region(devno, 1); } module_init(mymodule_init); module_exit(mymodule_exit); MODULE_LICENSE("GPL"); ``` 接下来是应用层的代码,其中打开设备、写入数据、读取数据和关闭设备: ``` #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <string.h> int main(int argc, char *argv[]) { int fd; char buf[256]; int len; fd = open("/dev/mydev", O_RDWR); if (fd < 0) { perror("open"); exit(1); } len = write(fd, "Hello, World!", strlen("Hello, World!")); if (len < 0) { perror("write"); exit(1); } len = read(fd, buf, sizeof(buf)); if (len < 0) { perror("read"); exit(1); } buf[len] = '\0'; printf("%s\n", buf); close(fd); return 0; } ``` 这个源码实现了一个简单的内核应用层交互机制,其中应用层通过打开设备、写入数据、读取数据和关闭设备来实现与内核交互内核通过字符设备驱动来实现对应的功能。当然这只是一个简单的示例,实际中可能需要更加复杂的实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值