fcntl VS ioctl

fcntl函数,也就是file control,提供了对文件描述符的各种操作。另一个常见的控制文件描述符的属性和行为的系统调用是ioctl,而且ioctlfcntl能够执行更多的控制。但是,对于控制文件描述符常见的属性和行为,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时设置。

  1. 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;
}
  1. 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函数支持的常用操作以其参数如表所示:


SIGIOSIGURG这两个信号与其他Linux信号不同,它们必须和某个文件描述符相关联方可使用:

  • 当被关联的文件描述符可读或者可写时,系统将触发SIGIO信号
  • 当被关联的文件描述符(必须是一个socket)上有带外数据可读时,系统将触发SIGURG信号

将信号和文件描述符关联的方法,就是使用fcntl函数为目标文件描述符指定宿主进程或者进程组,那么被指定的宿主进程或者进程组将会捕获这两个信号

使用SIGIO时,还需要利用fcntl设置其O_ASYNC标志(异步IO标志)

F_GETFL:

  • 返回fd对应的文件状态标志

Unix/Linux编程:fcntl函数总结相关推荐

  1. UNIX网络编程——fcntl函数

    fcntl函数提供了与网络编程相关的如下特性: 非阻塞式I/O.  通过使用F_SETFL命令设置O_NONBLOCK文件状态标志,我们可以把一个套接字设置为非阻塞型. 信号驱动式I/O. 通过使用F ...

  2. Unix/Linux编程:进程间通信(IPC)总结

    IPC工具分类 如上,Unix系统上IPC根据功能可以分为三类 通信:这些工具关注进程间的数据交换 同步:这些进程关注进程和线程操作之间的同步 信号:虽然信号的主要作用不为此,但是在特定场景下仍然可以 ...

  3. 学习Unix/Linux编程要学些什么

    最近利用空余时间看了一下<Unix/Linux编程实践教程>,原书名为:Understanding Unix/Linux Programming: A Guide to Theory an ...

  4. 《Unix/linux编程实践教程》------重定向程序的I/O

    <Unix/linux编程实践教程>书中举例命令more的用法: $more filename $command | more $more < filename 用法1直接显示fil ...

  5. Linux 文件锁 fcntl 函数详解

    Linux 文件锁 fcntl 函数详解 #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd); ...

  6. UNIX网络编程-listen函数及其包裹函数介绍

    UNIX网络编程-listen函数及其包裹函数介绍 函数简介 #include<sys/socket.h>int listen(int sockfd,int backlog);返回:若成功 ...

  7. Unix/Linux编程实践教程–书评

    花了两个月的时间把这本书读完了,完成了一部分的课后习题. 总的来说,这是一本挺好的Unix\Linux编程的入门书(虽然书中的小错误一大堆),书的开始部分简要介绍了Unix系统编程,讲述了如何使用男人 ...

  8. stty详解-Unix/Linux编程实践教程第五章 学习stty

    读书笔记-第五章 连接控制 学习stty 先放上思维导图 为设备编程&设备就像文件 前面所学的知识都是基于文件的,而在unix/linux系统中,所有的设备(打印机,终端,话筒,摄像头等等)也 ...

  9. Unix/Linux编程:socketpair

    理论 管道内部传输的是字节流,TCP socket传输的也是字节流,区别在于: 应用层程序能够往一个TCP连接中写入多少字节的数据,取决于对方的接收通告窗口的大小和本端的拥塞窗口的大小. 管道本身拥有 ...

最新文章

  1. 19年8月 字母哥 第五章 静态资源与模板引擎的整合 用热点公司网不行
  2. 域名自动跳转不搭建服务器,宝塔搭建的服务器WEB系统环境如果做域名301跳转
  3. SonarQube开机自启动
  4. 【ubuntu】给新装好的UBUNTU系统配置静态IP
  5. python工资高还是java-未来Java、大数据、Python哪个前景更好,薪资更高?
  6. 关于 jspx 文件的一些说明
  7. DHCP自动分配IP地址
  8. 英语流利说 level4 unti2 part1-extinction events 1
  9. Delphi 人民币大小写转换
  10. 【回忆杀】2012年拥有第一台电脑【致逝去的青春】
  11. 51实现todos-完整js
  12. Android Provision (OOBE)
  13. java身份证工具类(校验身份证是否合法、通过身份证获取年龄、性别、生日,将15位身份证转为18位等)
  14. 韩国的计算机sci,JOURNAL OF KOREAN MEDICAL SCIENCE《韩国医学科学杂志》SCI论文投稿_SCI期刊大全_SCI期刊点评_万维书刊网...
  15. iCheck组件使用方法总结
  16. C#版 - 小红书后台开发面试题: 二维数组中的查找
  17. Sqoop1和Sqoop2的刨析对比
  18. 如何在ppt中生成柱状图_在ppt中做柱状图的方法图解步骤
  19. POJ 2987 Firing
  20. windows 系统的copy命令

热门文章

  1. python文件的读取
  2. 太好玩了!手机一插上充电,“微信钱包余额”一直上涨!
  3. 计算机协会维修部面试,协会社团面试自我介绍
  4. led会标软件下载_如何将自定义字符添加到Apple Watch的会标中
  5. 高性能猛兽 网御星云发布320G安全网关
  6. 周总结20200413-20200426
  7. 220V左右的得交流电有效值测量
  8. 如何使用Opencv调用电脑摄像头?
  9. springboot日志可视化_使用 SpringBoot Admin 监控你的 SpringBoot 程序
  10. [世界杯]世界杯不仅仅只是足球