Linux-IO操作之fcntl 和 ioctl

news2024/11/26 21:18:34

fcntl函数,也就是file control,提供了对文件描述符的各种操作。另一个常见的控制文件描述符的属性和行为的系统调用是ioctl,而且ioctlfcntl能够执行更多的控制。但是,对于控制文件描述符常见的属性和行为,fcntl函数是由POSIX规范指定的首选方法

  • ioctl()是底层的系统调用(system call),所以跨平台特性不好。

  • 而fcntl则是被封装的函数,各个OS都是支持的。

可参考:

Unix/Linux编程:fcntl函数总结-CSDN博客

【Linux C | 文件I/O】fcntl函数详解 | 设置描述符非阻塞、文件(记录)锁-CSDN博客

fcntl函数可以改变一个已打开的文件的属性,可以重新设置读、写、追加、非阻塞等标志(这些标志称为File Status Flag),而不必重新open文件。

通过fcntl设置的都是当前进程如何访问设备或文件的访问控制属性,例如读、写、追加、非阻塞、加锁等,但并不设置文件或设备本身的属性,例如文件的读写权限、串口波特率等。

int fcntl(int fd, int cmd);

int fcntl(int fd, int cmd, long arg);

int fcntl(int fd, int cmd, struct flock *lock);

ioctl函数用于设置某些设备本身的属性,例如串口波特率、终端窗口大小,注意区分这两个函数的作用。

ioctl用于向设备发控制和配置命令,有些命令也需要读写一些数据,但这些数据是不能用read/write读写的,称为Out-of-band数据。也就是说,read/write读写的数据是in-band数据,是I/O操作的主体,而ioctl命令传送的是控制信息,其中的数据是辅助的数据。例如,在串口线上收发数据通过ead/write操作,而串口的波特率、校验位、停止位通过ioctl设置,A/D转换的结果通过read读取,而A/D转换精度和工作频率通过ioctl设置。

int ioctl(int d, int request, ...);

fcntl 函数介绍

fcntl - 对一个打开的文件描述符执行一系列控制操作。

#include <unistd.h>

#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );

fcntl() 对打开的文件描述符fd执行下面操作之一。操作由cmd决定 fcntl() 可以接受可选的第三个参数。是否需要此参数由cmd确定。所需要的 参数类型在每个cmd名称后面的括号中表示(在大多数情况下,所需 的类型是int,我们使用名称arg标识参数),如果参数不是必需的, 则指定void。 第三个参数以省略号来表示,这意味着可以将其设置为不同的类型, 或者加以省略。内核会依据 cmd 参数(如果有的话)的值来确定该 参数的数据类型

返回值: 失败返回-1,并设置errno 功能:

复制一个已经有的描述符(cmd=F_DUPFD或者F_DUPFD_CLOEXEC)

获取/设置文件描述符标志(cmd=F_GETFD或者F_SETFD)

获取/设置文件状态标志(cmd=F_GETFL或者F_SETFL)

获取/设置异步IO所有权(cmd=F_GETOWN或者F_SETOWN)

获取/设置记录锁(cmd=F_GETLK或者F_SETLK或者F_SETLKW)

本文主要记录前三个功能。

复制文件描述符(F_DUPFD、F_DUPFD_CLOEXEC)

F_DUPFD(int)

F_DUPFD(int) 表示使用 F_DUPFD 作为cmd时,第三个参数需要传入int型数据。

cmd为F_DUPFD表示复制文件描述符fd。调用成功会返回新的描述符。新描述符使用大于或等于arg参数的编号最低的可用文件描述符复制文件描述符fd。

// fcntl_F_DUPFD.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main()
{
	int fd = open("./fcntl_F_DUPFD", O_RDWR | O_CREAT | O_TRUNC, 0775);
	int fcntlFd = fcntl(fd, F_DUPFD, 0); // 指定从 0 开始分配最小的可用描述符作为新描述符
	int dupFd = dup(fd); // 等效于 fcntl(fd, F_DUPFD, 0);
	
	close(fd);
	close(fcntlFd);
	close(dupFd);
	return 0;
}

F_DUPFD_CLOEXEC(int)

F_DUPFD_CLOEXEC(int) 表示使用 F_DUPFD_CLOEXEC 作为cmd时,第三个参数需要传入int型数据。

