系统编程(四)文件IO之常用API fcntl

这次我们介绍一下系统编程,文件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;
}

还有一个文件锁的问题,我们放到下一节进行详细介绍,每次内容太多都不好消化。哈哈哈

猜你喜欢

转载自blog.csdn.net/bin_zhang1/article/details/81063506