当前位置: 首页 > 面试题库 >

如何在内核模块代码中添加轮询功能?

澹台奇略
2023-03-14
问题内容

据我所知,要从内核空间通知用户空间,一种方法是使用轮询。这意味着内核驱动程序应首先提供轮询方法。下面的代码是从互联网上找到的,它确实有效!

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <asm/uaccess.h>

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Fortune Cookie Kernel Module");
MODULE_AUTHOR("M. Tim Jones");

#define MAX_COOKIE_LENGTH       PAGE_SIZE

static struct proc_dir_entry *proc_entry;
static char *cookie_buf;  // Space for fortune strings
static int write_index;   // Index to write next fortune
static int read_index;    // Index to read next fortune

ssize_t fortune_write( struct file *filp, const char __user *buff,
                        unsigned long len, void *data )
// Refer to: ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
{
  int space_available = (MAX_COOKIE_LENGTH-write_index);

  if (len > space_available) {
    printk(KERN_INFO "fortune: cookie buffer is full!\n");
    return -ENOSPC;
  }

  if (copy_from_user( &cookie_buf[write_index], buff, len )) {
    return -EFAULT;
  }

  write_index += len;
  cookie_buf[write_index-1] = 0;

  return len;
}

ssize_t fortune_read(struct file *file, char *buf, size_t count, loff_t *f_pos){
// Refer to: ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    int len;

    //there's no fortune or a fortune has already been read
    //the *f_pos > 0 hack is needed because `cat /proc/fortune` would otherwise
    //display every thing in the cookie_buf
    if(write_index == 0 || *f_pos > 0){
        return 0;
    }

    // cicle through fortunes
    if(read_index >= write_index){
        read_index = 0;
    }

    len = sprintf(buf, "%s\n", &cookie_buf[read_index]);

    read_index += len;
    *f_pos += len;

    return len;
}

static const struct file_operations proc_test_fops = {
   .owner        = THIS_MODULE,
//    .open        = led_proc_open,
   .read        = fortune_read,
//    .llseek        = seq_lseek,
//    .release    = single_release,
   .write        = fortune_write,
//    unsigned int (*poll) (struct file *, struct poll_table_struct *);
//    int (*fasync) (int, struct file *, int);
};

int __init init_fortune_module( void )
{
    int ret = 0;
    cookie_buf = (char *)vmalloc( MAX_COOKIE_LENGTH );
    if (!cookie_buf) {
        ret = -ENOMEM;
    } else {
        memset( cookie_buf, 0, MAX_COOKIE_LENGTH );
//        proc_entry = create_proc_entry( "fortune", 0644, NULL );
        proc_entry = proc_create( "fortune", 0644, NULL, &proc_test_fops );

        if (proc_entry == NULL) {
            ret = -ENOMEM;
            vfree(cookie_buf);
            printk(KERN_INFO "fortune: Couldn't create proc entry\n");
        } else {
            write_index = 0;
            read_index = 0;
            printk(KERN_INFO "fortune: Module loaded.\n");
        }
    }

    return ret;
}

void __exit exit_fortune_module( void )
{
//    remove_proc_entry("fortune", &proc_entry);
    proc_remove(proc_entry);
    vfree(cookie_buf);
    printk(KERN_INFO "fortune: Module unloaded.\n");
}

module_init( init_fortune_module );
module_exit( exit_fortune_module );

我可以这样使它起作用:

echo "hello" > /proc/fortune

然后

cat /proc/fortune

看结果。

但是如何添加轮询方法呢?我试了几次,但还是失败了。有人可以帮忙吗?谢谢!


问题答案:

您可以在内核本身中找到一些很好的示例。看下一个文件:

  • 驱动程序/char/rtc.c
  • fs / proc / kmsg.c
  • drivers / char / random.c

要将poll()功能添加到代码中,请遵循以下步骤。

  1. 包括所需的标题:
#include <linux/wait.h>
#include <linux/poll.h>
  1. 声明waitqueue变量:
