Unix/Linux编程:fcntl函数总结
fcntl VS ioctl
fcntl函数,也就是
file control
,提供了对文件描述符的各种操作。另一个常见的控制文件描述符的属性和行为的系统调用是ioctl
,而且ioctl
比fcntl
能够执行更多的控制。但是,对于控制文件描述符常见的属性和行为,fcntl
函数是由POSIX规范指定的首选方法
- ioctl()是底层的系统调用(system call),所以跨平台特性不好。
- 而fcntl则是被封装的函数,各个OS都是支持的。
fcntl
NAMEfcntl - 对一个打开的文件描述符执行一系列控制操作。SYNOPSIS#include <unistd.h>#include <fcntl.h>int fcntl(int fd, int cmd, ... /* arg */ );DESCRIPTIONfcntl() 对打开的文件描述符fd执行下面操作之一。操作由cmd决定fcntl() 可以接受可选的第三个参数。是否需要此参数由cmd确定。所需要的参数类型在每个cmd名称后面的括号中表示(在大多数情况下,所需的类型是int,我们使用名称arg标识参数),如果参数不是必需的,则指定void。第三个参数以省略号来表示,这意味着可以将其设置为不同的类型,或者加以省略。内核会依据 cmd 参数(如果有的话)的值来确定该参数的数据类型仅从特定的Linux内核版本开始,才支持以下某些操作。 检查主机内核是否支持特定操作的首选方法是使用所需的cmd值调用fcntl(),然后使用EINVAL测试调用是否失败,这表明内核无法识别该值。 。。。(待补)返回值: 失败返回-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)
*/
获取文件标志
fcntl()的用途之一是针对一个打开的文件,获取或者修改器访问模式和状态标志(这些值是通过指定open()调用的flag参数来设置的)。要获取这些设置,应将 fcntl()的 cmd 参数设置为F_GETFL:
if ((flags = fcntl(atoi(argv[1]), F_GETFL)) == -1){printf("fcntl error for fd %d", atoi(argv[1]));exit(EXIT_FAILURE);}
在上述代码之后,可以以如下代码测试文件是否以同步写方式打开:
if (flags & O_SYNC)printf(", synchronous writes");
SUSv3 规定:针对一个打开的文件,只有通过 open()或后续 fcntl()的 F_SETFL 操作,才能对该文件的状态标志进行设置。然而在如下方面,Linux 实现与标准有所偏离:如果一个程序编译时采用了 5.10 节所提及的打开大文件技术,那么当使用 F_GETFL 命令获取文件状态标志时,标志中将总是包含 O_LARGEFILE 标志。
判定文件的访问模式有一点复杂,这是因为 O_RDONLY(0)、O_WRONLY(1)和 O_RDWR(2)这 3 个常量并不与打开文件状态标志中的单个比特位对应。因此,要判定访问模式,需使用掩码 O_ACCMODE 与 flag 相与,将结果与 3 个常量进行比对:
int accessMode ;
accessMode = flags & O_ACCMODE;
if(accessMode == O_RDONLY){printf("read only");
}
综上所示,完整代码如下:
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>int main(int argc, char *argv[])
{int flags;if (argc != 2){printf("usage: a.out <descriptor#>");exit(EXIT_FAILURE);}// 第一个参数指定文件描述符if ((flags = fcntl(atoi(argv[1]), F_GETFL)) < 0){printf("fcntl error for fd %d", atoi(argv[1]));exit(EXIT_FAILURE);}switch (flags & O_ACCMODE) {case O_RDONLY:printf("read only");break;case O_WRONLY:printf("write only");break;case O_RDWR:printf("read write");break;default:printf("unknown access mode");exit(EXIT_SUCCESS);}if (flags & O_APPEND)printf(", append");if (flags & O_NONBLOCK)printf(", nonblocking");if (flags & O_SYNC)printf(", synchronous writes");#if !defined(_POSIX_C_SOURCE) && defined(O_FSYNC) && (O_FSYNC != O_SYNC)if (val & O_FSYNC)printf(", synchronous writes");
#endifputchar('\n');exit(0);
}
测试
[root@localhost build]$ ./apud 0 < /dev/tty
read only
[root@localhost build]$ ./apud 1 > temp.foo
[root@localhost build]$ ./apud 2 > temp.foo
[root@localhost build]$ ./apud 2 2>>temp.foo
write only, append
[root@localhost build]$ ./apud 5 5<>temp.foo #5<>temp.foo表示再文件描述符5上以读写的方式打开文件temp.foo
read write
修改文件的属性
可以使用 fcntl()的 F_SETFL 命令来修改打开文件的某些状态标志。允许更改的标志有
O_APPEND、O_NONBLOCK、O_NOATIME、O_ASYNC 和 O_DIRECT。系统将忽略对其他标志的修改操作。(有些其他的 UNIX 实现允许 fcntl()修改其他标志,如 O_SYNC。
使用fcntl()修改文件状态标志,尤其适用于如下场景
- 文件不是由调用程序打开的,所以程序也无法使用open()调用来控制文件的状态标志(比如,文件时3个标准输入输出描述符中的一员,这些描述符在程序启动之前就被打开)
- 文件描述符的获取是通过 open()之外的系统调用
- 比如 pipe()调用,该调用创建一个管道,并返回两个文件描述符分别对应管道的两端。
- 比如 socket()调用,该调用创建一个套接字并返回指向该套接字的文件描述符
为了修改打开文件的状态标志,可以使用 fcntl()的 F_GETFL 命令来获取当前标志的副本,然后修改需要变更的比特位,最后再次调用 fcntl()函数的 F_SETFL 命令来更新此状态标志。
设置文件属性
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
void set_fl(int fd, int flags) /* flags are file status flags to turn on */
{int val;if ((val = fcntl(fd, F_GETFL, 0)) < 0){perror("fcntl F_GETFL error");exit(EXIT_FAILURE);}val |= flags; /* turn on flags */if (fcntl(fd, F_SETFL, val) < 0){perror("fcntl F_GETFL error");exit(EXIT_FAILURE);}
}
清除文件属性
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
void clr_fl(int fd, int flags)
{int val;if ((val = fcntl(fd, F_GETFL, 0)) == -1) {printf("fcntl F_GETFL error\n");exit(EXIT_FAILURE);}val &= ~flags;if (fcntl(fd, F_SETFL, val) == -1) {printf("fcntl F_SETFL error\n");exit(EXIT_FAILURE);}
}
设置/清除文件描述符为非阻塞的
int setnonblocking(int fd){int old_option = fcntl(fd, F_GETFL); // 获取文件描述符旧的状态标志int new_option = old_option | O_NONBLOCK; //设置非阻塞标志fcntl(fd, F_SETFL, new_option); return old_option;
}int clrnonblocking(int fd){int old_option = fcntl(fd, F_GETFL); // 获取文件描述符旧的状态标志int new_option = old_option & ~O_NONBLOCK; //设置非阻塞标志fcntl(fd, F_SETFL, new_option); return old_option;
}
O_NONBLOCK和O_NDELAY所产生的结果都是使I/O变成非阻塞模式(non-blocking),在读取不到数据或是写入缓冲区已满会马上return,而不会阻塞等待。
它们的差别在于:在读操作时,如果读不到数据,O_NDELAY会使I/O函数马上返回0,但这又衍生出一个问题,因为读取到文件末尾(EOF)时返回的也是0,这样无法区分是哪种情况。因此,O_NONBLOCK就产生出来,它在读取不到数据时会回传-1,并且设置errno为EAGAIN。
O_NDELAY是在System V的早期版本中引入的,在编码时,还是推荐POSIX规定的O_NONBLOCK,O_NONBLOCK可以在open和fcntl时设置。
- acl_non_blocking.c
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>#define ACL_SOCKET int
#define acl_msg_error printf
/*** 设置套接口为阻塞或非阻塞* @param fd {ACL_SOCKET} SOCKET 套接字* @param on {int} 是否设置该套接字为非阻塞* @return {int} >= 0: 成功, 返回值 > 0 表示设置之前的标志位; -1: 失败*/
int acl_non_blocking(ACL_SOCKET fd, int on){int flags;int nonb = O_NONBLOCK;if ((flags = fcntl(fd, F_GETFL)) == -1) {acl_msg_error("%s(%d), %s: fcntl(%d, F_GETFL) error: %s",__FILE__, __LINE__, __FUNCTION__,fd, strerror(errno));return -1;}if (fcntl(fd, F_SETFL, on ? flags | nonb : flags & ~nonb) < 0) {acl_msg_error("%s(%d), %s: fcntl(%d, F_SETL, nonb) error: %s",__FILE__, __LINE__, __FUNCTION__,fd, strerror(errno));return -1;}return flags;
}/*** 判断套接口为阻塞或非阻塞* @param fd {ACL_SOCKET} SOCKET 套接字* @return {int} == 1, 非阻塞; 0阻塞*/
int acl_is_blocking(ACL_SOCKET fd)
{int flags;if ((flags = fcntl(fd, F_GETFL, 0)) == -1) {acl_msg_error("fcntl(fd, F_GETFL) failed");return -1;}return (flags & O_NONBLOCK) == 0 ? 1 : 0;
}
- acl_non_blocking.h
#ifndef ACL_LEARING_ACL_NON_BLOCKING_H
#define ACL_LEARING_ACL_NON_BLOCKING_H# define ACL_SOCKET int#define ACL_BLOCKING 0 /**< 阻塞读写标志位 */
#define ACL_NON_BLOCKING 1 /**< 非阻塞读写标志位 *//*** 设置套接口为阻塞或非阻塞* @param fd {ACL_SOCKET} SOCKET 套接字* @param on {int} 是否设置该套接字为非阻塞, ACL_BLOCKING 或 ACL_NON_BLOCKING* @return {int} >= 0: 成功, 返回值 > 0 表示设置之前的标志位; -1: 失败*/
int acl_non_blocking(ACL_SOCKET fd, int on);/*** 判断所给套按口是否为阻塞模式* @param fd {ACL_SOCKET} SOCKET 套接字* @return {int} -1 表示出错或所给参数有误或该平台不支持,1 表示所给套接字为阻塞模式* 0 表示所给套接字为非阻塞模式*/
int acl_is_blocking(ACL_SOCKET fd);#endif //ACL_LEARING_ACL_NON_BLOCKING_H
总结
fcntl函数支持的常用操作以其参数如表所示:
SIGIO
和SIGURG
这两个信号与其他Linux信号不同,它们必须和某个文件描述符相关联方可使用:
- 当被关联的文件描述符可读或者可写时,系统将触发
SIGIO
信号 - 当被关联的文件描述符(必须是一个socket)上有带外数据可读时,系统将触发
SIGURG
信号
将信号和文件描述符关联的方法,就是使用fcntl
函数为目标文件描述符指定宿主进程或者进程组,那么被指定的宿主进程或者进程组将会捕获这两个信号
使用SIGIO
时,还需要利用fcntl
设置其O_ASYNC
标志(异步IO标志)
F_GETFL
:
- 返回fd对应的文件状态标志
Unix/Linux编程:fcntl函数总结相关推荐
- UNIX网络编程——fcntl函数
fcntl函数提供了与网络编程相关的如下特性: 非阻塞式I/O. 通过使用F_SETFL命令设置O_NONBLOCK文件状态标志,我们可以把一个套接字设置为非阻塞型. 信号驱动式I/O. 通过使用F ...
- Unix/Linux编程:进程间通信(IPC)总结
IPC工具分类 如上,Unix系统上IPC根据功能可以分为三类 通信:这些工具关注进程间的数据交换 同步:这些进程关注进程和线程操作之间的同步 信号:虽然信号的主要作用不为此,但是在特定场景下仍然可以 ...
- 学习Unix/Linux编程要学些什么
最近利用空余时间看了一下<Unix/Linux编程实践教程>,原书名为:Understanding Unix/Linux Programming: A Guide to Theory an ...
- 《Unix/linux编程实践教程》------重定向程序的I/O
<Unix/linux编程实践教程>书中举例命令more的用法: $more filename $command | more $more < filename 用法1直接显示fil ...
- Linux 文件锁 fcntl 函数详解
Linux 文件锁 fcntl 函数详解 #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd); ...
- UNIX网络编程-listen函数及其包裹函数介绍
UNIX网络编程-listen函数及其包裹函数介绍 函数简介 #include<sys/socket.h>int listen(int sockfd,int backlog);返回:若成功 ...
- Unix/Linux编程实践教程–书评
花了两个月的时间把这本书读完了,完成了一部分的课后习题. 总的来说,这是一本挺好的Unix\Linux编程的入门书(虽然书中的小错误一大堆),书的开始部分简要介绍了Unix系统编程,讲述了如何使用男人 ...
- stty详解-Unix/Linux编程实践教程第五章 学习stty
读书笔记-第五章 连接控制 学习stty 先放上思维导图 为设备编程&设备就像文件 前面所学的知识都是基于文件的,而在unix/linux系统中,所有的设备(打印机,终端,话筒,摄像头等等)也 ...
- Unix/Linux编程:socketpair
理论 管道内部传输的是字节流,TCP socket传输的也是字节流,区别在于: 应用层程序能够往一个TCP连接中写入多少字节的数据,取决于对方的接收通告窗口的大小和本端的拥塞窗口的大小. 管道本身拥有 ...
最新文章
- 19年8月 字母哥 第五章 静态资源与模板引擎的整合 用热点公司网不行
- 域名自动跳转不搭建服务器,宝塔搭建的服务器WEB系统环境如果做域名301跳转
- SonarQube开机自启动
- 【ubuntu】给新装好的UBUNTU系统配置静态IP
- python工资高还是java-未来Java、大数据、Python哪个前景更好,薪资更高?
- 关于 jspx 文件的一些说明
- DHCP自动分配IP地址
- 英语流利说 level4 unti2 part1-extinction events 1
- Delphi 人民币大小写转换
- 【回忆杀】2012年拥有第一台电脑【致逝去的青春】
- 51实现todos-完整js
- Android Provision (OOBE)
- java身份证工具类(校验身份证是否合法、通过身份证获取年龄、性别、生日,将15位身份证转为18位等)
- 韩国的计算机sci,JOURNAL OF KOREAN MEDICAL SCIENCE《韩国医学科学杂志》SCI论文投稿_SCI期刊大全_SCI期刊点评_万维书刊网...
- iCheck组件使用方法总结
- C#版 - 小红书后台开发面试题: 二维数组中的查找
- Sqoop1和Sqoop2的刨析对比
- 如何在ppt中生成柱状图_在ppt中做柱状图的方法图解步骤
- POJ 2987 Firing
- windows 系统的copy命令