五一3天小长假,在家里悠闲度过了。基本没学习,昨天下午回到深圳,研究了一下fcntl()系统调用。
1. 函数介绍
函数fcntl()用于修改某个文件描述符的属性,函数原型为:
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
第1个参数fd为待修改属性的文件描述符,第2个参数cmd为对应的操作命令,第3个参数为cmd的参数。
常见的cmd命令有:
F_DUPFD //复制文件描述符,跟dup()函数功能一样
F_GETFD //获取文件描述符标志
F_SETFD //设置文件描述符标志
F_GETFL //获取文件状态
F_SETFL //设置文件状态
函数执行失败,将返回-1并设置errno全局变量来指明错误。
不同的cmd其返回值不同: 若是设置文件属性,成功返回0失败返回-1;
若是读取文件属性,成功返回该属性值,失败返回-1。
2. 函数使用
2.1 F_DUPFD赋值文件描述符
fcntl()函数中cmd若使用F_DUPFD命令则是复制文件描述符,返回的值是新的文件描述符,该文件描述符同样指向原文件,具有特点有:
1) 大于或者等于指定的最小可用的文件描述符。”指定”是在函数的第三个参数指定的。
2) 和原文件相同方式的打开文件(包括管道)
3) 两个文件描述符共享一个文件指针
4) 两个文件描述符共享同一文件状态标志
5) 新的文件描述符关联的close-on-exec标志在各execX()函数簇中还是保持打开状态
int main(void)
{
const char* ptr = "Hello, funtl()\n";
int fd, new_fd;
fd = open("tmp.file", O_WRONLY | O_CREAT, 0666);
if (fd < 0)
{
perror("open");
exit(EXIT_FAILURE);
}
//赋值文件描述符,参数3设置为0,那么new_fd为3
new_fd = fcntl(fd, F_DUPFD, 0);
printf("fd = %d, new_fd = %d\n", fd, new_fd);
printf("write %d bytes to fd\n", write(fd, ptr, strlen(ptr)));
printf("write %d bytes to new_fd\n", write(new_fd, ptr, strlen(ptr)));
close(fd);
close(new_fd);
system("cat tmp.file");
return 0;
}
运行结果:
2.2 F_GETFD / F_SETFD操作close-on-exec
首先了解close-on-exec是什么?每个文件描述符都有一个close-on-exec标志,这个标志的具体作用是体现在其它进程调用execX()函数簇时,是否关闭在这之前的文件描述符。F_GETFD命令获取文件描述符关联的close-on-exec标志。如果close-on-exec低序位为0,文件将在execX()函数执行期间保持打开状态,否则文件在执行execX()函数期间是关闭的。默认情况下是打开的,则允许在execX()函数簇代码中访问原来打开的文件描述符。
F_SETFL命令用于设置与文件描述符相关的close-on-exec标志,设置的值为fcntl的第3个参数。
若设置为1,即FD_CLOEXEC,
/* for F_[GET|SET]FL */
#define FD_CLOEXEC 1 /* actually anything with low bit set goes */
那么子进程的execX()函数簇中不可以使用该文件描述符,代码示例在http://blog.csdn.net/qq_29344757/article/details/70316536#t13讲过。
2.3 F_GETFL / F_SETFD设置文件描述符状态
F_GETFL命令用于获取文件状态标志,F_SETFL将文件状态标志设置为funtl()的第3个参数。也就是说,它不能设置读写属性(O_RDWR、O_WRONLY、O_RDONLY),设置文件状态属性,如阻塞/非阻塞、追加(O_APPEND)。
/*
实验证明:
F_SETFD是设置文件的close-on-exec标志
F_SETFD是设置文件状态属性,如阻塞/非阻塞、追加(O_APPEND)
*/
int pre_test(int flag)
{
int accmode;
accmode = flag & O_ACCMODE; //获取文件的读写属性需要这么操作
if (accmode == O_RDONLY)
printf("read only");
else if (accmode == O_WRONLY)
printf("write only");
else if (accmode == O_RDWR)
printf("read write");
else
printf("unknown mode");
if (flag & O_APPEND)
printf(", append");
if (flag & O_NONBLOCK)
printf(", nonblocking");
printf("\n");
return 0;
}
int main(int argc, char** argv)
{
int flag;
int fd;
fd = open("./tmp.file", O_RDONLY);
flag = fcntl(fd, F_GETFL);
if (flag < 0)
{
perror("fcntl");
exit(EXIT_FAILURE);
}
pre_test(flag);
fcntl(fd, F_SETFL, flag | O_NONBLOCK | O_APPEND);
flag = fcntl(fd, F_GETFL);
pre_test(flag);
return 0;
}
运行结果:
F_SETFD和F_SETFL存在区别:
F_SETFD仅更改该文件描述符的信息,而F_SETFL则是更改与该文件相关的所有描述符。比如复制了某个文件描述符后,若使用F_SETFD修改close-on-exec标志,那么只是修改该文件描述符信息,若使用F_SETFL修改将修改原文件和拷贝文件描述符的状态信息。
水平有限,上述结论可能还有误,先总结这么多吧,