static DECLARE_WAIT_QUEUE_HEAD(fortune_wait);
  1. 添加fortune_poll()函数并将其(作为.poll回调)添加到文件操作结构中:
static unsigned int fortune_poll(struct file *file, poll_table *wait)
{
poll_wait(file, &fortune_wait, wait);
if (new-data-is-ready)
    return POLLIN | POLLRDNORM;
return 0;
}

static const struct file_operations proc_test_fops = {
....
.poll = fortune_poll,
};

请注意,POLLIN | POLLRDNORM如果您有一些新数据要读取,并且0没有新数据要读取(poll()呼叫超时),则应返回。有关详细信息,请参见man2民意调查。

  1. 有了新数据后,通知您的等待队列:
    wake_up_interruptible(&fortune_wait);
    

这是实现poll()操作的基本内容。根据您的任务,可能需要在函数中使用一些waitqueue
API
.read(例如wait_event_interruptible())。



 类似资料:
  • 问题内容: 我有一个同时具有两个外部内核模块和一个用户空间守护程序的应用程序。我想在启动时从用C编写的守护程序代码中加载模块,然后在干净退出时将其卸载。我可以用比使用相应的方式更干净的方式加载它们吗? 问题答案: 最小的可运行示例 使用此简单的参数打印机模块,在QEMU + Buildroot VM和Ubuntu 16.04主机上进行了测试。 我们使用/ 和 Linux系统调用。 Linux内核为

  • 问题内容: 在不深入了解 为什么 的细节的情况下,我正在寻找一种干净的(尽可能)的方法来替换可加载模块中的内核函数和系统调用。我最初的想法是编写一些代码来覆盖某些功能,这些功能将采用原始功能(可能的话, 调用 该功能),然后添加一些自己的代码。关键是我编写的函数必须具有原始函数的名称,因此其他代码在尝试访问它时将改为访问我的函数。 通过将代码放入适当的函数中,我可以轻松地(相对地)直接在内核中执行

  • 问题内容: 我正在研究一个Linux内核模块,该模块注册一个来自定制板的中断的回调,并将接收到的数据放入char设备接口后面的队列中,以供应用程序处理。即使没有来自板的中断,该模块也需要不断地监视和测量来自板的中断和数据,因此它具有另一个根据时间触发的回调。 当前的实现使用RTC中断作为恒定的定时器源。我禁用了内核RTC驱动程序(),并请求IRQ 8,并将计时器回调作为RTC中断处理程序。RTC芯

  • 主要内容:initramfe虚拟文件系统GRUB 加载了内核之后,内核首先会再进行二次系统的自检,而不一定使用 BIOS 检测的硬件信息。这时内核终于开始替代 BIOS 接管 Linux 的启动过程了。 内核完成再次系统自检之后,开始采用动态的方式加载每个硬件的模块,这个动态模块大家可以想象成硬件的驱动(默认 Linux 硬件的驱动是不需要手工安装的,如果是重要的功能,则会直接编译到内核当中;如果是非重要的功能,比如硬件驱动会编译为模块

  • 问题内容: 我尝试安装内核模块。它已成功安装在容器内。确实令人惊讶,但是没有在容器内或主机系统中列出此模块。如何在容器中装入新的内核模块?(容器,主机) 问题答案: 容器通过系统调用与内核交互,并且不包含内核的任何部分或容器内部的内核模块。这就是为什么容器设计轻巧且便于携带的原因之一。xfsprogs也是用户空间程序,而不是内核模块。 如何在容器中加载新的内核模块?(CentOS容器,Ubuntu

  • 内核模块 对于模块而言,引导选项只能用于直接编译到核心中的模块,格式是"模块名.选项=值",比如"usbcore.blinkenlights=1"。 动态加载的模块则可以在 modprobe 命令行上指定相应的选项值,比如"modprobe usbcore blinkenlights=1"。 可以使用"modinfo -p ${modulename}"命令显示可加载模块的所有可用选项。已经加载到内