在 linux 中可以使用 ioctl() 函数实现用户程序与内核模块通信,原理相对比较简单,即在内核模块中创建设备文件,并实现 unlocked_ioctl() 文件操作接口, 在实现时,需要定义若干请求码和对应的处理逻辑,最后在用户程序中调用 ioctl() 函数向内核模块发送请求码和参数,由内核模块进行处理和响应。下面看示例代码:
内核模块:
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <linux/miscdevice.h>
enum {
ADD,
SUB,
MUL = 3,
DIV,
MOD
};
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhoumin");
static long ioctl_demo(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int nums[2];
int ret;
switch (cmd) {
case ADD:
if (copy_from_user(nums, (void *)arg, sizeof(nums)))
return -EFAULT;
ret = nums[0] + nums[1];
printk(KERN_INFO "%d + %d = %d\n", nums[0],
nums[1], ret);
break;
case SUB:
if (copy_from_user(nums, (void *)arg, sizeof(nums)))
return -EFAULT;
ret = nums[0] - nums[1];
printk(KERN_INFO "%d - %d = %d\n", nums[0], nums[1], ret);
break;
case MUL:
if (copy_from_user(nums, (void *)arg, sizeof(nums)))
return -EFAULT;
ret = nums[0] * nums[1];
printk(KERN_INFO "%d * %d = %d\n", nums[0], nums[1], ret);
break;
case DIV:
if (copy_from_user(nums, (void *)arg, sizeof(nums)) || nums[1] == 0)
return -EFAULT;
ret = nums[0] / nums[1];
printk(KERN_INFO "%d / %d = %d\n", nums[0], nums[1], ret);
break;
case MOD:
if (copy_from_user(nums, (void *)arg, sizeof(nums)) || nums[1] == 0)
return -EFAULT;
ret = nums[0] % nums[1];
printk(KERN_INFO "%d %% %d = %d\n", nums[0], nums[1], ret);
break;
default:
printk(KERN_ALERT "unsupported op: %d\n", cmd);
return -EFAULT;
}
if (copy_to_user(((int *)arg) + 2, &ret, sizeof(ret)))
return -EFAULT;
return 0;
}
struct file_operations ioctl_ops = {
.unlocked_ioctl = ioctl_demo,
};
static struct miscdevice ioctl_dev = {
MISC_DYNAMIC_MINOR,
"ioctl_demo",
&ioctl_ops,
};
static int ioctl_mod_init(void)
{
int ret;
ret = misc_register(&ioctl_dev);
if (ret) {
pr_err("ioctl_demo: misc device register failed\n");
return -1;
}
printk(KERN_ALERT "ioctl module init!\n");
return 0;
}
static void ioctl_mod_exit(void)
{
misc_deregister(&ioctl_dev);
printk(KERN_ALERT "ioctl module exit!\n");
}
module_init(ioctl_mod_init);
module_exit(ioctl_mod_exit);
用户程序:
#include <assert.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
enum {
ADD,
SUB,
MUL = 3,
DIV,
MOD,
};
int main(int argc, char *argv[])
{
int fd;
int nums[3];
if (argc != 4) {
printf("Usage: %s num1 op(+,-,x,/,%) num2\n", argv[0]);
return -1;
}
nums[0] = atoi(argv[1]);
nums[1] = atoi(argv[3]);
fd = open("/dev/ioctl_demo", O_RDONLY);
switch (argv[2][0]) {
case '+':
ioctl(fd, ADD, nums);
assert(nums[0] + nums[1] == nums[2]);
break;
case '-':
ioctl(fd, SUB, nums);
assert(nums[0] - nums[1] == nums[2]);
break;
case 'x':
ioctl(fd, MUL, nums);
assert(nums[0] * nums[1] == nums[2]);
break;
case '/':
if (nums[1] == 0) {
printf("num2 cannot be zero!\n");
goto out;
}
ioctl(fd, DIV, nums);
assert(nums[0] / nums[1] == nums[2]);
break;
case '%':
if (nums[1] == 0) {
printf("num2 cannot be zero!\n");
goto out;
}
ioctl(fd, MOD, nums);
assert(nums[0] % nums[1] == nums[2]);
break;
default:
printf("unsupported op: %s\n", argv[2]);
break;
}
printf("%d %s %d = %d\n", nums[0], argv[2], nums[1], nums[2]);
out:
close(fd);
return 0;
}
测试效果
用户程序输出:
$ ./main 19 + 91
19 + 91 = 110
$ ./main 19 - 91
19 - 91 = -72
$ ./main 19 x 91
19 x 91 = 1729
$ ./main 19 / 91
19 / 91 = 0
$ ./main 19 % 91
19 % 91 = 19
内核模块输出:
$ dmesg
[19215.796000] ioctl module init!
[19385.856000] 19 + 91 = 110
[19390.336000] 19 - 91 = -72
[19397.224000] 19 * 91 = 1729
[19418.400000] 19 / 91 = 0
[19421.144000] 19 % 91 = 19