简体   繁体   English

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

[英]Call a userspace function from within a Linux kernel module

I'm programming a simple Linux character device driver to output data to a piece of hardware via I/O ports.我正在编写一个简单的 Linux 字符设备驱动程序,以通过 I/O 端口将数据输出到一个硬件。 I have a function which performs floating point operations to calculate the correct output for the hardware;我有一个函数可以执行浮点运算来计算硬件的正确输出; unfortunately this means I need to keep this function in userspace since the Linux kernel doesn't handle floating point operations very nicely.不幸的是,这意味着我需要将此函数保留在用户空间中,因为 Linux 内核不能很好地处理浮点运算。

Here's a pseudo representation of the setup (note that this code doesn't do anything specific, it just shows the relative layout of my code):这是设置的伪表示(请注意,此代码不执行任何特定操作,它仅显示我的代码的相对布局):

Userspace function :用户空间功能

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;
}

Kernelspace code :内核空间代码

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 */
}

I thought about using ioctl to pass in the data from the userspace function, but I'm not sure how to handle the fact that the function call is in a loop and more code executes before the next call to calculate_output occurs.我想过使用ioctl从用户空间函数传入数据,但我不确定如何处理函数调用处于循环中并且在下一次调用calculate_output之前执行更多代码的事实。

The way I envision this working is:我设想这项工作的方式是:

  1. main userspace program will start the kernelspace code (perhaps via ioctl )主用户空间程序将启动内核空间代码(可能通过ioctl
  2. userspace program blocks and waits for kernelspace code用户空间程序阻塞并等待内核空间代码
    • kernelspace program asks userspace program for output data, and blocks to wait内核空间程序用户空间程序请求输出数据,并阻塞等待
    • userspace program unblocks , calculates and sends data ( ioctl ?), then blocks again用户空间程序解除阻塞,计算并发送数据( ioctl ?),然后再次阻塞
    • kernelspace program unblocks and continues内核空间程序解除阻塞并继续
  3. kernelspace program finishes and notifies userspace内核空间程序完成并通知用户空间
  4. userspace unblocks and continues to next task用户空间解锁并继续下一个任务

So how do I have the communication between kernelspace and userspace, and also have blocking so that I don't have the userspace continually polling a device file to see if it needs to send data?那么我如何在内核空间和用户空间之间进行通信,并且还有阻塞,这样我就不会让用户空间不断轮询设备文件以查看它是否需要发送数据?


A caveat: while fixed point arithmetic would work quite well in my example code, it is not an option in the real code;一个警告:虽然定点算法在我的示例代码中工作得很好,但在实际代码中它不是一个选项; I require the large range that floating point provides and -- even if not -- I'm afraid rewriting the code to use fixed point arithmetic would obfuscate the algorithm for future maintainers.我需要浮点提供的大范围 - 即使没有 - 我担心重写代码以使用定点算法会混淆未来维护者的算法。

I think the simplest solution would be to create a character device in your kernel driver, with your own file operations for a virtual file.我认为最简单的解决方案是在您的内核驱动程序中创建一个字符设备,并为虚拟文件使用您自己的文件操作。 Then userspace can open this device O_RDWR .然后用户空间可以打开这个设备O_RDWR You have to implement two main file operations:您必须实现两个主要的文件操作:

  • read -- this is how the kernel passes data back up to userspace. read ——这是内核如何将数据传回用户空间。 This function is run in the context of the userspace thread calling the read() system call, and in your case it should block until the kernel has another seed value that it needs to know the output for.此函数在调用read()系统调用的用户空间线程的上下文中运行,在您的情况下,它应该阻塞,直到内核有另一个需要知道输出的种子值。

  • write -- this is how userspace passes data into the kernel. write ——这是用户空间将数据传递到内核的方式。 In your case, the kernel would just take the response to the previous read and pass it onto the hardware.在您的情况下,内核只会获取对先前读取的响应并将其传递给硬件。

Then you end up with a simple loop in userspace:然后你在用户空间得到一个简单的循环:

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

and no loop at all in the kernel -- everything runs in the context of the userspace process that is driving things, and the kernel driver is just responsible for moving the data to/from the hardware.内核中根本没有循环——一切都在驱动事物的用户空间进程的上下文中运行,内核驱动程序只负责将数据移入/移出硬件。

Depending on what your "do some random stuff here" on the kernel side is, it might not be possible to do it quite so simply.根据您在内核方面“在这里做一些随机的事情”是什么,可能不可能这么简单地做到这一点。 If you really need the kernel loop, then you need to create a kernel thread to run that loop, and then have some variables along the lines of input_data , input_ready , output_data and output_ready , along with a couple of waitqueues and whatever locking you need.如果您确实需要内核循环,那么您需要创建一个内核线程来运行该循环,然后在input_datainput_readyoutput_dataoutput_ready行中output_data一些变量,以及几个等待队列和您需要的任何锁定。

When the kernel thread reads data, you put the data in input_ready and set the input_ready flag and signal the input waitqueue, and then do wait_event(<output_ready is set>) .当内核线程读取数据时,您将数据放入input_ready并设置input_ready标志并向输入等待队列发出信号,然后执行wait_event(<output_ready is set>) The read file operation would do a wait_event(<input_ready is set>) and return the data to userspace when it becomes ready. read文件操作会执行一个wait_event(<input_ready is set>)并在数据准备好时将数据返回给用户空间。 Similarly the write file operation would put the data it gets from userspace into output_data and set output_ready and signal the output waitqueue.类似地, write文件操作会将它从用户空间获取的数据放入output_data并设置output_ready并向输出等待output_ready发出信号。

Another (uglier, less portable) way is to use something like ioperm , iopl or /dev/port to do everything completely in userspace, including the low-level hardware access.另一种(更丑陋,不太便携)的方法是使用iopermiopl/dev/port东西来完全在用户空间中完成所有操作,包括低级硬件访问。

I would suggest that you move the code that does all the "heavy lifting" to user mode - that is, calculate all the 300 values in one go, and pass those to the kernel.我建议您将执行所有“繁重工作”的代码移至用户模式 ​​- 即一次性计算所有 300 个值,并将它们传递给内核。

I'm not even sure you can let an arbitrary piece of code call user-mode from the kernel.我什至不确定您是否可以让任意一段代码从内核调用用户模式。 I'm sure it's possible to do, because that's what for example "signal" does, but I'm far from convinced you can do it "any way you like" (and almost certainly, there are restrictions regarding, for example, what you can do in that function).我确信这是可能的,因为这就是例如“信号”所做的,但我远不相信你可以“以你喜欢的任何方式”做到这一点(几乎可以肯定,有一些限制,例如,什么你可以在那个函数中做)。 It certainly doesn't seem like a great idea, and it would DEFINITELY be quite slow to call back to usermode many times.这当然看起来不是一个好主意,而且多次回调到用户模式肯定会很慢。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM