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

所以我试图编写一个使用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(),但我不知道如何在我的MODULE FILE中指定TimerSetup()应该可以通过ioctl()调用。

另外,我的第二个问题是:我可以将模块和mknod用正确的主编号写入/ dev / mytimer。 但是当我试图打开()它,以便我可以从它得到文件描述符,它保持返回-1,我假设是错误的。 我确定权限没有问题(实际上,我只是确定了777)…它仍然不起作用…有什么我失踪了吗?

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

 #include <stdio.h> int main(int argc, char* argv[]) { int fd = open("/dev/mytimer", "r"); printf("fd: %d\n", fd); return 0; } 

Solutions Collecting From Web of "我如何使用ioctl()来操作我的内核模块?"

你需要的示例代码可以在drivers/watchdog/softdog.c (在编写时从Linux 2.6.33开始),它说明了正确的文件操作以及如何允许userland用ioctl()填充结构。 。

对于需要编写简单字符设备驱动程序的人来说,这实际上是一个很好的工作教程。

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

这是它的要点(尽管非常详尽)。

softdog_ioctl()您可以看到struct watchdog_info的一个简单初始化,它softdog_ioctl()了功能,版本和设备信息:

  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明显表示“看门狗书”

您可以轻松地进一步,让您的驱动程序做一些事情,并将结果放入结构中,并将其复制到用户空间。 例如,如果struct watchdog_info也有一个成员__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()是理智的最短路径。

您在file_operations结构中缺少.open函数指针,以指定在进程尝试打开设备文件时要调用的函数。 您还需要为您的ioctl函数指定一个.ioctl函数指针。

尝试通读“Linux内核模块编程指南” ,特别是第4章(字符设备文件)和第7章(与设备文件交谈)。

第4章介绍了file_operations结构,该结构持有指向由模块/驱动程序定义的函数的指针,用于执行各种操作,如openioctl

第7章提供了通过ioctls与模块/驱动器进行通信的信息。

Linux设备驱动程序,第三版是另一个很好的资源。

最小的可运行示例

在完全可重复的QEMU + Buildroot环境中进行测试,因此可以帮助其他人获得他们的ioctl工作。 GitHub上游: 内核模块 | 共享标题 | 用户区 。

最烦人的部分是理解一些低ID被劫持: 如果cmd = 2 , 则不调用ioctl ,则必须使用_IOx宏。

内核模块:

 #include <asm/uaccess.h> /* copy_from_user, copy_to_user */ #include <linux/debugfs.h> #include <linux/module.h> #include <linux/printk.h> /* printk */ #include "ioctl.h" MODULE_LICENSE("GPL"); static struct dentry *dir; static long unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long argp) { void __user *arg_user; union { int i; lkmc_ioctl_struct s; } arg_kernel; arg_user = (void __user *)argp; pr_info("cmd = %x\n", cmd); switch (cmd) { case LKMC_IOCTL_INC: if (copy_from_user(&arg_kernel.i, arg_user, sizeof(arg_kernel.i))) { return -EFAULT; } pr_info("0 arg = %d\n", arg_kernel.i); arg_kernel.i += 1; if (copy_to_user(arg_user, &arg_kernel.i, sizeof(arg_kernel.i))) { return -EFAULT; } break; case LKMC_IOCTL_INC_DEC: if (copy_from_user(&arg_kernel.s, arg_user, sizeof(arg_kernel.s))) { return -EFAULT; } pr_info("1 arg = %d %d\n", arg_kernel.si, arg_kernel.sj); arg_kernel.si += 1; arg_kernel.sj -= 1; if (copy_to_user(arg_user, &arg_kernel.s, sizeof(arg_kernel.s))) { return -EFAULT; } break; default: return -EINVAL; break; } return 0; } static const struct file_operations fops = { .owner = THIS_MODULE, .unlocked_ioctl = unlocked_ioctl }; static int myinit(void) { dir = debugfs_create_dir("lkmc_ioctl", 0); /* ioctl permissions are not automatically restricted by rwx as for read / write, * but we could of course implement that ourselves: * https://stackoverflow.com/questions/29891803/user-permission-check-on-ioctl-command */ debugfs_create_file("f", 0, dir, NULL, &fops); return 0; } static void myexit(void) { debugfs_remove_recursive(dir); } module_init(myinit) module_exit(myexit) 

共享标题:

 #ifndef IOCTL_H #define IOCTL_H #include <linux/ioctl.h> typedef struct { int i; int j; } lkmc_ioctl_struct; #define LKMC_IOCTL_MAGIC 0x33 #define LKMC_IOCTL_INC _IOWR(LKMC_IOCTL_MAGIC, 0, int) #define LKMC_IOCTL_INC_DEC _IOWR(LKMC_IOCTL_MAGIC, 1, lkmc_ioctl_struct) #endif 

用户级:

 #define _GNU_SOURCE #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include "../ioctl.h" int main(int argc, char **argv) { int fd, arg_int, ret; lkmc_ioctl_struct arg_struct; if (argc < 2) { puts("Usage: ./prog <ioctl-file>"); return EXIT_FAILURE; } fd = open(argv[1], O_RDONLY); if (fd == -1) { perror("open"); return EXIT_FAILURE; } /* 0 */ { arg_int = 1; ret = ioctl(fd, LKMC_IOCTL_INC, &arg_int); if (ret == -1) { perror("ioctl"); return EXIT_FAILURE; } printf("arg = %d\n", arg_int); printf("ret = %d\n", ret); printf("errno = %d\n", errno); } puts(""); /* 1 */ { arg_struct.i = 1; arg_struct.j = 1; ret = ioctl(fd, LKMC_IOCTL_INC_DEC, &arg_struct); if (ret == -1) { perror("ioctl"); return EXIT_FAILURE; } printf("arg = %d %d\n", arg_struct.i, arg_struct.j); printf("ret = %d\n", ret); printf("errno = %d\n", errno); } close(fd); return EXIT_SUCCESS; }