最后是简洁的版本。
简介:
我正在做一个木炭驱动器(木炭类型。通过在内核中分配内存区域并将其视为设备来模拟驱动程序)。发件人:https://www.youtube.com/playlist?list=PL16941B715F5507C5-来自此的7号和8号视频。
到目前为止,司机的代码运行良好。
当前代码:

struct fake_device
{
char data[100];
struct semaphore sem;
} virtual_device;

正如我所说,上面的代码创建了一个名为virtual_device的伪char设备。
ssize_t device_write(struct file* filp, const char* bufSourceData,size_t bufCount, loff_t* curOffset)
{
    printk(KERN_INFO "soliduscode: writing to device");
    printk (KERN_INFO "%s",bufSourceData);
    ret = copy_from_user(virtual_device.data, bufSourceData, bufCount);
    return ret;
}

我用上面的函数来写假设备
问题:
如您所见,device_write()从它附加到的节点(在我的例子中是file:/dev/solidusmodule)读取数据,并将完全相同的数据写入设备。
现在我应该反转(使回文)这个字符串,这个转换应该在模块代码(这个代码)中执行,而不是在用户应用程序中执行。
我尝试过的解决方案:
问题是device_write()有语法
ssize_t write(struct file *filp, const char __user *buff, size_t count, loff_t *offp);

它接受一个常量字符用户空间指针。不允许更改语法。我无法更改bufSourceData所指的值。所以我试着
ssize_t device_write(struct file* filp, const char* bufSourceData,size_t bufCount, loff_t* curOffset)
{
    char temptry2[100]; //user app has the same size for writing to /dev/solidusdevice
    int i, len, ascii_null = 0;

    len = strlen(bufSourceData);

    for(i=0;i<len;i++)
    {
        temptry2[i] = bufSourceData[len-i-1];
    }

    temptry2[i] = ascii_null;
    for(i=len + 1; i<100;i++)
    {
        temptry2[i] = '0';
    }

    const char* palindrome_pointer = temptry2;

    ret = copy_from_user(virtual_device.data, palindrome_pointer, bufCount);
    return ret;
}

但这不会将字符串写入virtual_device.data。ret的值不是0。从用户复制失败。
从Jonathan Corbett的Linux设备驱动程序中,我发现copy_From_user(void*to,const void_uuser*mypointer,unsigned long count)只适用于源中的用户空间指针。所以我试着
const char __user *palindrome_pointer = temptry2; //tried to make it a user space pointer so that I can pass it to copy_from_user( ,const void __user *mypointer, )

甚至尝试过(不要笑):
const void __user *palindrome_pointer = temptry2; //changed from char to void

还是不行。
TL;DR(简明问题):如何创建一个常量字符类型的用户空间指针(const char}u user*my}u pointer),用于保存字符串,以便将其作为源传递以从用户(目标、源、大小)复制?
如果你知道解决办法,请告诉我。甚至可以在模块代码中执行这样的操作吗?还是应该将其保存在用户应用程序本身中?
谢谢
编辑:如果你在这个网站或其他网站上找到任何部分或全部回答我问题的链接,请务必告诉我。我只是一个设备驱动程序编程的初学者。

最佳答案

根本没有Linux内核模块编程的经验,所以我可能会说些完全没有意义的话。然而,链接的教程和快速的谷歌搜索提供了一个非常清楚的情况发生了什么。
您的device_write()函数是内核模块的一部分,它在内核模式下运行,因此它中分配的所有缓冲区都应该在内核空间中。第二个参数bufSourceData从用户空间传递。copy_from_user()方法的主要目的是安全地处理外部指针:验证,内存是可访问的,并且按预期位于用户空间中,然后才进行复制。所以内核不会因为不正确的输入而惊慌失措或损坏。查看answer了解更多详细信息。
当前版本执行以下操作:
分配内核空间缓冲区。
直接使用用户空间指针填充内核空间缓冲区。(不安全,传递不正确的指针会导致系统崩溃)
要求copy_from_user()将数据从内核空间缓冲区复制到内核空间缓冲区,它限制这样做。
你应该做什么:
分配内核空间缓冲区。
使用copy_from_user()填充用户空间数据(注意,您需要防止可能的缓冲区溢出)
反转字符串并像往常一样将其写入virtual_device.data缓冲区。
编辑
显然,需要澄清。
但我想做的是:从用户空间获取数据,对其进行操作并将其传递到内核空间
看起来您认为您的例程device_write()介于用户空间和内核空间之间,但是没有这样的层。您已经在内核空间中了,这个函数中的第一行代码是在内核模式下执行的。你没有把任何东西传递给内核,你是内核的一部分(好吧,术语“内核模块”是很自我描述的)。在内核模式下工作时,您需要正确处理来自用户空间的数据,然后将其传递到设备中。
在步骤3中,您提到了我应该像往常一样写入“virtual_device.data”的状态。现在,如果我按照你说的做(即添加一个语句“virtual_device.data=*palindrome”),那么这将只对这个程序起作用,因为它是一个模拟程序(我使用的是一个伪字符设备)。但要使其实际工作,我需要像往常一样使用“copy_from_user()”和适当的用户空间指针(包含回文)。
方法copy_from_user()是一种复杂而安全的memcpy()变体,仅此而已。它与设备(虚拟或非虚拟)或其他任何东西无关。函数非常简单:从一个缓冲区复制到另一个缓冲区。如果您可以使用设备完成此操作,则可以使用memcpy()将数据写入设备或任何其他处理缓冲区数据的标准方法。顺便说一下,您的建议virtual_device.data = *palindrome既不是有效的C代码,也不是将数据从一个数组传递到另一个数组的好概念。看起来您正在尝试修改指针,而不是在数组之间复制数据。这会以非常糟糕的方式结束。
好的,那么你的解决方案在伪代码中应该是什么样子的:

ssize_t device_write(struct file* filp, const char* bufSourceData,size_t bufCount, loff_t* curOffset)
{
    //kernel space buffer
    char palindrome_data[SOME_SIZE];
    ssize_t palindrome_size = min(bufCount, SOME_SIZE);

    //fill it with user data via safe function
    //instead of acessing user space data directly
    copy_from_user(palindrome_data, bufSourceData, palindrome_size));

    <check if copy_from_user hasn't failed>

    //do whatever you like
    make_palindrome(palindrome_data, palindrome_size);

    //write to device
    memcpy(virtual_device.data, palindrome_data, palindrome_size);

    return palindrome_size;
}

10-08 04:09
查看更多