cmd为F_DUPFD_CLOEXEC的功能与F_DUPFD类似,区别在于F_DUPFD_CLOEXEC在复制的同时会设置文件描述符标志FD_CLOEXEC,用来表示该描述符在执行完fork+exec系列函数创建子进程时会自动关闭,以防止它们被传递给子进程。

获取/设置文件描述符标志(F_GETFD、F_SETFD)

当前只定义了一个文件描述符标志FD_CLOEXEC。注意,是文件描述符标志,不是文件描述符本身。

FD_CLOEXEC用来表示该描述符在执行完fork+exec系列函数创建子进程时会自动关闭,以防止它们被传递给子进程。

为什么要这样做呢?因为当一个进程调用exec系列函数(比如execve)来创建子进程时,所有打开的文件描述符都会被传递给子进程。如果文件描述符没有设置FD_CLOEXEC标志,这些文件将保持打开状态并继续对子进程可见。这可能导致潜在的安全风险或者意外行为。

文件描述符的FD_CLOEXEC标志可以通过三个方法得到:

1、调用open函数是,指定 O_CLOEXEC

2、通过fcntl函数使用F_DUPFD_CLOEXEC复制文件描述符,新的描述符就是FD_CLOEXEC

3、通过fcntl函数使用F_SETFD直接设置FD_CLOEXEC。

F_GETFD(void) :表示使用 F_GETFD 作为cmd时,不需要传入第三个参数。

功能:获取文件描述符标志。

返回值:

成功返回文件描述符标志

失败返回 -1.

F_SETFD(int):表示使用 F_SETFD 作为cmd时,传入第三个参数是int型的。

功能:设置文件描述符标志,第三个参数传入新的标志值。

返回值:

成功返回 0

失败返回 -1.

获取/设置文件状态标志(F_GETFL、F_SETFL)

文件状态标志就是open时指定的flags标志。

F_GETFL(void) :表示使用 F_GETFL 作为cmd时,不需要传入第三个参数。

功能:获取文件状态标志。

返回值:

成功返回文件状态标志

失败返回 -1.

访问方式标志:O_RDONLY 、O_WRONLY、O_RDWR。这3个值是互斥的,因此首先必须用屏蔽O_ACCMODE取得访问方式位,然后将结果与这3个值中的每一个相比较。

F_SETFL(int):表示使用 F_SETFL 作为cmd时,传入第三个参数是int型的。

功能:设置文件状态标志,第三个参数传入新的文件状态标志值。

返回值:

成功返回 0

失败返回 -1.

在Linux上,只能设置这5个文件状态标志:O_APPEND、 O_ASYNC、 O_DIRECT、 O_NOATIME、O_NONBLOCK,其中最常用的是将文件描述符设置成非阻塞(O_NONBLOCK),特别是在网络编程中很常见。

看例子,设置文件状态标志在日常使用中,就用来设置非阻塞,其他的可以先不关注。

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main()
{
	int fd = open("./fcntl_F_GETFL", O_RDWR | O_CREAT | O_TRUNC | O_NONBLOCK, 0775);
	int flag = fcntl(fd, F_GETFL);
	if(flag<0)
		return ;
	
	printf("flag=%x, O_ACCMODE=%x [%x %x %x] [%x %x %x %x %x %x %x][%x %x]\n", 
		flag & O_ACCMODE,O_ACCMODE,O_RDONLY,O_WRONLY,O_RDWR,O_APPEND,O_NONBLOCK,O_ASYNC,O_DSYNC,O_RSYNC,O_FSYNC,O_SYNC,__O_DIRECT,__O_NOATIME);

	fcntl(fd, F_SETFL, flag | O_NONBLOCK);
	close(fd);
	return 0;
}

更多待补充。

ioctl 函数详解

参考:

https://blog.csdn.net/qq_19923217/article/details/82698787

https://www.cnblogs.com/tdyizhen1314/p/4896689.html

ioctl 是设备驱动程序中设备控制接口函数,一个字符设备驱动通常会实现设备打开、关闭、读、写等功能,在一些需要细分的情境下,如果需要扩展新的功能,通常以增设 ioctl() 命令的方式实现。

用户空间 ioctl

#include <sys/ioctl.h>

