内核空间与用户空间数据交换的方式之一 --ioctl(通过字符设备演示)
2013-09-04 18:12
399 查看
对于linux而言,内核程序和用户程序分别运行在内核空间和用户空间,要实现两者的数据交换,主要有以下几种方式:系统调用,读写系统文件(procfs,sysfs, seq_file,debugfs等), Netlink, 内核模块加载参数,内核启动参数,以及设备驱动实现的设备读、写、控制(ioctl)(这种方式可以归结到读写系统文件)。
设备驱动的实现过程中一般都会通过struct file_operations来实现对设备文件读、写以及控制命令。下面就仅通过ioctl的实现来说明通过字符设备,如何实现内核空间与用户空间的数据交换的。本例分为两个部分,内核代码为ict_k.c和ict_k.h,用户代码为ict_u.c
下面为内核部分代码:
1.ict_k.h
2.ict_k.c
3.内核部分代码通过下面的Makefile文件可编译成ict_u.ko
通过gcc将其便以为可执行文件,如: gcc ict_u.c -o ict_app
通过运行 ./ict_app get 或./ict_app set [0/1] 就可观察内核与用户空间是如何实现数据交换的。
在运行ict_app是会去打开设备/dev/ictdev,故可能会因读写权限问题出现打开失败的问题,则可先修改/dev/ictdev的读写权限。
设备驱动的实现过程中一般都会通过struct file_operations来实现对设备文件读、写以及控制命令。下面就仅通过ioctl的实现来说明通过字符设备,如何实现内核空间与用户空间的数据交换的。本例分为两个部分,内核代码为ict_k.c和ict_k.h,用户代码为ict_u.c
下面为内核部分代码:
1.ict_k.h
#ifndef __ICT_K_H__ #define __ICT_K_H__ #define MAX_BUFFER_SIZE 64 #define ICTDEV_MAJOR 250 #define ICT_IOCTL_MAGIC_NUM 'K' #define ICTIOC_GETDEV_INFO _IOR(ICT_IOCTL_MAGIC_NUM, 0, int) #define ICTIOC_SETDEV_INFO _IOWR(ICT_IOCTL_MAGIC_NUM, 1, int) #endif
2.ict_k.c
/******************************************************************************** *FileName :ict_k.c * *Description :This program is the kernel part which is used to illustrate the usage of ioctl. * *Author :Michael Zhang <zhang_mq@sina.com> * *Version :V0.1 2013-09-02 *********************************************************************************/ #include <linux/kernel.h> #include <linux/module.h> #include <linux/cdev.h> #include <linux/types.h> #include <asm/uaccess.h> #include <linux/fs.h> /*struct file*/ #include <linux/slab.h> /*kfree*/ #include "ict_k.h" struct ict_dev { struct cdev cdev; char buffer[MAX_BUFFER_SIZE]; }; static int mod_param = 0; static int ictdev_major = ICTDEV_MAJOR; static struct ict_dev *ict_devp; static int devinfo = 0; /*Device open function*/ static int ictdev_open(struct inode *inode, struct file *filp) { printk(KERN_INFO"Ict device has been open.\n"); return 0; } /*ioctl: device control function*/ //static long ictdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) static long ictdev_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { int ret = 0; int tmp; void __user *argp = (void __user *)arg; switch(cmd) { case ICTIOC_GETDEV_INFO: put_user(devinfo, (int *)arg); break; case ICTIOC_SETDEV_INFO: get_user(tmp, (int __user *)argp); devinfo = tmp; printk(KERN_INFO"Set devinfo as: %d\n", devinfo); break; default: break; } return ret; } static int ictdev_release(struct inode *inode, struct file *filp) { printk(KERN_INFO"Ict device will be closed.\n"); return 0; } struct file_operations ictdev_fops = { .owner = THIS_MODULE, .open = ictdev_open, .ioctl = ictdev_ioctl, //.unlock_ioct = ictdev_ioctl, .release = ictdev_release, }; static void ict_dev_setup(dev_t devno, struct ict_dev *dev) { int ret; cdev_init(&dev->cdev, &ictdev_fops); dev->cdev.owner = THIS_MODULE; ret = cdev_add(&dev->cdev, devno, 1); if(ret) { printk(KERN_ERR"Add cdev fail.\n"); } return; } /*Ict module intilization*/ static int __init ict_ill_init(void) { int result; dev_t devno; devno = MKDEV(ictdev_major, 0); if(ictdev_major) { result = register_chrdev_region(devno, 1, "ictdev"); } else { result = alloc_chrdev_region(&devno, 0, 1, "ictdev"); ictdev_major = MAJOR(devno); } printk(KERN_INFO"ictdev_major is %d\n", ictdev_major); if(result < 0) { printk("Register/Allocate device number fail.\n"); return result; } ict_devp = kmalloc(sizeof(struct ict_dev), GFP_KERNEL); if(!ict_devp) { printk("Memory allocation fail.\n"); result = -ENOMEM; goto fail_malloc; } memset(ict_devp, 0, sizeof(struct ict_dev)); ict_dev_setup(devno, ict_devp); return 0; fail_malloc: unregister_chrdev_region(devno, 1); return result; } static void __exit ict_ill_exit(void) { cdev_del(&ict_devp->cdev); kfree(ict_devp); unregister_chrdev_region(MKDEV(ictdev_major, 0), 1); } module_init(ict_ill_init); module_exit(ict_ill_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Michael Zhang <zhang_mq@sina.com>"); module_param(mod_param, int, S_IRUGO); MODULE_PARM_DESC(mod_param, "Initialization param of ioctl illustration program."); module_param(ictdev_major, int, S_IRUGO); MODULE_PARM_DESC(mod_param, "Initialization param of ioctl illustration program.");
3.内核部分代码通过下面的Makefile文件可编译成ict_u.ko
ifneq ($(KERNELRELEASE),) obj-m := ict_k.o else KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: rm -rf *.o *.mod.c *.ko endif 在编译出ict_k.ko后,将模块记载到内核,查看设备的主设备号。 在/dev目录下通过运行以下命令来生成设备节点: sudo mkdev ictdev c 250 0 对于内核代码中,struct file_operations实现了成员ioctl,但是对于大于2.6.36内核版本,其将会被unlocked_ioctl取代。 4.用户空间代码ict_u.c /******************************************************************************** *FileName :ict_u.c * *Description :This program is the user part which is used to illustrate the usage of ioctl. * *Author :Michael Zhang <zhang_mq@sina.com> * *Version :V0.1 2013-09-04 *********************************************************************************/ #include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <fcntl.h> /**********************Define Error Return Value**********************************/ #define ICT_SUCCESS 0 #define ICT_ERROR_DEV 1 #define ICT_ERROR_PARAM 2 #define ICT_ERROR_IOCTL 3 /**************************IOCTL Releate Macro**********************************/ #define ICT_IOCTL_MAGIC_NUM 'K' #define ICTIOC_GETDEV_INFO _IOR(ICT_IOCTL_MAGIC_NUM, 0, int) #define ICTIOC_SETDEV_INFO _IOWR(ICT_IOCTL_MAGIC_NUM, 1, int) #define ICT_DEV_FILE "/dev/ictdev" static int ict_fd; static void usage() { printf("************************************************\n"); printf("ictdev [get | set [0|1]]\n"); printf(" -get Get ict device info\n"); printf(" -set Set ict device info\n"); printf(" -0 Set ict device info as 0\n"); printf(" -1 Set ict device info as 0\n"); printf("************************************************\n"); } static int parse_param(char *param) { if(*param == '1') return 1; else if(*param == '0') return 0; else usage(); return -1; } int main(int argc, char **argv) { int fd; int devinfo; if(argc < 2) { usage(); return ICT_ERROR_PARAM; } /*Open device file*/ ict_fd = open(ICT_DEV_FILE, O_RDWR); if(ict_fd < 0) { printf("Open ict device fail\n"); return ICT_ERROR_DEV; } if(strcmp("get", argv[1]) == 0) { if(ioctl(ict_fd, ICTIOC_GETDEV_INFO, &devinfo) < 0) { printf("Get ICT device info fail.\n"); return ICT_ERROR_IOCTL; } printf("ICT device info is: %d\n", devinfo); return ICT_SUCCESS; } else if(strcmp("set", argv[1]) == 0) { devinfo = parse_param(argv[2]); if(devinfo == -1) { return ICT_ERROR_PARAM; } if(ioctl(ict_fd, ICTIOC_SETDEV_INFO, &devinfo)) { printf("Set ICT device info fail.\n"); return ICT_ERROR_IOCTL; } return ICT_SUCCESS; } else { usage(); return ICT_ERROR_PARAM; } }
通过gcc将其便以为可执行文件,如: gcc ict_u.c -o ict_app
通过运行 ./ict_app get 或./ict_app set [0/1] 就可观察内核与用户空间是如何实现数据交换的。
在运行ict_app是会去打开设备/dev/ictdev,故可能会因读写权限问题出现打开失败的问题,则可先修改/dev/ictdev的读写权限。
相关文章推荐
- 转:内核空间与用户空间数据交换的方式之一 --ioctl(通过字符设备演示)
- 用户空间与内核空间数据交换的方式(2)------procfs
- 用户空间与内核空间数据交换的方式(7)------sysctl
- 用户空间与内核空间数据交换的方式------seq_file
- 用户空间与内核空间数据交换的方式(2)------procfs
- 用户空间与内核空间数据交换的方式------seq_file
- 在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分: 内核启动参数、模块参数与sysfs、sysctl、系统调用和netlink
- 用户空间与内核空间数据交换的方式(5)------内核启动参数
- 用户与内核空间数据交换的方式(1)-debugfs
- 在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分: 内核启动参数、模块参数与sysfs、sysctl、系统调用和netlink
- 在 Linux 下用户空间与内核空间数据交换的方式
- 在 Linux 下用户空间与内核空间数据交换的方式,第 2 部分: procfs、seq_file、debugfs和relayfs
- 用户与内核空间数据交换的方式(3)-seq_file
- 用户空间与内核空间数据交换的方式(3)------模块参数与sysfs
- 用户空间与内核空间数据交换的方式(8)------syscall
- 用户空间与内核空间数据交换的方式(1)------debugfs
- 用户空间与内核空间数据交换的方式(6)------模块参数与sysfs
- 用户与内核空间数据交换的方式(3)-seq_file
- 在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分: 内核启动参数、模块参数与sysfs、sysctl、系统调用和netlink
- 在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分: 内核启动参数、模块参数与sysfs、sysctl、系统调用和netlink