Linux用户与内核空间交互—ioctl

Posted 为了维护世界和平_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux用户与内核空间交互—ioctl相关的知识,希望对你有一定的参考价值。

目录

简介

一、交互方法笔记与总结

二、ioctl

三、实战

1、头文件

2、应用程序

3、内核程序

4、 程序输出


简介

用户空间与内核的交互方式,使用copy_from_user(), copy_to_user().

除了这两种交互方式,内核还提供了其他高级的方式,对于写驱动来说很重要。有proc、sysfs、debugfs、netlink、ioctl。

本文学习ioctl

一、交互方法笔记与总结

procfssysfsdebugfsnetlinkioctl
容易开发容易开发与使用相对容易学习与使用非常容易学习与使用困难,必须用户空间和内核空间同步编程相对困难,必须写用户空间程序
适合场景仅仅内核,旧内核使用,避免驱动使用设备驱动为生产与调试目的的驱动接口多种接口(设备、驱动、网络、udev系统)设备驱动
接口可见性可见;用户权限控制访问可见;用户权限控制访问可见;用户权限控制访问在文件系统中隐藏,避免污染内核空间在文件系统中隐藏,避免污染内核空间
支持度已经废弃使用与用户空间的“正式”方法在内核中大量使用支持很好支持很好
是否用于debug不是对于调试非常有用不是是(通过隐藏的命令)

二、ioctl

input-output control

经常用在驱动程序中,与open、read、write一起使用。
 

API

#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...);

 读-写-读写 函数

_IO(type,nr)//没有参数
_IOR(type,nr,datatype)//读
_IOW(type,nr,datatype)//写
_IOWR(type,nr,datatype)//读/写

三、实战

1、头文件

公共头文件

/* our dummy ioctl (IOC) RESET command */
#define IOCTL_LLKD_IOCRESET             _IO(IOCTL_LLKD_MAGIC, 0)

/* our dummy ioctl (IOC) Query POWER command */
#define IOCTL_LLKD_IOCQPOWER            _IOR(IOCTL_LLKD_MAGIC, 1, int)

/* our dummy ioctl (IOC) Set POWER command */
#define IOCTL_LLKD_IOCSPOWER            _IOW(IOCTL_LLKD_MAGIC, 2, int)

2、应用程序

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include "ioctl_llkd.h"

//使用说明

int main(int argc, char **argv)

	int fd, power;


    //打开文件
	if ((fd = open(argv[1], O_RDWR, 0)) == -1) 
		perror("open");
		exit(EXIT_FAILURE);
	
	printf("device opened: fd=%d\\n", fd);

	//重置设备
	if (ioctl(fd, IOCTL_LLKD_IOCRESET, 0) == -1) 
		close(fd);
		exit(EXIT_FAILURE);
	
	printf("%s: device reset.\\n", argv[0]);

	// 2. 查询
	if (ioctl(fd, IOCTL_LLKD_IOCQPOWER, &power) == -1) 
		close(fd);
		exit(EXIT_FAILURE);
	
	printf("%s: power=%d\\n", argv[0], power);

	// 3. 翻转power值
	if (0 == power) 
		printf("%s: Device OFF, powering it On now ...\\n", argv[0]);

        //设置命令,fd 文件描述符;命令 IOCTL_LLKD_IOCSPOWER ,1 值
		if (ioctl(fd, IOCTL_LLKD_IOCSPOWER, 1) == -1) 
			close(fd);
			exit(EXIT_FAILURE);
		
		printf("%s: power is ON now.\\n", argv[0]);
	 else if (1 == power) 
		printf("%s: Device ON, powering it OFF in 3s ...\\n", argv[0]);
		sleep(3);	/* yes, careful here of sleep & signals! */

        //设置命令,fd 文件描述符;命令 IOCTL_LLKD_IOCSPOWER ,0 值
		if (ioctl(fd, IOCTL_LLKD_IOCSPOWER, 0) == -1) 
			close(fd);
			exit(EXIT_FAILURE);
		
		printf("%s: power OFF ok, exiting..\\n", argv[0]);
	

	close(fd);
	exit(EXIT_SUCCESS);