int ioctl(int fd, int cmd, ...) ;

参数描述

fd 文件描述符

cmd 交互协议,设备驱动将根据 cmd 执行对应操作

… 可变参数 arg,依赖 cmd 指定长度以及类型

ioctl() 函数执行成功时返回 0,失败则返回 -1 并设置全局变量 errno 值,如下:

EBADF d is not a valid descriptor.

EFAULT argp references an inaccessible memory area.

EINVAL Request or argp is not valid.

ENOTTY d is not associated with a character special device.

ENOTTY The specified request does not apply to the kind of object that the descriptor d references.

因此,在用户空间使用 ioctl 时,可以做如下的出错判断以及处理:

int ret;

ret = ioctl(fd, MYCMD);

if (ret == -1) {

printf("ioctl: %s\n", strerror(errno));

}

在实际应用中,ioctl 最常见的 errorno 值为 ENOTTY(error not a typewriter),顾名思义,即第一个参数 fd 指向的不是一个字符设备,不支持 ioctl 操作,这时候应该检查前面的 open 函数是否出错或者设备路径是否正确

要记住,用户程序所作的只是通过命令码(cmd)告诉驱动程序它想做什么,至于怎么解释这些命令和怎么实现这些命令,这都是驱动程序要做的事情。

驱动程序中,对应的ioctl

struct file_operations {

struct module *owner;

int (*ioctl) (struct inode *inode, struct file *filep, unsigned int cmd, unsigned long args);

long (*unlocked_ioctl) (struct file *filep, unsigned int cmd, unsigned long args); ......

在2.6.36以后linux的内核中,只支持unlocked_ioctl(),不支持ioctl()2.6.35.7内核中,两个函数都可以使用。compat 全称 compatible(兼容的),主要目的是为 64 位系统提供 32 位 ioctl 的兼容方法。

在字符设备驱动开发中,一般情况下只要实现 unlocked_ioctl 函数即可,因为在 vfs 层的代码是直接调用 unlocked_ioctl 函数

ioctl 用户与驱动之间的协议

前文提到 ioctl 方法第二个参数 cmd 为用户与驱动的 “协议”,理论上可以为任意 int 型数据,可以为 0、1、2、3……,但是为了确保该 “协议” 的唯一性,ioctl 命令应该使用更科学严谨的方法赋值,在linux中,提供了一种 ioctl 命令的统一格式,将 32 位 int 型数据划分为四个位段,从高到低分别占据2bits、14bits、8bits、8bits,如下图所示:

在驱动程序中实现的ioctl函数体内,实际上是有一个switch{case}结构,每一个case对应一个命令码,做出一些相应的操作。怎么实现这些操作,那就具体设备具体对待了。

这个32位的数据如果由我们来自己生成也可以,就是会比较麻烦,因此,在内核中,提供了宏接口以生成上述格式的 ioctl 命令:

// include/uapi/asm-generic/ioctl.h

#define _IOC(dir,type,nr,size) \

