嵌入式 浅谈fcntl与ioctl函数
来源:互联网 发布:武汉编程培训 编辑:程序博客网 时间:2024/05/29 09:19
fcntl:
ioctl函数提供对连接到fd的设备驱动程序的属性和操作的访问
extern int ioctl(int fd,unsigned long int request[,char * arg ....])
arg 是参数 可选的
因为有些属性需要使用返回值 这时不一定是0了
void prinft_screen_dimension(int fd)
{
int fd; //
struct winsize buf;
if (ioctl( fd, TIOCGWINZ,&buf ) != -1){
printf("%d rows x %d cols\n",buf.ws_row,buf.ws_col);
printf("%d wide x %d tall\n",buf.ws_xpixel,buf.ws_ypixel);
}
}
但是在数据比较重要的时候担心意外事故导致缓冲数据丢失
这时我们可以设置数据存入是同步的(即关闭缓冲属性)
描述符的属性被编码到一个整数中了
fcntl通过读写该整数位来设置文件描述符的属性。
使用fcntl取得此值 修改后 再用fcntl写回
#include <fcntl.h>
fcntl 原型为
extern int fcntl(int fd, int _cmd,... )
调用形式有
fcntl(int fd,int cmd);
fcntl(int fd,int cmd,long arg)
fcntl(int fd,int cmd,struct flock *lock)
fd 值相应的描述符,cmd 是指相应的操作
相应操作可以查看man fcntl 会发现fcntl不只会干这些哟。
这里我们用fcntl进行演示(关闭fd的缓冲属性 注意只有块设备与实际文件有这个属性)
int fd,va;
va = fcntl( fd,F_GETFD);
va | = O_SYNC;
fcntl( fd,F_SETFD,va);
当然也可以直接在open的同时进行设置。
不过open的属性设置没有用fcntl那样灵活。
#include<unistd.h>
int ioctl( int fd, int request, .../* void *arg */ ); 返回0——成功, -1——出错
第一个参数 fd 指示某个文件描述符(当然也包括 套接口描述符)
第二个参数 request 指示要ioctl执行的操作
第三个参数 总是某种指针,具体的指向类型依赖于 request 参数
我们可以把和网络相关的请求(request)划分为6 类:
套接口操作
文件操作
接口操作
ARP 高速缓存操作
路由表操作
流系统
下表列出了网络相关ioctl 请求的request 参数以及arg 地址必须指向的数据类型:
类别 request 说明 (第三个参数)数据类型
SIOCATMARK 是否位于带外标记 int
套接口 SIOCSPGRP 设置套接口的进程ID或组ID int
SIOCGPGRP 获取套接口的进程ID或组ID int
文件 FIONBIO 设置/清除非阻塞IO标识 int
FIOASYNC 设置/清除信号驱动异步IO标识 int
FIONREAD 获取接收缓冲区中的字节数 int
……
SIOCGIFCONF 获取所有接口的清单 struct ifconf
接口 SIOCSIFADDR 设置接口的ip地址 struct ifreq
SIOCGIFADDR 获取接口地址 struct ifreq
SIOCSIFFLAGS 设置接口标识 struct ifreq
SIOCGIFFLAGS 获取接口标识 struct ifreq
SIOCSIFDSTADDR 设置点到点地址 struct ifreq
SIOCGIFDSTADDR 获取点到点地址 struct ifreq
SICSIFBRDADDR 设置广播地址 struct ifreq
SICGIFBRDADDR 获取广播地址 struct ifreq
……
SIOCSARP 创建/修改ARP表项 struct arpreq
ARP SIOCGARP 获取ARP表项 struct arpreq
SIOCDARP 删除ARP表项 struct arpreq
路由 ……
(有很多request请求没有列出,而且不同的系统所提供的request也有所不同,比如linux就不提供SIOCGSIZIFCONF请求)
上面的表中,用ioctl执行接口操作的请求时,要用到结构体 struct ifconf 与 struct ifreq
struct ifconf{
int ifc_len; // 缓冲区ifcu_buf的大小
union{
caddr_t ifcu_buf; // 其实就是char *类型。
struct ifreq *ifcu_req; // 为ifconf分配空间时我们用ifcu_buf指针;当要取得或设置该缓冲区中的ifreq类型时,则用这个指针
}ifc_ifcu;
};
#define ifc_buf ifc_ifcu.ifcu_buf //buffer address
#define ifc_req ifc_ifcu.ifcu_req //array of structures returned
struct ifreq
{
char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */
union {
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
struct sockaddr ifru_netmask;
struct sockaddr ifru_hwaddr;
short ifru_flags;
int ifru_ivalue;
int ifru_mtu;
struct ifmap ifru_map;
char ifru_slave[IFNAMSIZ]; /* Just fits the size */
char ifru_newname[IFNAMSIZ];
void * ifru_data;
struct if_settings ifru_settings;
} ifr_ifru;
};
#define ifr_name ifr_ifrn.ifrn_name /* interface name */
#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */
#define ifr_addr ifr_ifru.ifru_addr /* address */
#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */
#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */
#define ifr_flags ifr_ifru.ifru_flags /* flags */
#define ifr_metric ifr_ifru.ifru_ivalue /* metric */
#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */
#define ifr_map ifr_ifru.ifru_map /* device map */
#define ifr_slave ifr_ifru.ifru_slave /* slave device */
#define ifr_data ifr_ifru.ifru_data /* for use by interface */
#define ifr_ifindex ifr_ifru.ifru_ivalue /* interface index */
#define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */
#define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */
#define ifr_newname ifr_ifru.ifru_newname /* New name */
#define ifr_settings ifr_ifru.ifru_settings /* Device/proto settings*/
ifconf 结构包含了 ifreq 结构指针, 在使用ifconf之前,我们得先为其ifcu_buf指针(或者说ifcu_req指针)分配缓冲区
其他不知道该怎么说。
贴代码吧。 一个用ioctl实现的类似ifconfig命令的小程序。
设备控制接口(ioctl 函数)
回想一下我们在字符设备驱动中介绍的struct file_operations 结构,这里我们将介绍一个新的方法:
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
这是驱动程序设备控制接口函数(ioctl函数)的内核原型定义,struct inode * 和struct file* 描述了操作的文件,unsigned int 描述了ioctl命令号,这是一个重要的参数,我们稍后会对它做详细介绍。最后一个参数是unsigned long数据类型,描述了ioctl命令可能带有的参数,它可能是一个整数或指针数据。
- ioctl命令号
- dir:
- type:
- nr:
- size:
_IO(type,nr)
_IOR(type,nr,size)
_IOW(type,nr,size)
_IOWR(type,nr,size)
宏_IO用于无数据传输,宏_IOR用于从设备读数据,宏 _IOW用于向设备写数据,宏_IOWR用于同时有读写数据的IOCTL命令。相对的,Linux内核也提供了相应的宏来从ioctl命令号种解码相应的域值:
_IOC_DIR(nr)
_IOC_TYPE(nr)
_IOC_NR(nr)
_IOC_SIZE(nr)
- ioctl返回值
- ioctl参数
unsigned long __must_check copy_to_user(void __user *to,
const void *from, unsigned long n);
unsigned long __must_check copy_from_user(void *to,
const void __user *from, unsigned long n);
copy_from_user和copy_to_user一般用于复杂的或大数据交换,对于简单的数据类型,如int或char,内核提供了简单的宏来实现这个功能:
#define get_user(x,ptr)
#define put_user(x,ptr)
其中,x是内核空间的简单数据类型地址,ptr是用户空间地址指针。
我们需要牢记:在内核中是无法直接访问用户空间地址数据的。因此凡是从用户空间传递过来的指针数据,务必使用内核提供的函数来访问它们。
举例
好了,是时候举个例子了。我们将扩展我们的helloworld驱动添加ioctl函数。
首先,我们添加一个头文件来定义ioctl接口需要用到的数据(hello.h):
#ifndef _HELLO_H
#define _HELLO_H
#include <asm/ioctl.h>
#define MAXBUF 20
typedef struct _buf_data{
int size;
char data [MAXBUF];
}buf_data;
#define HELLO_IOCTL_NR_BASE 0
#define HELLO_IOCTL_NR_SET_DATA (HELLO_IOCTL_NR_BASE + 1)
#define HELLO_IOCTL_NR_MAX (HELLO_IOCTL_NR_GET_BUFF + 1)
#define HELLO_IOCTL_SET_DATA _IOR('h', HELLO_IOCTL_NR_SET_DATA, buf_data*)
#endif
然后为我们的驱动程序添加ioctl接口hello_ioctl,并实现这个函数:
static int hello_ioctl (struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
int cmd_nr;
int err;
buf_data buff;
err = 0;
cmd_nr = _IOC_NR (cmd);
switch (cmd_nr){
case HELLO_IOCTL_NR_SET_DATA:
if (copy_from_user(&buff, (unsigned char *)arg, sizeof(buf_data)))
{
err = -ENOMEM;
goto error;
}
memset(hello_buf, 0, sizeof(hello_buf));
memcpy(hello_buf, buff.data, buff.size);
break;
default:
printk("hello_ioctl: Unknown ioctl command (%d)\n", cmd);
break;
}
error:
return err;
}
static struct file_operations hello_fops = {
.read = hello_read,
.write = hello_write,
.open = hello_open,
.ioctl = hello_ioctl,
.release = hello_release,
};
后记
到这里我们已经向您展示了Linux内核驱动程序的设备控制接口(ioctl接口),详细的介绍了它的使用,并给出了一个实际的例子,尽管它很简单,但已经足够了。到这里你可以写出一个标准的Linux驱动程序了。不过这里还有个问题,那就是我们不得不从/proc/devices文件里读取设备号然后手动创建设备节点。我们是否可以让系统自动的创建这个设备节点文件呢?当然可以。不过在那之前,我们必须深入了解Linux的设备驱动模型。后面的章节我们就详细的介绍Linux的设备驱动模型及Hotplug机制。
- 嵌入式 浅谈fcntl与ioctl函数
- 文件属性fcntl函数和文件输入输出控制ioctl()函数
- 文件属性fcntl函数和文件输入输出控制ioctl()函数
- Linux文件I/O的lseek,fcntl和ioctl函数
- fcntl文件属性和ioctl文件输入输出控制函数详解
- 嵌入式 Linux下fcntl函数用法
- ioctl 和fcntl
- fcntl, ioctl, tcgetattr
- fcntl 和 ioctl
- 嵌入式 ioctl函数的实际意义小论
- 嵌入式 Linux下fcntl函数和flock函数区别说明
- Linux的ioctl和fcntl
- 系统调用fcntl 和 ioctl
- ioctl() fcntl() setsockopt() ioctlsocket() 用法
- Linux的ioctl和fcntl
- ioctl 函数与网络接口
- ioctl 函数与网络接口
- ioctl 函数与网络接口
- 筛选器“headerfiles”下已存在项目“***.h”
- 三星I9300手机在调用系统摄像头拍照onActivityResult data为null的解决方法
- iOS应用崩溃日志揭秘
- Centos-6.3-x86_64 minimal 迷你版安装笔记 - GoAccess 篇
- IBATIS中的时间插入问题
- 嵌入式 浅谈fcntl与ioctl函数
- [LeetCode]Populating Next Right Pointers in Each Node II, 解题报告
- Java的基本数据类型与流
- RedHat Enterprise Linux root密码更改
- 微信开发技术
- Java基础之protected访问权限
- 直接拿来用!最火的Android开源项目
- 分享 JS在firefox和IE下差异及解决方案
- 西游题材手游《多米诺骨牌-和悟空比IQ》