写这篇文正主要是为了介绍下fcntl,并将我自己在学习过程中的一些理解写下来,不一定那么官方,也有错误,希望指正,共同进步~
fcntl:
一个修改一打开文件的性质的函数。基本的格式是 int fcntl(int filedes, int cmd, ...) 包含在头文件<fcntl.h>中。 参数三是看cmd这个参数的设置。函数返回一个文件描述符。fcntl有五种功能。下面介绍其中的三种功能
1、复制一个现有的描述符(cmd = F_DUPFD)
2、获得/设置文件描述符标记(cmd = F_GETFD或是cmd = F_SETFD)
3、获得/设置文件状态标志(cmd = F_GETFL或是cmd = F_SETFL)
F_DUPFD:
简单的说下把,在我的上一篇博客里面有提到过,复制文件描述符filedes,新的文件描述符作为返回值返回。这个返回值是尚未打开的描述符中大于或是等于你所设置的那个第三个参数的。
例如: newfd = fcntl(fd, F_DUPFD,fd1),就是将fd复制给描述符的值为fd1的上。和dup2一样的。这边是对dup过程的一个解释把。
F_GETFD和F_SETFD:
这两个也是比较简单的,F_GETFD将 对应于filedes的文件描述符标志作为函数值返回。很好理解的,就是返回一个文件描述符。相当于查看下,不需要第三个参数
F_SETFD 将filedes所对应的文件描述符,按照第三个参数的值设定。
文件描述符标志和文件状态标志:
这两个容易混淆。文件描述符标志可以看作是文件描述符里面的一项,文件描述符包含两个,一个就是fd标志,还有一个就是文件指针。 文件指针指向文件状态标志,文件状态标志里面保存了文件的读、写、添写、以及同步和非阻塞等。
所以着两个是不同的,文件状态标志更加复杂一点。下面这个图可以帮助我们理解(来自Unix环境高级编程)
这样看上去是不是感觉没有那么混淆了?很清晰的就解释了二者的区别。
F_GETFL:
对应的filedes的文件状态标志作为函数值返回。里面的参数包含open函数里面的参数 如:O_RDWR O_RDONLY O_WRONLY O_APPEND O_NONBLOCK O_SYNC 等等,值得注意的是 在open里面有的参数如(O_TRUNC O_EXCL O_CREAT等,这些是不在这里面的。)
有一个比较很不好的是,O_RDONLY O_WRONLY O_RDWR这三个不可以同时存在的,所以他们的三个共同占据一位,所以要想能够把这个状态更get出来,我们要和O_ACCMODE这个屏蔽字操作下,才能获得对已的状态信息。其他的各占一位。
F_SETFL:
这边就要用到了fcntl的第三个参数了,将文件的状态标志设置为第三个参数的值。可以更改的有O_APPEND ,O_SYNC ,O_NONBLOCK, O_DSYNC, O_RSYNC, O_FSYNC 和O_ASYNC.这些都是可以设置上去的。
上面的是一些基础的知识,只是用作理解fcntl的一个基础。下面我们可以看两个例子。一个是打印出一个文件的一些状态标志。一个是设置一个或者多个文件状态标志的。
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<fcntl.h> 5 #include<unistd.h> 6 #include<errno.h> 7 8 int main() 9 { 10 int val; 11 int fd; 12 13 fd = open("a.txt",O_RDWR|O_APPEND); 14 if(fd < 0) 15 { 16 fprintf(stderr,"open:%s\n",strerror(errno)); 17 exit(0); 18 } 19 20 if((val = fcntl(fd,F_GETFL,0)) < 0) 21 { 22 fprintf(stderr,"fcntl:%s\n",strerror(errno)); 23 exit(0); 24 } 25 //判断文件读写的信息 26 switch(val & O_ACCMODE) 27 { 28 case O_RDONLY: 29 printf("Read Only"); 30 break; 31 case O_WRONLY: 32 printf("Write Only"); 33 break; 34 case O_RDWR: 35 printf("Read & Write"); 36 break; 37 default: 38 printf("Can not identify the mode!"); 39 } 40 //添写状态信息 41 if(val & O_APPEND) 42 { 43 printf(",append"); 44 } 45 //非阻塞模式 46 if(val & O_NONBLOCK) 47 { 48 printf(", nonblocking"); 49 } 50 //条件编译,看有没有同步写操作 51 #if defined(O_SYNC) 52 if(val & O_SYNC) 53 printf(",synchronous writes"); 54 #endif 55 56 printf("\n"); 57 close(fd); 58 return 0; 59 60 }
这个程序,我是直接在程序里面打开一个文件,然后程序给出这个文件的相关标志,上面也可以看出来,我们通过F_GETFL获取到文件的状态标志,然后我们和O_ACCMODE这个屏蔽字相比较,判断出文件的读写状态。通过和不同的状态标志,不断的&操作,判断是否有这个位。然后打印下标志信息。
继续下一个程序:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<errno.h> 5 #include<fcntl.h> 6 #include<unistd.h> 7 8 int set_fl(int fd,int flag); 9 10 int main(void) 11 { 12 int fd; 13 int val1; 14 //打开文件没有加入O_APPEND状态标志 15 fd = open("a.txt",O_RDWR); 16 if(fd < 0) 17 { 18 fprintf(stderr,"open:%s\n",strerror(errno)); 19 exit(0); 20 } 21 22 printf("Set status O_APPEND to the a.txt\n"); 23 //调用set_fl函数 24 fd = set_fl(fd,O_APPEND); 25 26 if((val1 = fcntl(fd,F_GETFL,0)) < 0) 27 { 28 fprintf(stderr, "F_GETFL:%s\n",strerror(errno)); 29 exit(0); 30 } 31 //判断有没有设置成功 32 if(val1 & O_APPEND) 33 printf("O_APPEND was be setted!\n"); 34 35 36 return 0; 37 38 39 } 40 41 int set_fl(int fd, int flag) 42 { 43 int val; 44 //获取到状态标志信息 45 if((val = fcntl(fd, F_GETFL,0)) < 0) 46 { 47 fprintf(stderr, "fcntl F_GETFL:%s\n",strerror(errno)); 48 exit(0); 49 } 50 //将指定状态标志加到状态信息中 51 val |= flag; 52 //设置状态标志信息 53 if(fcntl(fd,F_SETFL,val) < 0) 54 { 55 fprintf(stderr,"fcntl F_SETFL:%s\n",strerror(errno)); 56 exit(0); 57 } 58 59 return fd; 60 }
关键的是函数,set_fl() 必须先获取到位置信息,才能对其设置。这里面还有一个就是,我在main里面没有写的操作,所以对O_SYNC这类的是测试不出来状态信息,所以我在打开文件的时候没有加上O_APPEND这个参数,为的是在测试的时候能够显示出来这个状态信息。
说了这么多,fcntl有什么用,我套用书本上的话就是。我们的程序在一个描述符(标准输出)上进行操作,但不知道是由shell打开的相应文件的文件名,因为这是有shell打开的,所以不能在打开时,按我们的要求设置成O_SYNC标志,而fcntl则允许仅知道打开文件描述符时可以修改文件的性质。这句话需要多读几次才能理解是什么意思。
最后和大家共勉一下: 无他,唯手熟尔——《卖油翁》