Linux fcntl 函数详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/rikeyone/article/details/88828154

接口

#include <fcntl.h>

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

返回值:
正确返回值根据命令码而定,错误返回-1。

fcntl是用来修改已经打开文件的属性的函数,包含5个功能:

  1. 复制一个已有文件描述符,功能和dup和dup2相同,对应的cmd:F_DUPFD、F_DUPFD_CLOEXEC。
    当使用这两个cmd时,需要传入第三个参数,fcntl返回复制后的文件描述符,此返回值是之前未被占用的描述符,并且必须一个大于等于第三个参数值。F_DUPFD命令要求返回的文件描述符会清除对应的FD_CLOEXEC标志;F_DUPFD_CLOEXEC要求设置新描述符的FD_CLOEXEC标志。

  2. 获取、设置文件描述符标志,对应的cmd:F_GETFD、F_SETFD。
    用于设置FD_CLOEXEC标志,此标志的含义是:当进程执行exec系统调用后此文件描述符会被自动关闭。

  3. 获取、设置文件访问状态标志,对应的cmd:F_GETFL、F_SETFL。
    获取当前打开文件的访问标志,设置对应的访问标志,一般常用来设置做非阻塞读写操作。

  4. 获取、设置记录锁功能,对应的cmd:F_GETLK、F_SETLK、F_SETLKW。
    作为记录锁功能使用,在我的另一篇博客中有介绍《POSIX记录锁》[https://blog.csdn.net/rikeyone/article/details/88743394]

  5. 获取、设置异步I/O所有权,对应的cmd:F_GETOWN、F_SETOWN。
    获取和设置用来接收SIGIO/SIGURG信号的进程id或者进程组id。返回对应的进程id或者进程组id取负值。

示例

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

/*
 * fcntl function has these cmd arguments:
 * F_DUPFD、F_DUPFD_CLOEXEC : dup file descriptor
 * F_GETFD、F_SETFD : set file FD_CLOEXEC flag
 * F_GETFL、F_SETFL: file open flags
 * F_GETLK、F_SETLK、F_SETLKW : file lock
 */
#define pr_debug(fmt,...)   do{ printf("[%ld] DEBUG: "fmt,(long)getpid(),##__VA_ARGS__); fflush(stdout); }while(0)
#define pr_info(fmt,...)    do{ printf("[%ld] INFO:  "fmt,(long)getpid(),##__VA_ARGS__); fflush(stdout); }while(0)
#define pr_err(fmt,...)     do{ printf("[%ld] ERROR: "fmt,(long)getpid(),##__VA_ARGS__);fflush(stdout); }while(0)
#define err_exit(fmt,...)   do{ printf("[%ld] ERROR: "fmt,(long)getpid(),##__VA_ARGS__); exit(1); }while(0)

int main(int argc, char *argv[])
{
	int fd, ret, access;
	pid_t pid;
	char buffer[100] = {0};

	pr_info("argc:%d\n", argc);

	if (argc == 2)
		sscanf(argv[1], "%d", &fd);
	else
		fd = open("testfile", (O_RDWR|O_NONBLOCK|O_APPEND|O_SYNC|O_CREAT), (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH));

	if (fd < 0)
		err_exit("Open failed, errno = %d, %s\n", errno, strerror(errno));

	pr_info("file descriptor = %d\n", fd);

	ret = fcntl(fd, F_GETFL, 0);
	if (ret < 0)
		err_exit("fcntl failed, errno = %d, %s\n", errno, strerror(errno));

	access = ret & O_ACCMODE;
	switch (access) {
	case O_RDWR:
		pr_info("read write mode\n");
		break;
	case O_RDONLY:
		pr_info("read only mode\n");
		break;
	case O_WRONLY:
		pr_info("write only mode\n");
		break;
	default:
		pr_err("unknown access mode\n");
		break;
	}
	if (ret & O_NONBLOCK)
		pr_info("non block access mode enable\n");
	if (ret & O_APPEND)
		pr_info("append mode enable\n");
	if (ret & O_SYNC)
		pr_info("sync writes enable\n");
	if ((ret = fcntl(fd, F_GETFD, 0)) < 0)
		err_exit("Get CLOSEXEC flag failed\n");
	else
		pr_info("close on exec flag:%d\n", ret);

	if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0)
		err_exit("Set CLOSEXEC flag failed\n");

	if ((ret = fcntl(fd, F_GETFD, 0)) < 0)
		err_exit("Get CLOSEXEC flag failed\n");
	else
		pr_info("close on exec flag:%d\n", ret);

	if (argc == 2)
		exit(0);

	pid = fork();
	if (pid < 0)
		err_exit("fork failed\n");
	else if (pid > 0) {
		pr_info("parent process: fd = %d\n", fd);
		wait(NULL);
	} else {
		sprintf(buffer, "%d", fd);
		pr_info("child process: fd = %d\n", fd);
		execlp("./fcntl", "./fcntl", buffer, (char *)0);
	}
	exit(0);
}

在我的测试机ubuntu 14.04 上运行结果如下显示:

$ ./fcntl 
[13591] INFO:  argc:1
[13591] INFO:  file descriptor = 3
[13591] INFO:  read write mode
[13591] INFO:  non block access mode enable
[13591] INFO:  append mode enable
[13591] INFO:  sync writes enable
[13591] INFO:  close on exec flag:0
[13591] INFO:  close on exec flag:1
[13591] INFO:  parent process: fd = 3
[13592] INFO:  child process: fd = 3
[13592] INFO:  argc:2
[13592] INFO:  file descriptor = 3
[13592] ERROR: fcntl failed, errno = 9, Bad file descriptor

这里顺便测试了 FD_CLOEXEC 的功能,当父进程设置了对应的标志后,子进程再去访问对应的fd会报错 ERROR: fcntl failed, errno = 9, Bad file descriptor

猜你喜欢

转载自blog.csdn.net/rikeyone/article/details/88828154