fcntl函数可以改变已打开文件的性质。函数原型如下:
#include <fcntl.h>
int fcntl(int filedes, int cmd, ...);
当第二个参数cmd=F_GETFL时,它的作用是取得文件描述符filedes的文件状态标志。
当第二个参数cmd=F_SETFL时,它的作用是设置文件描述符filedes的文件状态标志,这时第三个参数为新的状态标志。
返回值:若成功则返回文件状态标志,若出错则返回-1。
函数返回一个整数,该整数低2位表示读写状态,其他位每个位代表一个状态。
fcntl函数可获取或者设置的状态标志列表如下:
文件状态标志 | 标志值(八进制) | 可否通过F_GETFL获取 | 可否通过F_SETFL设置 | 说明 |
O_RDONLY | 00 | 是 | 否 | 只读打开 |
O_WRONLY | 01 | 是 | 否 | 只写打开 |
O_RDWR | 02 | 是 | 否 | 为读、写打开 |
O_APPEND | 02000 | 是 | 是 | 每次读写在尾部追加 |
O_NONBLOCK | 04000 | 是 | 是 | 非阻塞模式 |
O_SYNC | 04010000 | 是 | 是 | 等待写完成(数据和属性) |
O_DSYNC | 010000 | 是 | 是 | 等待写完成(仅数据) |
O_RSYNC | 04010000 | 是 | 是 | 同步读、写 |
O_FSYNC | 04010000 | 是 | 是 | 等待完成 |
O_ASYNC | 020000 | 是 | 是 | 异步I/O |
从上表可以看出:
1、文件读写状态并非一个位代表一个状态,而是一个互斥值。所以无法通过返回值与状态字的与运算来判断读写状态。要从返回状态中提取读写状态值,首先要用屏蔽字(O_ACCMODE)取得读写状态位,然后再分别与三个读写状态字进行比较。
2、文件读写状态不能通过fcntl的F_SETFL来设置。
下面实例中函数set_fl用来设置文件状态标志,函数clr_fl用来清除文件状态标志。两个函数中的第二个参数flags可以是上表中个状态字或其组合,但对于文件读写状态(O_RDONLY、O_WRONLY和O_RDWR)无效,也不适用。
实例用O_APPEND状态字来测试两个函数(set_fl和clr_fl)的效果。
实例 x.3.14.3.c
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define BUFFSIZE 256
void set_fl(int fd, int flags);
void clr_fl(int fd, int flags);
int main()
{
char filepath[] = "/tmp/myfile"; /*待操作文件路径*/
int f_id; /*文件描述符*/
ssize_t nrdwr; /*实际读写字节书*/
size_t nbytes; /*要读写的字节书*/
off_t offset; /*文件指针偏移量*/
char wbuf[BUFFSIZE] = "0123456789abcdefghij"; /*待写入数据*/
/*打开文件,获取文件描述符*/
f_id = open(filepath, O_RDWR | O_CREAT);
if (f_id == -1) {
printf("open error for %s\n", filepath);
exit(1);
}
/*把文件指针移动到文件开始处*/
offset = lseek(f_id, 0, SEEK_SET);
if (offset == -1) {
printf("lseek error\n");
exit(1);
}
/*写入10字节数据[0-9]*/
nbytes = 10;
nrdwr = write(f_id, wbuf, nbytes);
if (nrdwr == -1) {
printf("write error\n", f_id);
exit(1);
}
/*设置为每次写时追加到尾部*/
/* set_fl(f_id, O_APPEND);*/
/*清除追加到尾部状态标志*/
/* clr_fl(f_id, O_APPEND);*/
/*再把文件指针移回文件开始处*/
offset = lseek(f_id, 0, SEEK_SET);
if (offset == -1) {
printf("lseek error\n");
exit(1);
}
/*再写入10字节数据[a-j]*/
nbytes = 10;
nrdwr = write(f_id, (wbuf + 10), nbytes);
if (nrdwr == -1) {
printf("write error\n", f_id);
exit(1);
}
/*关闭文件描述符*/
close(f_id);
exit(0);
}
/*设置文件状态标志*/
void set_fl(int fd, int flags)
{
int val;
/*取原状态(包含着其他方面的状态)*/
if ((val = fcntl(fd, F_GETFL, 0)) < 0) {
printf("fcntl error for F_GETFL");
exit(1);
}
/*置位指定状态位flags*/
val |= flags;
/*设置新状态*/
if (fcntl(fd, F_SETFL, val) < 0) {
printf("fcntl error for F_SETFL");
exit(1);
}
}
/*清除文件状态标志*/
void clr_fl(int fd, int flags)
{
int val;
/*取原状态(包含着其他方面的状态)*/
if ((val = fcntl(fd, F_GETFL, 0)) < 0) {
printf("fcntl error for F_GETFL");
exit(1);
}
/*清除指定状态位flags*/
val &= ~flags;
/*设置新状态*/
if (fcntl(fd, F_SETFL, val) < 0) {
printf("fcntl error for F_SETFL");
exit(1);
}
}
编译与执行:
第一步:上诉代码直接编译
[root@localhost unixc]# rm -f /tmp/myfile
[root@localhost unixc]# cc x.3.14.3.c
[root@localhost unixc]# ./a.out
[root@localhost unixc]# cat /tmp/myfile
abcdefghij[root@localhost unixc]#
结果分析:第二次写入的内容把第一次写入的内容给覆盖掉了。
第二步:将上面代码中的44行“/* set_fl(f_id, O_APPEND);*/”注释符去掉激活该行代码,然后继续编译测试
[root@localhost unixc]# rm -f /tmp/myfile
[root@localhost unixc]# cc x.3.14.3.c
[root@localhost unixc]# ./a.out
[root@localhost unixc]# cat /tmp/myfile
0123456789abcdefghij[root@localhost unixc]#
分析:O_APPEND状态位被设置,第二次写入的东西[abcdefghij]不覆盖前面的[0123456789]。
第三步:将上面代码中的44行“/* set_fl(f_id, O_APPEND);*/”和47行“/* clr_fl(f_id, O_APPEND);*/”两行代码中的注释符去掉激活代码,然后继续编译测试
[root@localhost unixc]# rm -f /tmp/myfile
[root@localhost unixc]# cc x.3.14.3.c
[root@localhost unixc]# ./a.out
[root@localhost unixc]# cat /tmp/myfile
abcdefghij[root@localhost unixc]#
结果分析:设置状态后紧接着又清除了,等价于第一步。