从Linux内核模块中调用用户空间函数

时间:2013-04-17 18:54:46

标签: c linux linux-kernel driver linux-device-driver

我正在编写一个简单的Linux字符设备驱动程序,通过I / O端口将数据输出到硬件。我有一个函数执行浮点运算来计算硬件的正确输出;不幸的是,这意味着我需要在用户空间中保留此功能,因为Linux内核不能很好地处理浮点运算。

这是设置的伪表示(请注意,此代码不执行任何特定操作,它只显示我的代码的相对布局):

用户空间功能

char calculate_output(char x){
    double y = 2.5*x;
    double z = sqrt(y);

    char output = 0xA3;

    if(z > 35.67){
        output = 0xC0;
    }

    return output;
}

内核空间代码

unsigned i;
for(i = 0; i < 300; i++){
    if(inb(INPUT_PORT) & NEED_DATA){
        char seed = inb(SEED_PORT);
        char output = calculate_output(seed);
        outb(output, OUTPUT_PORT);
    }

    /* do some random stuff here */
}

我考虑过使用ioctl传递来自userspace函数的数据,但我不知道如何处理函数调用处于循环中并且在下次调用之前执行更多代码的事实calculate_output发生。

我设想这个工作的方式是:

  1. 主用户空间程序将启动内核空间代码(可能通过ioctl
  2. 用户空间程序阻止并等待内核空间代码
    • 内核空间程序要求用户空间程序输出数据,阻止等待
    • 用户空间计划取消阻止,计算并发送数据(ioctl?),然后阻止
    • 内核空间程序取消阻止并继续
  3. kernelspace程序完成,通知用户空间
  4. 用户空间取消阻止并继续执行下一个任务
  5. 那么我如何在内核空间和用户空间之间进行通信,并且还有阻塞,这样我就不会让用户空间连续轮询设备文件以查看是否需要发送数据?


    警告:虽然定点算术在我的示例代码中可以很好地工作,但它不是真实代码中的一个选项;我需要浮点提供的大范围 - 即使不是 - 我害怕重写代码以使用定点算法会使算法混淆未来的维护者。

2 个答案:

答案 0 :(得分:4)

我认为最简单的解决方案是在内核驱动程序中创建一个字符设备,使用您自己的虚拟文件文件操作。然后,用户空间可以打开此设备O_RDWR。您必须实现两个主要文件操作:

  • read - 这是内核将数据传递回用户空间的方式。此函数在调用read()系统调用的用户空间线程的上下文中运行,在您的情况下,它应该阻塞,直到内核具有另一个需要知道输出的种子值。

  • write - 这是用户空间将数据传递到内核的方式。在您的情况下,内核只会将对上一次读取的响应传递给硬件。

然后你在用户空间中得到一个简单的循环:

while (1) {
    read(fd, buf, sizeof buf);
    calculate_output(buf, output);
    write(fd, output, sizeof output);
}

并且内核中根本没有循环 - 一切都在驱动事物的用户空间进程的上下文中运行,而内核驱动程序只负责将数据移入/移出硬件。

根据内核方面的“做一些随机内容”,可能无法简单地完成。如果你真的需要内核循环,那么你需要创建一个内核线程来运行该循环,然后在input_datainput_readyoutput_data和{{ 1}},以及一些等待和任何你需要的锁定。

当内核线程读取数据时,您将数据放入output_ready并设置input_ready标志并发出输入等待标记信号,然后执行input_readywait_event(<output_ready is set>)文件操作将执行read并在数据准备就绪时将数据返回给用户空间。类似地,wait_event(<input_ready is set>)文件操作会将从用户空间获取的数据放入write并设置output_data并发出输出等待标记。

另一种(更加丑陋,不那么便携)的方法是使用output_readyiopermiopl之类的东西来完成用户空间中的所有操作,包括低级硬件访问。

答案 1 :(得分:0)

我建议您将执行所有“繁重工作”的代码移动到用户模式 ​​- 也就是说,一次性计算所有300个值,然后将这些值传递给内核。

我甚至不确定你是否可以让任意一段代码从内核调用用户模式。我确信这是可能的,因为这就是“信号”的作用,但我很不相信你能“以任何你喜欢的方式”做到这一点(而且几乎可以肯定的是,例如,有什么限制你可以做那个功能)。这当然不是一个好主意,而且很多时候回调用户模式肯定会很慢。

相关问题