内核空间与用户空间数据交换的方式之一 --ioctl(通过字符设备演示)

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



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值