您的位置:首页 > 其它

内核空间与用户空间数据交换的方式之一 --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

#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的读写权限。




                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