        (((dir) << _IOC_DIRSHIFT) | \

        ((type) << _IOC_TYPESHIFT) | \

        ((nr) << _IOC_NRSHIFT) | \

        ((size) << _IOC_SIZESHIFT))

dir(direction),ioctl 命令访问模式(数据传输方向),占据 2 bit,可以为 _IOC_NONE、_IOC_READ、_IOC_WRITE、_IOC_READ | _IOC_WRITE,分别指示了四种访问模式:无数据、读数据、写数据、读写数据;

type(device type),设备类型,占据 8 bit,在一些文献中翻译为 “幻数” 或者 “魔数”,可以为任意 char 型字符,例如

‘a’、’b’、’c’ 等等,其主要作用是使 ioctl 命令有唯一的设备标识;

nr(number),命令编号/序数,占据 8 bit,可以为任意 unsigned char 型数据,取值范围 0~255,如果定义了多个 ioctl 命令,通常从 0 开始编号递增;

size,涉及到 ioctl 函数 第三个参数 arg ,占据 13bit 或者 14bit(体系相关,arm 架构一般为 14 位),指定了 arg 的数据类型及长度,如果在驱动的 ioctl 实现中不检查,通常可以忽略该参数;

通常而言,为了方便会使用宏 _IOC() 衍生的接口来直接定义 ioctl 命令:

// include/uapi/asm-generic/ioctl.h

/* used to create numbers */

#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)

#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))

#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))

#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))

_IO: 定义不带参数的 ioctl 命令

_IOW: 定义带写参数的 ioctl 命令(copy_from_user)

_IOR: 定义带读参数的ioctl命令(copy_to_user)

_IOWR: 定义带读写参数的 ioctl 命令

同时,内核还提供了反向解析 ioctl 命令的宏接口:

// include/uapi/asm-generic/ioctl.h

/* used to decode ioctl numbers */

#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)

#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)

#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)

#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)

更多实际应用待补充。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1952132.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

认证中心:基于cookie和session实现单点登陆

流程图 参数 不同域名之下&#xff08;不同父域名&#xff09; cookiesessionredis 流程追踪 用户访问系统1的受保护资源&#xff0c;系统1发现用户未登录&#xff0c;跳转至sso认证中心&#xff0c;并将自己的地址作为参数 sso认证中心发现用户未登录&#xff0c;将用户引…

Adobe Photoshop(PS) 2024软件下载(附下载链接)+PS教程

一、简介 Adobe Photoshop 2024&#xff08;简称PS2024&#xff09;是全球公认的专业图像处理和设计软件&#xff0c;它为摄影师、设计师和艺术家等创意工作者提供了强大的工具和功能。这款软件是Adobe Creative Cloud创意云桌面程序中心中的明星产品&#xff0c;凭借其出色的…

微信小程序之调查问卷

一、设计思路 1、界面 调查问卷又称调查表&#xff0c;是以问题的形式系统地记载调查内容的一种形式。微信小程序制作的调查问卷&#xff0c;可以在短时间内快速收集反馈信息。具体效果如下所示&#xff1a; 2、思路 此调查问卷采用服务器客户端的方式进行设计&#xff0c;服…

乐尚代驾八订单执行三

司机到达代驾终点&#xff0c;代驾结束了。结束代驾之后&#xff0c; – 获取额外费用&#xff08;高速费、停车费等&#xff09; – 计算订单实际里程&#xff08;实际与预估有偏差&#xff09; – 计算代驾实际费用 – 系统奖励 – 分账信息 – 生成最终账单 计算订单…

【计算机网络】RIP路由协议实验

一&#xff1a;实验目的 1&#xff1a;掌握在路由器上配置RIPv2。 二&#xff1a;实验仪器设备及软件 硬件&#xff1a;RCMS交换机、网线、内网网卡接口、Windows 2019操作系统的计算机等。具体为&#xff1a;三层交换机1台、路由器2台。 软件&#xff1a;wireshark软件、记…

Qwen2-Audio:对话式AI突破,让你“声”临其境

阿里巴巴最新推出的音频处理模型Qwen2-Audio&#xff0c;不仅能直接用语音聊天&#xff0c;还能像一位专业的听觉大师一样分析各种声音&#xff0c;功能强大得令人难以置信。 Qwen2-Audio可以通过语音聊天和音频分析两种方式与用户互动&#xff0c;用户无需区分这两种模式&…

请问C语言到底允不允许动态定义数组大小?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「c语言的资料从专业入门到高级教程」&#xff0c;点个关注在评论区回复“666”之后私信回复“666”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; 按照谭浩强的第五版C程序设…

vue实现电子签名、图片合成、及预览功能

业务功能&#xff1a;电子签名、图片合成、及预览功能 业务背景&#xff1a;需求说想要实现一个电子签名&#xff0c;然后需要提供一个预览的功能&#xff0c;可以查看签完名之后的完整效果。 需求探讨&#xff1a;后端大佬跟我说&#xff0c;文档我返回给你一个PDF的oss链接…

MySQL客户端命令一节将.sql文件导入MySQL

MySql客户端命令 直接输入SQL语句 使用MySQL客户端连接到服务器之后&#xff0c;可以发送SQL语句到服务器执行&#xff0c;并且以&#xff1b;和\g, \G作为结束不同的结束方式显示内容有所不同** TIPS: ;和\g结尾以表格的形式显示结果\G以行的形式显示结果 在连接到服务器之后…

小程序获取订阅消息状态

uni.getSetting获取用户的当前设置 uni.getSetting({success(res) {console.log(res)} })1. 当withSubscriptions&#xff1a;true的时候&#xff0c;只返回用户勾选过订阅面板中的“总是保持以上选择&#xff0c;不再询问”的订阅消息。 2.返回值中的subscriptionsSetting表示…

自动驾驶-机器人-slam-定位面经和面试知识系列05之常考公式推导(02)

这个博客系列会分为C STL-面经、常考公式推导和SLAM面经面试题等三个系列进行更新&#xff0c;基本涵盖了自己秋招历程被问过的面试内容&#xff08;除了实习和学校项目相关的具体细节&#xff09;。在知乎和牛客&#xff08;牛客上某些文章上会附上内推码&#xff09;也会同步…

【运维自动化-配置平台】模型及模型关联最小化实践

蓝鲸智云配置平台&#xff0c;以下简称配置平台 我们知道主机是配置平台最常见的管控资源对象&#xff0c;在业务拓扑里可以通过划分模块来清晰的可视化管理&#xff1b;那其他资源如何通过配置平台来纳管呢&#xff0c;比如网络设备交换机。场景需求&#xff1a;如何把交换机…

怎么培养政府机关的公文写作能力?

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频百万播放量 公文写作千万不能零起步&#xff0c;你有时间慢慢学习&#xff0c;但领导哪有时间等你慢慢进步啊。 如果问写公文有什么捷径&#xff0c;那就不得不靠「AI写作工具…

Study--Oracle-07-ASM相关参数(四)

一、ASM主要进程 1、ASM主要后台进程 ASM实例除了传统的DBWn、LGWR、CKPT、SMON和PMON等进程还包含如下几个新后台进程: 2、牛人笔记 邦德图文解读ASM架构,超详细 - 墨天轮 二、数据库实例于ASM实例之间的交互关系 数据库实例与ASM实例之间的交互关系涉及多个步骤和过程,…

联想教育电脑硬盘保护同传EDU系统使用简明教程

目录 一、原理概述 二、简明使用方法 1、软件下载 2、开机引导 3、开始安装 4、使用 &#xff08;1&#xff09;进入底层 &#xff08;2&#xff09;进行分区设置 &#xff08;3&#xff09;系统设置 &#xff08;4&#xff09;安装硬盘保护驱动 &#xff08;5&…

Android Studio导入源码

在有源码并且编译环境可用的情况下&#xff1a; 1.生成导入AS所需的配置文件 在源码的根目录执行以下命令&#xff1a; source build/ensetup.sh lunch 要编译的项目 make idegen //这一步会生成out/host/linux-x86/framework/idegen.jar development/tools/idegen/idegen.sh…

若依框架 : 生成代码

6.生成代码 6.1.配置生成设置 ruoyi-generator -> src -> main -> resources -> generator.yml 由于 案例中 表都有 前缀 为 tta_ , 这里设置去掉 6.2.生成代码 6.2.1.导入数据库中的表 6.2.2.修改设置 6.2.2.1.设置生成信息 点击 编辑 -> 生成信息 特别…

【数据结构-前缀和】力扣3152.特殊数组II

如果数组的每一对相邻元素都是两个奇偶性不同的数字&#xff0c;则该数组被认为是一个 特殊数组 。 周洋哥有一个整数数组 nums 和一个二维整数矩阵 queries&#xff0c;对于 queries[i] [fromi, toi]&#xff0c;请你帮助周洋哥检查子数组 nums[fromi…toi] 是不是一个 特殊…

19 Python常用内置函数——range()

range() 是 Python 开发中非常常用的一个内置函数。该函数返回具有惰性求值特点的 range 对象&#xff0c;其中包含左闭右开区间 [start, end) 内以 step 为步长的整数。 参数 start 默认为 0&#xff0c;step 默认为 1。 print(range(5)) print(list(range(5))) print(list(r…

科研绘图系列:R语言山脊图(Ridgeline Chart)

介绍 山脊图(Ridge Chart)是一种用于展示数据分布和比较不同类别或组之间差异的数据可视化技术。它通常用于展示多个维度或变量之间的关系,以及它们在不同组中的分布情况。山脊图的特点: 多变量展示:山脊图可以同时展示多个变量的分布情况,允许用户比较不同变量之间的关…