字符驱动程序中的手动睡眠会永远停止 read()

Manual sleep in char driver halts read() forever

我正在学习 linux 设备驱动程序。我遇到了一个话题 'manual sleep'。所以我写了一个简单的字符驱动程序,如果没有数据写入缓冲区,读取方法将休眠

问题是读取方法永远休眠,即使数据已经写入

代码中可能存在的错误是什么? 在此先感谢您的帮助。

字符驱动程序的代码片段是

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;
}

user-space 应用程序的完整代码是

#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;
}

函数 prepare_to_waitfinish_wait 是单个 wait.

的一部分

它们 不是 wait()notify() high-level 语言的类似物,例如 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);

在这种情况下,等待完成后会自动重新武装。