对于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
- #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
- <pre name="code" class="cpp">/********************************************************************************
- *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;
- }
- }
- </pre><br>
- <br>
- 通过gcc将其便以为可执行文件,如: gcc ict_u.c -o ict_app<br>
- 通过运行 ./ict_app get 或./ict_app set [0/1] 就可观察内核与用户空间是如何实现数据交换的。<br>
- 在运行ict_app是会去打开设备/dev/ictdev,故可能会因读写权限问题出现打开失败的问题,则可先修改/dev/ictdev的读写权限。<br>
- <pre></pre>
- <p></p>
- <pre></pre>
- <p><br>
- </p>