fcntl函数:操纵文件描述符,改变已经打开的文件的属性
int fcntl(int fd, int cmd, ... //arg );
cmd选项:
一、复制文件描述符:F_DUPFD
二、更改设置文件描述标志:F_GETFD 、F_SETFD 文件描述符标志,是体现进程的文件描述符的状态.
当前只定义了一个文件描述符标志FD_CLOEXEC。
0: exec时不关闭已经打开的文件描述符
1: exec时关闭已经打开的文件描述符
三、获取或者设置文件状态标识:F_GETFL、 F_SETFL(读写追加阻塞等等)
文件状态标签中的标志可分为三类:访问方式、打开时标志和I/O操作方式。
1. 访问方式 访问方式指明允许文件描述字用于读、写或两者兼之,包括O_RDONLY、O_WRONLY和O_RDWR。
这些访问方式在文件被打开时选定,之后便不能再改变。
2、打开时标志指明打开文件时影响open()行为的一些选项。这些选项一旦文件打开就不保留,
但有一个例外是O_NONBLOCK,因为O_NONBLOCK同时也是一个I/O操作方式,故此标志被保留例如:O_CREAT
3、I/O操作方式:I/O操作方式影响使用文件描述字进行输入输出操作的工作方式。这些标志由open()设置,
之后可以用fcntl()获取和改变。O_APPEND、O_NONBLOCK、O_SYNC等
四、设置获取文件锁:F_GETLK、F_SETLK,F_SETLKW
1 #include<unistd.h> 2 #include<sys/types.h> 3 #include<sys/stat.h> 4 #include<fcntl.h> 5 #include<stdlib.h> 6 #include<stdio.h> 7 #include<errno.h> 8 #include<string.h> 9 #define ERR_EXIT(m) 10 do 11 { 12 perror(m); 13 exit(EXIT_FAILURE); 14 }while(0) //宏要求一条语句 15 void set_flags(int fd,int flags); 16 void clr_flags(int fd,int flags); 17 int main(int argc,char*argv[]) 18 { 19 char buf[1024]={0}; 20 int ret; 21 /* flags=fcntl(0,F_GETFL,0);//先获取标准输入的标志 22 if(flags==-1) 23 ERR_EXIT("fcntl get flags error"); 24 ret=fcntl(0,F_SETFL,flags|O_NONBLOCK);//将标准输入设置为非阻塞,默认对读是阻塞的。不更改其他状态 25 if(ret==-1) 26 ERR_EXIT("fcntl set flags error"); 27 */ set_flags(0,O_NONBLOCK);//设置非阻塞,直接返回 (EAGIAN)read error: Resource temporarily unavailable(资源暂且不可用),如果是socket,会返回EWOUDBOCK 28 //clr_flags(0,O_NONBLOCK);//清除 29 ret=read(0,buf,1024);//标准输入读,read标准输入0默认阻塞(文件状态标志) 30 if(ret==-1) 31 ERR_EXIT("read error"); 32 printf("buf=%s ",buf); 33 return 0; 34 } 35 void set_flags(int fd,int flags) 36 { 37 int val; 38 val=fcntl(fd,F_GETFL,0);//先获取标准输入的标志 39 if(val==-1) 40 ERR_EXIT("fcntl get flags error"); 41 val |=flags; 42 if(fcntl(fd,F_SETFL,val)<0) 43 ERR_EXIT("fcntl set flags error"); 44 } 45 void clr_flags(int fd,int flags)//清除状态 46 { 47 int val; 48 val=fcntl(fd,F_GETFL,0);//先获取标准输入的标志 49 if(val==-1) 50 ERR_EXIT("fcntl get flags error"); 51 val&=(~flags);//原状态中与上 flags 的反 52 if(fcntl(fd,F_SETFL,val)<0) 53 ERR_EXIT("fcntl set flags error"); 54 }
下面的例子讲解fcntl更改设置文件描述标志:
1 //hello.c 2 #include<unistd.h> 3 #include<stdio.h> 4 //hello程序打印程序环境变量 5 extern char** environ;//指针的指针,指向一个指针数组 environ-->[...]数组中每一项指向一个环境信息。例如"TERM=VI00","SHELL=/bin/bash" 6 int main(void) 7 { 8 printf("hello pid=%d ",getpid()); 9 //打印环境变量 10 int i; 11 for(i=0;environ[i]!=NULL;i++) 12 { 13 printf("%s ",environ[i]); 14 } 15 return 0; 16 } 17 18 //ececlp.标准输出在execlp中已经关闭,无法使用 19 #include<unistd.h> 20 #include<sys/types.h> 21 #include<stdlib.h> 22 #include<stdio.h> 23 #include<errno.h> 24 #include<fcntl.h> 25 #define ERR_EXIT(m) 26 do 27 { 28 perror(m); 29 exit(EXIT_FAILURE); 30 }while(0) //宏要求一条语句 31 int main() 32 { 33 printf("Entering main... "); 34 int flag=fcntl(1,F_GETFD); 35 int ret=fcntl(1,F_SETFD,flag|FD_CLOEXEC);//标准输出EXEC位置1 36 if(ret==-1) 37 perror("fcntl"); 38 //使用exec执行的程序里,此描述符被关闭,不能再使用它 39 execlp("./hello","hello",NULL);//ececlp替换是成功的,但是标准输出EXEC位已经被置位,所以hello无法输出。 40 /*输出结果 41 Entering main... 42 */ 43 printf("Exiting main... ");//不输出了 44 return 0; 45 }