3、内核程序

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/version.h>
#include <linux/uaccess.h>
#include "ioctl_llkd.h"

#define OURMODNAME   "ioctl_llkd_kdrv"
MODULE_AUTHOR("wy");
MODULE_LICENSE("Dual MIT/GPL");
MODULE_VERSION("0.1");

static int ioctl_intf_major,power = 1;

static long ioctl_intf_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)

	int retval = 0;
	pr_debug("In ioctl method, cmd=%d\\n", _IOC_NR(cmd));

	switch (cmd) 
	case IOCTL_LLKD_IOCRESET:
		pr_debug("reset\\n");
		break;
	case IOCTL_LLKD_IOCQPOWER:	/*获取参数 */
        //将内核的值power的值,赋值给arg
        //retval是返回值
		retval = __put_user(power, (int __user *)arg);

		break;
	case IOCTL_LLKD_IOCSPOWER:	/*设置参数*/
		power = arg;//用户空间命令行传递的参数arg 赋值给power

		pr_debug("In ioctl cmd option: IOCTL_LLKD_IOCSPOWER\\n"
			"power=%d now.\\n", power);

		break;
	default:
		return -ENOTTY;
	
	return retval;


static const struct file_operations ioctl_intf_fops = 
	.llseek = no_llseek,
	.unlocked_ioctl = ioctl_intf_ioctl,	
;

static int ioctl_intf_open(struct inode *inode, struct file *filp)

	pr_debug("Device node with minor # %d being used\\n", iminor(inode));

	switch (iminor(inode)) 
	case 0:
		filp->f_op = &ioctl_intf_fops;
		break;
	default:
		return -ENXIO;
	
	if (filp->f_op && filp->f_op->open)
		return filp->f_op->open(inode, filp);
	return 0;


static struct file_operations ioctl_intf_open_fops = 
	.open = ioctl_intf_open,
;

static int __init ioctl_llkd_kdrv_init(void)

	int result;
	result = register_chrdev(ioctl_intf_major, OURMODNAME, &ioctl_intf_open_fops);
	if (result < 0) 
		pr_info("register_chrdev() failed trying to get ioctl_intf_major=%d\\n", ioctl_intf_major);
		return result;
	

	if (ioctl_intf_major == 0)
		ioctl_intf_major = result;	
	pr_debug("registered:: ioctl_intf_major=%d\\n", ioctl_intf_major);

	return 0;	


static void ioctl_llkd_kdrv_cleanup(void)

	unregister_chrdev(ioctl_intf_major, OURMODNAME);
	pr_info("removed\\n");


module_init(ioctl_llkd_kdrv_init);
module_exit(ioctl_llkd_kdrv_cleanup);

4、 程序输出

# ./ioctl_llkd_userspace /dev/ioctl_intf
device opened: fd=3
./ioctl_llkd_userspace: device reset.
./ioctl_llkd_userspace: power=0
./ioctl_llkd_userspace: Device OFF, powering it On now ...
./ioctl_llkd_userspace: power is ON now.


# ./ioctl_llkd_userspace /dev/ioctl_intf
device opened: fd=3
./ioctl_llkd_userspace: device reset.
./ioctl_llkd_userspace: power=1
./ioctl_llkd_userspace: Device ON, powering it OFF in 3s ...
./ioctl_llkd_userspace: power OFF ok, exiting..

以上是关于Linux用户与内核空间交互—ioctl的主要内容,如果未能解决你的问题,请参考以下文章

Linux用户与内核空间交互—debugfs

Linux用户与内核空间交互—debugfs

linux内核空间与用户空间信息交互方法

Linux用户与内核空间交互—netlink

Linux用户与内核空间交互—netlink

linux 内核中Netlink