最后的简洁版。
简介:
我正在制作一个char驱动程序(scull的类型。通过在内核中分配一个内存区域并将其视为一个设备来模拟驱动程序)。来自: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. 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(目标,源,大小) )?
如果您知道解决方案,请告诉我。甚至可以在模块代码中执行此类操作,还是应该将其保留在用户应用程序本身中?
由于
编辑:如果您在本网站或其他网站上发现任何部分或全部回答我的问题的链接,请告诉我们。我只是设备驱动程序编程的初学者。
答案 0 :(得分:1)
根本没有Linux内核模块编程的经验,所以我可能完全是胡说八道。但是,链接的教程和快速的谷歌搜索可以清楚地了解正在发生的事情。
您的device_write()
函数是内核模块的一部分,它在内核模式下运行,因此其中所有已分配的缓冲区也应该在内核空间中。第二个参数bufSourceData
从用户空间传递。 copy_from_user()
方法的主要目的是安全地处理外部指针:验证,内存是可访问的,并且按预期位于用户空间中,然后才复制。因此内核在输入错误时不会出现恐慌或损坏事件。有关详细信息,请查看此answer。
您当前的版本是这样做的:
copy_from_user()
将数据从内核空间缓冲区复制到内核空间缓冲区,这是限制它做的。你应该做什么:
copy_from_user()
填充用户空间数据(注意,您需要防止可能的缓冲区溢出)virtual_device.data
缓冲区。显然,需要澄清。
但我想用明确的术语做的是:从用户空间获取数据,操纵它并将其传递给内核空间
看起来你想象你的例程device_write()
在某种程度上介于用户空间和内核空间之间,但是没有这样的层。您已经在内核空间中,此函数中的第一行代码在内核模式下执行。你没有把任何东西传递给内核,你是内核的一部分(好吧,术语&#34;内核模块&#34;非常自我描述)。您需要在内核模式下正确处理来自用户空间的数据,然后将其传递到设备中。
在第3步中,您提到了我应该写入&#39; virtual_device.data&#39;照常。 &LT; ...&GT;现在,如果我按照你的说法(即添加一个语句&#39; virtual_device.data = * palindrome&#39;),那么这只适用于这个程序,因为它是一个模拟(我正在使用假的char设备) 。但为了实际工作,我需要使用&#39; copy_from_user()&#39;像往常一样使用适当的用户空间指针(包含回文)。
方法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;
}