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

如何使用ioctl()操作内核模块?

谷梁云瀚
2023-03-14
问题内容

因此,我试图编写一个使用linux / timer.h文件的内核模块。我让它只能在模块中工作,现在我正尝试从用户程序中使它工作。

这是我的内核模块:

//Necessary Includes For Device Drivers.
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/timer.h>
#include <linux/ioctl.h>

#define DEVICE_NAME "mytimer"
#define DEVICE_FILE_NAME "mytimer"
#define MAJOR_NUM 61
#define MINOR_NUM 0

MODULE_LICENSE("Dual BSD/GPL");

static struct timer_list my_timer;

struct file_operations FileOps = 
{
    //No File Operations for this timer.
};

//Function to perform when timer expires.
void TimerExpire(int data)
{
    printk("Timer Data: %d\n", data);
}

//Function to set up timers.
void TimerSetup(void)
{
    setup_timer(&my_timer, TimerExpire, 5678);
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(5000));
}

//Module Init and Exit Functions.
int init_module(void)
{
    int initResult = register_chrdev(MAJOR_NUM, "mytimer", &FileOps);

    if (initResult < 0)
    {
        printk("Cannot obtain major number %d\n", MAJOR_NUM);

        return initResult;
    }

printk("Loading MyTimer Kernel Module...\n");


return 0;
}
void cleanup_module(void)
{
    unregister_chrdev(MAJOR_NUM, "mytimer");
    printk("Unloading MyTimer Kernel Module...\n");
}

更具体地说,我希望我的用户程序调用TimerSetup()函数。我知道我需要使用ioctl(),但是我不确定如何在我的模块文件中指定TimerSetup()应该可以通过ioctl()调用。

另外,我的第二个问题是:我可以使用正确的主编号将模块插入到/ dev /
mytimer中,也可以将其插入。但是,当我尝试使用open()以便从中获取文件描述符时,它始终返回-1,我认为这是错误的。我确保权限没问题(实际上,我确定是777)…仍然不起作用…我缺少什么吗?

这是用户程序,以防万一:

#include <stdio.h>

int main(int argc, char* argv[])
{
    int fd = open("/dev/mytimer", "r");
    printf("fd: %d\n", fd);

    return 0;
}

问题答案:

您需要的示例代码可以在drivers/watchdog/softdog.c(撰写本文时来自Linux
2.6.33)中找到,该代码说明了正确的文件操作以及如何允许用户区使用ioctl()填充结构。

对于需要编写琐碎字符设备驱动程序的任何人来说,这实际上都是一个很棒的实用教程。

在回答我自己的问题时,我剖析了softdog的ioctl界面,这可能对您有所帮助。

这是它的要旨(尽管还不是很详尽)…

在其中,softdog_ioctl()您可以看到struct watchdog_info的简单初始化,该初始化发布了功能,版本和设备信息:

    static const struct watchdog_info ident = {
            .options =              WDIOF_SETTIMEOUT |
                                    WDIOF_KEEPALIVEPING |
                                    WDIOF_MAGICCLOSE,
            .firmware_version =     0,
            .identity =             "Software Watchdog",
    };

