最后是简洁版。
介绍:
我正在制作一个字符驱动程序(scull 类型。通过在内核中分配内存区域并将其视为设备来模拟驱动程序)。来自:https://www.youtube.com/playlist?list=PL16941B715F5507C5 -视频号7 和 8 来自这个。
到目前为止,驱动程序的代码工作正常。
当前代码:
struct fake_device
{
char data[100];
struct semaphore sem;
} virtual_device;
正如我所说,上面的代码创建了一个名为 virtual_device 的假字符设备。
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() 从它所连接的节点(文件:/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。copy_from_user() 失败。
在 Jonathan Corbett 的 Linux 设备驱动程序中,我发现 copy_from_user(void *to, const void __user *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 __user *my_pointer) 来保存字符串,以便我可以将其作为源传递给 copy_from_user(destination,source,size) )?
如果您知道解决方案,请告诉我。甚至可以在模块代码中执行此类操作,还是应该将其保留在用户应用程序本身中?
谢谢
编辑:如果您在本网站或其他网站上发现任何链接部分或全部回答了我的问题,请告诉我。我只是设备驱动程序编程的初学者。
最佳答案
完全没有 Linux 内核模块编程经验,所以我可能在胡说八道。但是,链接教程和快速谷歌搜索可以非常清楚地说明正在发生的事情。
您的 device_write()
函数是内核模块的一部分,它在内核模式下运行,因此其中所有分配的缓冲区也应该在内核空间中。第二个参数bufSourceData
是从用户空间传来的。 copy_from_user()
方法的主要目的是安全地处理外部指针:验证内存是否可访问并且按预期位于用户空间中,然后才进行复制。因此内核不会因输入不正确而 panic 或损坏。检查这个answer了解更多详情。
您当前的版本是这样做的:
- 分配内核空间缓冲区。
- 直接使用用户空间指针来填充内核空间缓冲区。 (不安全,传递不正确的指针会使系统崩溃)
- 要求
copy_from_user()
将数据从内核空间缓冲区复制到内核空间缓冲区,它限制这样做。
你可能应该做的:
- 分配内核空间缓冲区。
- 使用
copy_from_user()
来填充用户空间数据(注意,你需要防止可能的缓冲区溢出) - 像往常一样反转字符串并将其写入
virtual_device.data
缓冲区。
编辑
显然,需要澄清。
But what I wanted to do in clear terms is: take data from user space, manipulate it and pass that on to the kernel space
看起来你想象你的例程 device_write()
以某种方式介于用户空间和内核空间之间,但没有这样的层。你已经在内核空间中,这个函数的第一行代码是在内核模式下执行的。你没有向内核传递任何东西,你是内核的一部分(好吧,术语“内核模块”是非常 self 描述的)。您需要在内核模式下正确处理来自用户空间的数据,然后将其传递给设备。
In step 3, you mentioned states that I should write to 'virtual_device.data' as usual. <...> Now, if I follow what you say (ie. adding a statement 'virtual_device.data = *palindrome'), then this will work only for this program as it a simulation (I am using a fake char device). But for it to work practically, I need to use 'copy_from_user()' as usual with an appropriate user space pointer (containing the palindrome).
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;
}
关于创建一个用户空间指针来保存回文并将该指针传递给 copy_from_user(void *to, const void __user *mypointer, unsigned long count),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44094958/