【发布时间】:2016-08-19 11:38:21
【问题描述】:
我正在编写一个内核驱动程序来使用 PCI Express 设备发送/接收数据。对于第一个版本的驱动程序,我正在创建一个字符设备接口,用户可以在其中使用文件读取数据。
背景
我想在用户请求数据并且驱动程序填充用户缓冲区的情况下实现阻塞读取。为了屏蔽用户的read调用,我使用了completion结构。
当驱动程序被加载并且用户请求读取时,驱动程序会按预期阻塞。如果我要完成阅读,那么一切都会运行良好。
问题
为了安全起见,每当移除模块时,我都会调用complete_all 函数,以防有人在读取事务中移除模块或设备。
remove 或 exit 函数均未调用,模块和用户应用程序均被阻止。我已经尝试了以下三个函数(显示了它们的相关结果)。
-
wait_completion(&dev->read_complete);//无限期阻塞,我要重置电脑 -
retval = wait_for_completion_interruptible(&dev->read_complete);//我可以手动杀死用户应用程序,然后删除驱动程序 -
retval = wait_for_completion_killable(&dev->read_complete);//同可中断
我的期望是当remove函数被调用时我可以调用complete_all(&dev->read_complete)并且read函数会返回一个错误。
为了消除外部因素,我在 github 上做了一个 repo,所以如果有人想自己查看行为,他们只需要克隆并按照说明操作:
模块的相关部分在这里(/src/mymodule.c)
typedef struct {
struct cdev cdv;
struct class *cls;
struct device *dev;
struct completion complete;
} mymodule_t;
mymodule_t mymod;
//Sysfs 'mymodule_test' attribute (all but the actual function is left out for brevity)
static ssize_t mymodule_test_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int retval = 0;
int value = 0;
if (sscanf(buf, "%d", &value) == 1)
{
retval = strlen(buf);
}
if (value)
{
printk("Value is: %d\n", value);
if (!completion_done(&mymod.complete))
{
complete(&mymod.complete);
}
printk("Sent Completion\n");
}
return retval;
}
//FOPS (all but 'read' function is left out for brevity)
ssize_t mymodule_read(struct file *filp, char * buf, size_t count, loff_t *f_pos)
{
printk("Read!\n");
if (completion_done(&mymod.complete))
{
reinit_completion(&mymod.complete);
}
printk("Wait for Completion\n");
wait_for_completion_interruptible(&mymod.complete);
printk("After Completion\n");
return 0;
}
static int __init mymodule_init(void)
{
...
//Register class and device
//Configure character driver with fops
init_completion(&mymod.complete);
...
}
static void __exit mymodule_exit(void)
{
...
if (!completion_done(&mymod.complete))
{
printk("Send a completion!\n");
complete(&mymod.complete);
}
//Clean up the rest of the module
...
}
module_init(mymodule_init);
module_exit(mymodule_exit);
这是我用来练习的用户空间应用程序:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include "mymodule.h"
#define FILEPATH "/dev/mymodule0"
#define TEST_SIZE 10
int main(void)
{
int fn = -1;
char buf[TEST_SIZE];
printf("Attempting to open file module file...\n");
fn = open(FILEPATH, O_RDWR);
if (fn < 0)
{
printf("Failed to open file!\n");
return -1;
}
printf("Attempting to read from the file...\n");
read(fn, &buf, TEST_SIZE);
printf("Finished reading from file\n");
return 0;
}
这是我时的 dmesg 输出
- 加载模块
- 运行用户应用程序(它打开文件,尝试读取 10 个字符,然后退出)
- 将“1”写入 sysfs 属性
- 卸载模块
[3217633.993937] Registering Driver [3217633.993995] Driver Initialized! [3217643.747791] Opened! [3217643.747800] Read! [3217643.747801] Wait for Completion [3217646.436780] Value is: 1 [3217646.436792] Sent Completion [3217646.436806] After Completion [3217646.437010] Closed! [3217727.378388] Cleanup Module [3217727.378393] Check if we need to complete anything [3217727.378395] Send a completion! [3217727.378397] Unregistering Character Driver [3217727.378400] Give back all the numbers we requested [3217727.378402] Remove the class driver [3217727.378571] Release the class [3217727.378593] Finished Cleanup Module, Exiting
如果我运行以下命令:
- 加载模块
- 运行用户应用程序
- 卸载模块
[3218223.442777] Registering Driver [3218223.442934] Driver Initialized! [3218229.378396] Opened! [3218229.378419] Read! [3218229.378422] Wait for Completion
然后模块不会卸载。如果这是一个真实的设备,比如 USB 硬盘,用户可能会在读取事务的过程中移除该设备。好像出了点问题,或者我错过了一些东西。我错过了什么吗?
【问题讨论】:
-
@Toby Speight:感谢您的编辑!
标签: linux linux-kernel linux-device-driver kernel-module