然后,我们看一个简单的情况,用户只是想获得以下功能:

    switch (cmd) {
    case WDIOC_GETSUPPORT:
            return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;

…当然,这会将上面的初始化值填充到相应的用户空间watchdog_info中。如果copy_to_user()失败,则返回-
EFAULT,这将导致相应的用户空间ioctl()调用返回-1,并设置了有意义的errno。

注意,魔术请求实际上是在linux / watchdog.h中定义的,以便内核和用户空间共享它们:

#define WDIOC_GETSUPPORT        _IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
#define WDIOC_GETSTATUS         _IOR(WATCHDOG_IOCTL_BASE, 1, int)
#define WDIOC_GETBOOTSTATUS     _IOR(WATCHDOG_IOCTL_BASE, 2, int)
#define WDIOC_GETTEMP           _IOR(WATCHDOG_IOCTL_BASE, 3, int)
#define WDIOC_SETOPTIONS        _IOR(WATCHDOG_IOCTL_BASE, 4, int)
#define WDIOC_KEEPALIVE         _IOR(WATCHDOG_IOCTL_BASE, 5, int)
#define WDIOC_SETTIMEOUT        _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
#define WDIOC_GETTIMEOUT        _IOR(WATCHDOG_IOCTL_BASE, 7, int)
#define WDIOC_SETPRETIMEOUT     _IOWR(WATCHDOG_IOCTL_BASE, 8, int)
#define WDIOC_GETPRETIMEOUT     _IOR(WATCHDOG_IOCTL_BASE, 9, int)
#define WDIOC_GETTIMELEFT       _IOR(WATCHDOG_IOCTL_BASE, 10, int)

WDIOC显然表示“看门狗ioctl”

您可以轻松地将这一步骤更进一步,让驱动程序执行某项操作并将其结果放入结构中,然后将其复制到用户空间。例如,如果struct
watchdog_info也有一个member __u32 result_code。注意,__u32只是内核的版本uint32_t

使用ioctl(),用户可以将对象的地址(可以是结构,整数)传递给内核,无论内核希望内核将其答复写入相同的对象并将结果复制到提供的地址中。

您需要做的第二件事是确保设备在有人打开,读取,写入或使用像ioctl()这样的钩子时都知道该怎么做,通过研究softdog可以轻松地看到它们。

感兴趣的是:

static const struct file_operations softdog_fops = {
        .owner          = THIS_MODULE,
        .llseek         = no_llseek,
        .write          = softdog_write,
        .unlocked_ioctl = softdog_ioctl,
        .open           = softdog_open,
        .release        = softdog_release,
};

在哪里看到unlocked_ioctl处理程序将… …您猜对了,softdog_ioctl()。

我认为您可能是在并置一层复杂的层,当处理ioctl()时,它实际上并不存在,这真的很简单。出于同样的原因,除非绝对必要,否则大多数内核开发人员都不愿添加新的ioctl接口。太容易忘记ioctl()将要填充的类型与执行此操作所使用的魔术的关系,这是copy_to_user()失败通常导致内核腐烂而导致大量用户空间进程卡住的主要原因磁盘睡眠。

对于计时器,我同意,ioctl()是理智的最短路径。



 类似资料:
  • Important 要用内核模块操作,必须有一个在运行的 Ceph 集群。 获取映像列表 要挂载块设备映像,先罗列出所有的映像。 rbd list 映射块设备 用 rbd 把映像名映射为内核模块。必须指定映像名、存储池名、和用户名。若 RBD 内核模块尚未加载, rbd 命令会自动加载。 sudo rbd map {pool-name}/{image-name} --id {user-name}

  • 根据Docker基于LXC这一事实,我的理解是Docker容器共享来自其主机操作系统的各种资源。我关心的是CPU核心。下面是一个场景: 主机linux操作系统有8个内核 我必须在上面的主机操作系统上部署一组docker容器 我需要部署的一些docker容器更适合使用2核 a) 因此,如果我在该主机上运行所有docker容器,它们是否会像在该主机操作系统上作为正常安装的应用程序运行一样,根据需要消耗

  • 问题内容: 我正在从事学术项目,该项目修改了一些代码并包含了新代码。 我正在使用QEMU加载修改后的内核并进行测试。 但是,我发现某些操作系统需要完整的操作系统才能进行调试。 没有它可能吗? 或者,这是可以与Kernel 2.6一起用于系统的发行版。除了运行程序的功能(包括网络支持)以外,发行版不需要具有任何功能。 问题答案: 我认为最简单的方法是使用buildroot http://buildr

  • danger 以下操作不支持内容表自定义字段的获取,建议使用最新内容操作接口。 内容库 获取内容库详情 接口 GET https://cloud.minapp.com/oserve/v1/content/group/:content_group_id/ 其中 :content_group_id 需替换为你的内容库 ID 代码示例 {% tabs getGroupDetailCurl=”Curl”,

  • 本文档介绍了内容的获取(包括内容表的自定义字段)和内容的创建、编辑和删除等操作 获取内容详情 接口 GET https://cloud.minapp.com/userve/v1/content/:content_group_id/text/:text_id/ 其中 content_group_id 是内容库的 ID, text_id 是内容的 ID 代码示例 var axios = require

  • 本文档介绍了内容的获取(包括内容表的自定义字段)和内容的创建、编辑和删除等操作 获取内容详情 接口 GET https://cloud.minapp.com/oserve/v1/content/:content_group_id/text/:text_id/ 其中 content_group_id 是内容库的 ID, text_id 是内容的 ID 代码示例 {% tabs getRichText