linux系统下fcntl函数解析

        fcntl()函数可以对一个已经打开的文件描述符执行一系列控制操作,譬如复制一个文件描述符(与 dup 、dup2 作用相同)、获取 / 设置文件描述符标志、获取 / 设置文件状态标志等,类似于一个多功能文件描述符管理工具箱。fcntl() 函数原型如下所示(可通过 "man 2 fcntl" 命令查看):
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ )
        函数参数和返回值含义如下:
        fd 文件描述符。
        cmd 操作命令。此参数表示我们将要对 fd 进行什么操作, cmd 参数支持很多操作命令,大家可以打开 man 手册查看到这些操作命令的详细介绍,这些命令都是以 F_XXX 开头的,譬如 F_DUPFD F_GETFD 、F_SETFD 等,不同的 cmd 具有不同的作用, cmd 操作命令大致可以分为以下 5 种功能:
        ⚫ 复制文件描述符(cmd=F_DUPFD 或 cmd=F_DUPFD_CLOEXEC );
        ⚫ 获取/设置文件描述符标志(cmd=F_GETFD cmd=F_SETFD );
        ⚫ 获取/设置文件状态标志( cmd=F_GETFL cmd=F_SETFL );
        ⚫ 获取/设置异步 IO 所有权( cmd=F_GETOWN cmd=F_SETOWN );
        ⚫ 获取/设置记录锁( cmd=F_GETLK cmd=F_SETLK );
        这里列举出来,并不需要全部学会每一个 cmd 的作用,因为有些内容并没有给大家提及到,譬如什么 异步 IO 、锁之类的概念,在后面的学习过程中,当学习到相关知识内容的时候再给大家介绍。
         fcntl 函数是一个可变参函数,第三个参数需要根据不同的 cmd 来传入对应的实参,配合 cmd 来使用。
        返回值:执行失败情况下,返回 -1 ,并且会设置 errno ;执行成功的情况下,其返回值与 cmd (操作命令)有关,譬如 cmd=F_DUPFD (复制文件描述符)将返回一个新的文件描述符、 cmd=F_GETFD (获取文件描述符标志)将返回文件描述符标志、cmd=F_GETFL (获取文件状态标志)将返回文件状态标志等。
fcntl 使用示例
(1)复制文件描述符
        前面给大家介绍了 dup dup2 ,用于复制文件描述符,除此之外,我们还可以通过 fcntl 函数复制文件描述符, 可用的 cmd 包 括 F_DUPFD F_DUPFD_CLOEXEC , 这 里 就 只 介 绍 F_DUPFD ,F_DUPFD_CLOEXEC 暂时先不讲。
        当 cmd=F_DUPFD 时,它的作用会根据 fd 复制出一个新的文件描述符,此时需要传入第三个参数,第 三个参数用于指出新复制出的文件描述符是一个大于或等于该参数的可用文件描述符(没有使用的文件描述符);如果第三个参数等于一个已经存在的文件描述符,则取一个大于该参数的可用文件描述符。
测试:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
 int fd1, fd2;
 int ret;
 /* 打开文件 test_file */
 fd1 = open("./test_file", O_RDONLY);
 if (-1 == fd1) {
     perror("open error");
     exit(-1);
 }
 /* 使用 fcntl 函数复制一个文件描述符 */
 fd2 = fcntl(fd1, F_DUPFD, 0);
 if (-1 == fd2) {
     perror("fcntl error");
     ret = -1;
     goto err;
 }
 printf("fd1: %d\nfd2: %d\n", fd1, fd2);
 ret = 0;
 close(fd2);
err:
 /* 关闭文件 */
 close(fd1);
 exit(ret);
}
}
        在当前目录下存在 test_file 文件,上述代码会打开此文件,得到文件描述符 fd1 ,之后再使用 fcntl 函数复制 fd1 得到新的文件描述符 fd2 ,并将 fd1 fd2 打印出来,接下来编译运行:
        可知复制得到的文件描述符是 4 ,因为在执行 fcntl 函数时,传入的第三个参数是 0 ,也就时指定复制得到的新文件描述符必须要大于或等于 0 ,但是因为 0~3  都已经被占用了,所以分配得到的 fd 就是 4 ;如果传入的第三个参数是 100 ,那么 fd2 就会等于 100 ,大家可以自己动手测试。
(2)获取/设置文件状态标志
        cmd=F_GETFL 可用于获取文件状态标志, cmd=F_SETFL 可用于设置文件状态标志。 cmd=F_GETFL 时不需要传入第三个参数,返回值成功表示获取到的文件状态标志;cmd=F_SETFL 时,需要传入第三个参数,此参数表示需要设置的文件状态标志。这些标志指的就是我们在调用 open 函数时传入的 flags 标志,可以指定一个或多个(通过位或 | 运算符组合),但是文件权限标志(O_RDONLY、O_WRONLY、O_RDWR)以及文件创建标志(O_CREAT、
O_EXCL O_NOCTTY O_TRUNC )不能被设置、会被忽略;在 Linux 系统中,只有 O_APPEND O_ASYNC、O_DIRECT、O_NOATIME 以及 O_NONBLOCK 这些标志可以被修改,这里面有些标志并没有给大家介绍过,后面我们在用到的时候再给大家介绍。所以对于一个已经打开的文件描述符,可以通过这种方式添加或移除标志。
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MSG_TRY "try again\n"

int main(void)
{
	char buf[10];
	int flags, n;

	flags = fcntl(STDIN_FILENO, F_GETFL); //获取stdin属性信息
	if(flags == -1){
		perror("fcntl error");
		exit(1);
	}
	flags |= O_NONBLOCK;
	int ret = fcntl(STDIN_FILENO, F_SETFL, flags);
	if(ret == -1){
		perror("fcntl error");
		exit(1);
	}

tryagain:
	n = read(STDIN_FILENO, buf, 10);
	if(n < 0){
		if(errno != EAGAIN){		
			perror("read /dev/tty");
			exit(1);
		}
		sleep(3);
		write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
		goto tryagain;
	}
	write(STDOUT_FILENO, buf, n);

	return 0;
}

 

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值