【问题标题】:Manual sleep in char driver halts read() foreverchar 驱动程序中的手动睡眠会永远停止 read()
【发布时间】:2016-04-05 04:32:25
【问题描述】:

我正在学习 linux 设备驱动程序。我遇到了一个主题“手动睡眠”。所以我写了一个简单的char驱动,如果没有数据写入缓冲区,read方法会休眠

问题是即使数据已经写入,read 方法也会永远休眠

代码中可能出现的错误是什么? 提前感谢您的帮助。

char 驱动程序的代码 sn-ps 是

wait_queue_head_t q;
wait_queue_t wait;
int major_no, flag=0;
char device_buffer[100];

ssize_t myread(struct file *p, char __user *buf, size_t len, loff_t *s)
{
    printk(KERN_ALERT "myread() method\n");
    prepare_to_wait(&q, &wait, TASK_INTERRUPTIBLE);
    if(flag!=1)
    {
        schedule();
    }
    copy_to_user(buf, device_buffer, strlen(device_buffer));
    flag=0;
    return 0;
}

ssize_t mywrite(struct file *p, const char __user *buf, size_t len, loff_t *s)
{
    printk(KERN_ALERT "mywrite() method\n");
    memset(device_buffer, 0, 100);
    copy_from_user(device_buffer, buf, len);
    flag=1;
    finish_wait(&q, &wait);
    printk("%s", device_buffer);
    return 0;
}

struct file_operations p={
    .open=myopen,
    .release=myclose,
    .write=mywrite,
    .read=myread
};

int start(void)
{
    printk(KERN_ALERT "module registered\n");
    memset(device_buffer, 0, 100);
    major_no=register_chrdev(0, "mydriver", &p);
    printk(KERN_ALERT "driver major no : %d\n", major_no);
    init_waitqueue_head(&q);
    init_wait(&wait);
    return 0;
}

用户空间应用的完整代码是

#include<fcntl.h>

main()
{
    char option, m[100];
    memset(m, 0, 100);
    int fd=open("my_dev_file", O_RDWR);
    printf("%d\n", fd);
    printf("read : r\nwrite : w\n");
    scanf("%c", &option);
    switch(option)
    {
            case 'w':
                    printf("msg : ");
                    scanf("%s", m);
                    write(fd, m, strlen(m));
                    break;
            case 'r':
                    read(fd, m, 100);
                    printf("msg = %s\n", m);
                    break;
            default:
                    printf("wrong choice\n");
    }
    return 0;
}

【问题讨论】:

  • “用户空间应用程序的完整代码”甚至无法完全编译。编译时,始终启用所有警告,然后修复这些警告。 (对于 gcc,至少使用:-Wall -Wextra -pedantic。我通常还会添加:-std-c99 -Wconversion)然后修复生成的警告。首先,在使用操作系统时,main() 函数只有 2 个有效(和一个可选)签名,它们的返回类型均为 int
  • cont:代码缺少定义memset()open()printf()scanf()write()read()strlen() 所需的#include 语句并且由于调用了open(),因此应该有一个对close() 的匹配调用,但缺少代码。
  • 在调用open()函数时,一定要检查返回值,确保操作成功。调用scanf() 函数时,始终检查返回值以确保操作成功。调用read()函数时,一定要检查返回值,确保操作成功。
  • 这些头文件只是原型所必需的,而原型对于 scanf、printf、memset 和所有这些都是必需的......我可以用警告而不是错误来编译它们......可执行文件总是有效的很好...为什么要包含不必要的头文件来增加程序大小
  • 发布的代码包含一些“神奇”数字。 “神奇”数字使代码难以理解、调试和维护。 “神奇”数字包括100。建议使用#define 为“魔术”数字赋予有意义的名称,然后在整个代码中使用该有意义的名称。

标签: c linux linux-kernel operating-system kernel-module


【解决方案1】:

函数prepare_to_waitfinish_wait 是单个等待的一部分。

它们不是wait()notify()类似于java等高级语言。

更正确的等待实现可能是:

// Global scope: no need to declare `wait`, it will be locally declared in *myread*

// myread
DEFINE_WAIT(wait); // this is a *declaration*, so it should come before function's call like `printk`.
prepare_to_wait(&q, &wait, TASK_INTERRUPTIBLE);
if(flag!=1)
{
    schedule();
}
finish_wait(&q, &wait); // This is *finalization* for waiting
...

// mywrite
flag = 1;
wake_up(&q);

注意,这不是一个完全正确的例子:至少,flag 应该被检查并设置在一些关键部分,例如与自旋锁举行。但它只适用于您的简单场景。

或者,您可以使用完成

// Global scope
#include <linux/completion.h>
DECLARE_COMPLETION(comp);

// myread
wait_for_completion_interruptible(&comp);

// mywrite
complete(&comp);

在这种情况下完成将在等待后自动重新启动。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-13
    • 2020-04-18
    • 2015-06-28
    • 1970-01-01
    • 1970-01-01
    • 2017-01-05
    • 2020-03-26
    • 1970-01-01
    相关资源
    最近更新 更多