这次我们介绍一下系统编程,文件IO中常用的一个文件控制函数fcntl(),该函数非常强大,在我们改变已打开文件的的各种属性等地方用的比较多。
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);
fd:文件句柄
cmd:操作类型,cmd可以选择一下选项
F_DUPFD:复制文件描述符
F_DUPFD_CLOEXEC:复制文件描述符,新文件描述符被设置了close-on-exec
F_GETFD:读取文件描述标识
F_SETFD:设置文件描述标识
F_GETFL:读取文件状态标识
F_SETFL:设置文件状态标识
F_GETLK:如果已经被加锁,返回该锁的数据结构。如果没有被加锁,将l_type设置为F_UNLCK
F_SETLK:给文件加上进程锁
F_SETLKW:给文件加上进程锁,如果此文件之前已经被加了锁,则一直等待锁被释放
第三个参数根据第二个参数不同进行选择
fcntl函数有几种常见用法:
1.文件共享(cmd=F_DUPFD).
2.复制文件描述符,新文件描述符被设置了close-on-exec(cmd = F_DUPFD_CLOEXEC)
3.改变打开文件的属性
4.获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW).
下面就这几种用法分别举例说明
1.文件共享
这个用法和dup和dup2很类似,可以参照我的上一篇博客内容
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
int fd = open("demo4.txt",O_WRONLY|O_CREAT|O_TRUNC);
if(fd == -1)
{
printf("open file error\n");
exit(-1);
}
int fd2 = fcntl(fd,F_DUPFD,4); //这里制定fd2和fd共享读写指针,且设置fd2的文件描述符为4
int fd3 = fcntl(fd,F_DUPFD,6); //这里制定fd3和fd共享读写指针,且设置fd3的文件描述符为6
printf("fd = %d fd2 = %d fd3 = %d \n",fd ,fd2 ,fd3 ); //3 4 6
return 0 ;
}
注意,如果fcntl在共享文件的时候设置的第三个文件描述符已经存在,则返回可用的最小文件描述符
2.复制文件描述符,新文件描述符被设置了close-on-exec(cmd = F_DUPFD_CLOEXEC)
每个文件描述符都有一个close-on-exec标志。默认情况下,这个标志最后一位被设置为 0。当这个值设置为1的时候,其他进程调用exec()族函数无法共享文件描述符,因为该文件描述符已经释放掉了,当这个值设置为0的时候,其他进程调用exec()族函数可以共享文件描述符。
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main()
{
pid_t pid;
int fd;
fd = open("demo4.txt",O_RDWR | O_APPEND | O_CREAT);
if (fd == -1)
{
printf("fd = %d\n",fd);
}
fcntl(fd, F_SETFD, 1); //设置close-on-exec,即子进程调用exec相关函数的时候,文件描述符关闭
char *s="parent processer\n";
pid = fork();
if(pid == 0)
{
execl("subthread", "./subthread", &fd, NULL); //子进程调用subthead程序,并把fd传给这个程序
}
wait(NULL);
write(fd,s,strlen(s));
close(fd);
return 0;
}
subthread.c
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char *argv[])
{
int fd;
fd = *argv[1];
char *s = "subthread\n";
write(fd, (void *)s, strlen(s));
close(fd);
return 0;
}
这样子,子程序就无法写入subthread字符串,因此文件中只有parent thread这个字符串
3.改变打开文件的属性
阻塞读写模型
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main()
{
char buf[16] = {0};
int ret;
int flags;
if(flags = fcntl(STDIN_FILENO, F_GETFL, 0) < 0) //初始化标准输入属性
{
printf("init file failed\n");
return -1;
}
//flags |= O_NONBLOCK;
flags &= ~O_NONBLOCK;
if(fcntl(STDIN_FILENO, F_SETFL, flags) < 0) //设置标准输入阻塞属性
{
printf("set file failed\n");
return -1;
}
//阻塞读取
while(1)
{
ret = read(STDIN_FILENO, buf, 4); //如果标准输入没有输入,则会阻塞在这里,不会返回
if(ret == -1)
{
printf("read error");
break;
}
else
{
printf("buf = %s\n",buf);
}
}
return 0;
}
非阻塞读写模型
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main()
{
char buf[4];
int ret;
int flags;
if(flags = fcntl(STDIN_FILENO, F_GETFL, 0) < 0)
{
printf("init file failed\n");
return -1;
}
flags |= O_NONBLOCK; //设置为非阻塞读写
//flags &= ~O_NONBLOCK;
if(fcntl(STDIN_FILENO, F_SETFL, flags) < 0)
{
printf("set file failed\n");
return -1;
}
while(1)
{
while(read(STDIN_FILENO, buf, 4) < 0); //这里进行循环检测
if (buf[0] == '#')
{
break;
}
printf("buf = %s\n",buf );
}
return 0;
}
还有一个文件锁的问题,我们放到下一节进行详细介绍,每次内容太多都不好消化。哈哈哈