我如何使用 ioctl() 来操作我的内核模块? [英] How do I use ioctl() to manipulate my kernel module?

查看:17
本文介绍了我如何使用 ioctl() 来操作我的内核模块?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

So I'm trying to write a kernel module that uses the linux/timer.h file. I got it to work inside just the module, and now I am trying to get it to work from a user program.

这是我的内核模块:

//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
", 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
", MAJOR_NUM);

        return initResult;
    }

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


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

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

More specifically, I want my user program to call the TimerSetup() function. I know that I'll need to use ioctl() but I'm not sure how to specify in my MODULE FILE that TimerSetup() should be callable via ioctl().

另外,我的第二个问题:我能够使用正确的主编号插入我的模块,并且 mknod 到/dev/mytimer 中.但是当我尝试 open() 它以便我可以从中获取文件描述符时,它一直返回-1,我认为这是错误的.我确定权限没问题(事实上,我把它设置为 777 只是为了确定)......它仍然不起作用......我错过了什么吗?

Also, my second question: I was able to insmod my module and also mknod into /dev/mytimer with the correct major number. But when I tried to open() it so that I can get the file descriptor from it, it kept returning -1, which I'm assuming is wrong. I made sure the permissions were fine (in fact, I made it 777 just to be sure)... It still doesn't work... Is there something I'm missing?

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

Here is the user program just in case:

#include <stdio.h>

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

    return 0;
}

推荐答案

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

The example code you need can be found in drivers/watchdog/softdog.c (from Linux 2.6.33 at the time this was written), which illustrates proper file operations as well as how to permit userland to fill a structure with ioctl().

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

It's actually a great, working tutorial for anyone who needs to write trivial character device drivers.

我在回答我自己的问题时剖析了softdog的ioctl接口,这可能会有所帮助给你.

I dissected softdog's ioctl interface when answering my own question, which may be helpful to you.

这是它的要点(虽然远非详尽无遗)......

Here's the gist of it (though far from exhaustive) ...

softdog_ioctl() 中,您会看到 struct watchdog_info 的简单初始化,用于通告功能、版本和设备信息:

In softdog_ioctl() you see a simple initialization of struct watchdog_info that advertises functionality, version and device information:

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

然后我们看一个简单的案例,用户只想获得这些能力:

We then look at a simple case where the user just wants to obtain these capabilities:

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

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

... which of course, will fill the corresponding userspace watchdog_info with the initialized values above. If copy_to_user() fails, -EFAULT is returned which causes the corresponding userspace ioctl() call to return -1 with a meaningful errno being set.

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

Note, the magic requests are actually defined in linux/watchdog.h , so that the kernel and userspace share them:

#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"

WDIOC obviously signifying "Watchdog ioctl"

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

You can easily take that a step further, having your driver do something and place the result of that something in the structure and copy it to userspace. For instance, if struct watchdog_info also had a member __u32 result_code. Note, __u32 is just the kernel's version of uint32_t.

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

With ioctl(), the user passes the address of an object, be it a structure, integer, whatever to the kernel expecting the kernel to write its reply in an identical object and copy the results to the address that was provided.

您需要做的第二件事是确保您的设备知道当有人打开、读取、写入或使用像 ioctl() 这样的钩子时要做什么,您可以通过学习轻松了解软狗.

The second thing you are going to need to do is make sure your device knows what to do when someone opens, reads from it, writes to it, or uses a hook like ioctl(), which you can easily see by studying 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().

Where you see the unlocked_ioctl handler going to ... you guessed it, softdog_ioctl().

我认为您可能在处理 ioctl() 时并列了一层实际上并不存在的复杂性,它真的就是这么简单.出于同样的原因,除非绝对必要,否则大多数内核开发人员都不赞成添加新的 ioctl 接口.很容易忘记 ioctl() 将要填充的类型与您用来填充的魔法相比,这是 copy_to_user() 失败的主要原因,通常会导致内核腐烂,成群结队的用户空间进程卡在其中磁盘休眠.

I think you might be juxtaposing a layer of complexity that really doesn't exist when dealing with ioctl(), it really is that simple. For that same reason, most kernel developers frown on new ioctl interfaces being added unless they are absolutely necessary. Its just too easy to lose track of the type that ioctl() is going to fill vs the magic you use to do it, which is the primary reason that copy_to_user() fails often resulting in the kernel rotting with hordes of userspace processes stuck in disk sleep.

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

For a timer, I agree, ioctl() is the shortest path to sanity.

这篇关于我如何使用 ioctl() 来操作我的内核模块?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