转载_使用ioctl和内核交换数据
来源:互联网 发布:淘宝延长收货时间 花呗 编辑:程序博客网 时间:2024/04/28 06:22
1. 前言
使用ioctl系统调用是用户空间向内核交换数据的常用方法之一,从ioctl这个名称上看,本意是针对I/O设备进行的控制操作,但实际并不限制是真正的I/O设备,能是所有一个内核设备即可。
2. 基本过程
在内核空间中ioctl是非常多内核操作结构的一个成员函数,如文件操作结构struct file_operations(include/linux/fs.h)、协议操作结构struct proto_ops(include/linux/net.h)等、tty操作结构struct tty_driver(include/linux/tty_driver.h)等,而这些操作结构分别对应各种内核设备,只要在用户空间打开这些设备,如I/O设备可用open(2)打开,网络协议可用socket(2)打开等,获取一个文件描述符后,就能在这个描述符上调用ioctl(2)来向内核交换数据。
3. ioctl(2)
ioctl(2)函数的基本使用格式为:
int ioctl(int fd, int cmd, void *data)
第一个参数是文件描述符;cmd是操作命令,一般分为GET、SET及其他类型命令,GET是用户空间进程从内核读数据,SET是用户空间进程向内核写数据,cmd虽然是个整数,不过有一定的参数格式的,下面再周详说明;第三个参数是数据起始位置指针,
cmd命令参数是个32位整数,分为四部分:
dir(2b) size(14b) type(8b) nr(8b)
周详定义cmd要包括这4个部分时可使用宏_IOC(dir,type,nr,size)来定义,而最简单情况下使用_IO(type, nr)来定义就能了,这些宏都在include/asm/ioctl.h中定义
本文cmd定义为:
#define NEWCHAR_IOC_MAGIC 'M'
#define NEWCHAR_SET _IO(NEWCHAR_IOC_MAGIC, 0)
#define NEWCHAR_GET _IO(NEWCHAR_IOC_MAGIC, 1)
#define NEWCHAR_IOC_MAXNR 1
要定义自己的ioctl操作,能有两个方式,一种是在现有的内核代码中直接添加相关代码进行支持,比如想通过socket描述符进行ioctl操作,可在net/ipv4/af_inet.c中的inet_ioctl()函数中添加自己定义的命令和相关的处理函数,重新编译内核即可,不过这种方法一般不推荐;第二种方法是定义自己的内核设备,通过设备的ioctl()来操作,能编成模块,这样不影响原有的内核,这是最通常的做法。
4. 内核设备
为进行ioctl操作最通常是使用字符设备来进行,当然定义其他类型的设备也能。在用户空间,可使用mknod命令建立一个字符类型设备文件,假设该设备的主设备号为123,次设备号为0:
mknode /dev/newchar c 123 0
如果是编程的话,能用mknode(2)函数来建立设备文件。
建立设备文件后再将该设备的内核模块文件插入内核,就能使用open(2)打开/dev/newchar文件,然后调用ioctl(2)来传递数据,最后用close(2)关闭设备。而如果内核中还没有插入该设备的模块,open(2)时就会失败。
由于内核内存空间和用户内存空间不同,要将内核数据拷贝到用户空间,要使用专用拷贝函数copy_to_user();要将用户空间数据拷贝到内核,要使用copy_from_user()。
要最简单实现以上功能,内核模块只需要实现设备的open, ioctl和release三个函数即可,
下面介绍程式片断:
static int newchar_ioctl(struct inode *inode, struct file *filep,
unsigned int cmd, unsigned long arg);
static int newchar_open(struct inode *inode, struct file *filep);
static int newchar_release(struct inode *inode, struct file *filep);
// 定义文件操作结构,结构中其他元素为空
struct file_operations newchar_fops =
{
owner: THIS_MODULE,
ioctl: newchar_ioctl,
open: newchar_open,
release: newchar_release,
};
// 定义要传输的数据块结构
struct newchar{
int a;
int b;
};
#define MAJOR_DEV_NUM 123
#define DEVICE_NAME "newchar"
打开设备,非常简单,就是增加模块计数器,防止在打开设备的情况下删除模块,
当然想搞得复杂的话可进行各种限制检查,如只允许指定的用户打开等:
static int newchar_open(struct inode *inode, struct file *filep)
{
MOD_INC_USE_COUNT;
return 0;
}
关闭设备,也非常简单,减模块计数器:
static int newchar_release(struct inode *inode, struct file *filep)
{
MOD_DEC_USE_COUNT;
return 0;
}
进行ioctl调用的基本处理函数
static int newchar_ioctl(struct inode *inode, struct file *filep,
unsigned int cmd, unsigned long arg)
{
int ret;
// 首先检查cmd是否合法
if (_IOC_TYPE(cmd) != NEWCHAR_IOC_MAGIC) return -EINVAL;
if (_IOC_NR(cmd) > NEWCHAR_IOC_MAXNR) return -EINVAL;
// 错误情况下的缺省返回值
ret = EINVAL;
switch(cmd)
{
case KNEWCHAR_SET:
// 设置操作,将数据从用户空间拷贝到内核空间
{
struct newchar nc;
if(copy_from_user(&nc, (const char*)arg, sizeof(nc)) != 0)
return -EFAULT;
ret = do_set_newchar(&nc);
}
break;
case KNEWCHAR_GET:
// GET操作通常会在数据缓冲区中先传递部分初始值作为数据查找条件,获取全部
// 数据后重新写回缓冲区
// 当然也能根据具体情况什么也不传入直接向内核获取数据
{
struct newchar nc;
if(copy_from_user(&nc, (const char*)arg, sizeof(nc)) != 0)
return -EFAULT;
ret = do_get_newchar(&nc);
if(ret == 0){
if(copy_to_user((unsigned char *)arg, &nc, sizeof(nc))!=0)
return -EFAULT;
}
}
break;
}
return ret;
}
模块初始化函数,登记字符设备
static int __init _init(void)
{
int result;
// 登记该字符设备,这是2.4以前的基本方法,到2.6后有了些变化,
// 是使用MKDEV和cdev_init()来进行,本文还是按老方法
result = register_chrdev(MAJOR_DEV_NUM, DEVICE_NAME, &newchar_fops);
if (result
}
模块退出函数,登出字符设备
static void __exit _cleanup(void)
{
int result;
result = unregister_chrdev(MAJOR_DEV_NUM, DEVICE_NAME);
if (result
return;
}
module_init(_init);
module_exit(_cleanup);
5. 结论
用ioctl()在用户空间和内核空间传递数据是最常用方法之一,比较简单方便,而且能在同一个ioctl中对不同的命令传送不同的数据结构,本文只是为描述方便而在不同命令中使用了相同的数据结构。- 转载_使用ioctl和内核交换数据
- 使用ioctl和内核交换数据
- 使用ioctl和内核交换数据
- 使用ioctl与内核交换数据
- 使用ioctl与内核交换数据
- 使用ioctl与内核交换数据
- 使用ioctl与内核交换数据
- linux驱动开发之字符设备--内核和用户空间数据的交换(ioctl)
- 内核空间与用户空间数据交换的方式之一 --ioctl(通过字符设备演示)
- 使用sockopt与内核交换数据
- 数据结构_交换数据
- (转载) 在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分: 内核启动参数、模块参数与sysfs、sysctl、系统调用和netlink
- 【转载】在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分: 内核启动参数、模块参数与sysfs、sysctl、系统调用和netlink
- 利用proc 实现内核和用户态交换数据
- linux用户空间和内核空间交换数据
- 利用proc 实现内核和用户态交换数据
- 用户空间和内核空间数据交换方式-sysctl
- linux用户空间和内核空间交换数据
- 常用命令行工具
- Ubuntu11.10下配置JAVA开发环境(JDK+Eclipse)
- 如何安装MPlayer?
- qemu-kvm就是用了TMD多庫
- 表中误删数据恢复
- 转载_使用ioctl和内核交换数据
- Flex 制作EPUB
- 802.11协议帧间间隔-SIFS,DIFS,PIFS,EIFS 及 slottime
- Ribbon2: 创建动态的Ribbon库
- 【转】MFC和批处理方法遍历文件夹
- 系统启动后的 wifi加载 过程-图解
- PDF转换成图片-11种方案
- 封装自己的ghost xp sp3
- AF_UNIX 地址系列 (