6

我正在编写一个简单的 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从用户空间函数传入数据,但我不确定如何处理函数调用处于循环中并且在下一次调用calculate_output发生之前执行更多代码的事实。

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

  1. 主用户空间程序将启动内核空间代码(可能通过ioctl
  2. 用户空间程序阻塞并等待内核空间代码
    • 内核空间程序向用户空间程序询问输出数据,并阻塞等待
    • 用户空间程序解除阻塞、计算和发送数据(ioctl?),然后再次阻塞
    • 内核空间程序解除阻塞并继续
  3. 内核空间程序完成并通知用户空间
  4. 用户空间解除阻塞并继续下一个任务

那么我如何在内核空间和用户空间之间进行通信,并且还具有阻塞,以便我没有用户空间不断轮询设备文件以查看它是否需要发送数据?


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

4

2 回答 2

4

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

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

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

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

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

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

根据您在内核方面的“在这里做一些随机的事情”是什么,可能不可能这么简单地做到这一点。如果您真的需要内核循环,那么您需要创建一个内核线程来运行该循环,然后有一些变量input_data,如input_readyoutput_dataoutput_ready,以及几个等待队列和您需要的任何锁定。

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

另一种(更丑,更不便携)的方法是使用类似的东西iopermiopl或者/dev/port完全在用户空间中做所有事情,包括低级硬件访问。

于 2013-04-18T01:12:25.803 回答
0

我建议您将执行所有“繁重工作”的代码移至用户模式——也就是说,一次计算所有 300 个值,然后将它们传递给内核。

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

于 2013-04-17T19:18:44.520 回答