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

查看:130
本文介绍了如何使用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\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(),但我不确定如何在模块文件中指定应通过ioctl()调用TimerSetup().

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\n", 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天全站免登陆