当前位置: 代码网 > 服务器>服务器>Linux > Linux fcntl函数怎么用?一文详解文件控制操作

Linux fcntl函数怎么用?一文详解文件控制操作

2026年09月06日 Linux 我要评论
如果,想要深入的学习linux系统调用中的fcntl函数,还是需要去自己阅读linux系统中的帮助文档。具体输入命令:man 2 fcntl即可查阅到完整的资料信息。fcntl函数fcntl函数是li

如果,想要深入的学习linux系统调用中的fcntl函数,还是需要去自己阅读linux系统中的帮助文档。

具体输入命令:

man 2 fcntl

即可查阅到完整的资料信息。

fcntl函数

fcntl函数是linux系统api中的一员,它的主要有以下几个作用:

  1. 改变已经打开的文件性质.
  2. 类似 dup 的功能。
  3. 改变文件的文件状态标志(fl_flags)。
  4. 得到或设置文件锁记录。

在这篇文章中,我们主要介绍功能前1和3。

它的函数原型是长这样的:

int fcntl(int fd, int cmd, …);

接下来,我来介绍一下它的多个参数与返回值

    参数:
    fd : 表示需要操作的文件描述符
    cmd: 表示对文件描述符进行如何操作(一些系统定义宏值)
        - f_dupfd : 复制文件描述符,复制的是第一个参数fd,得到一个新的文件描述符(返回值)
            int ret = fcntl(fd, f_dupfd);

        - f_getfl : 获取指定的文件描述符文件状态flag
          获取的flag和我们通过open函数传递的flag是一个东西。

        - f_setfl : 设置文件描述符文件状态flag
          必选项:o_rdonly, o_wronly, o_rdwr 不可以被修改
          可选性:o_append, o)nonblock
            o_append 表示追加数据
            nonblok 设置成非阻塞
    ... :可变参数,看需求添加。
    阻塞和非阻塞:描述的是函数调用的行为。

在使用这个函数之前,我们需要往c/c++文件中导入这些头文件:

#include <unistd.h>
#include <fcntl.h>

下面我来通过一个例子,来演示一下这个函数的具体作用:

代码演示:往已经存在的文件里追加数据

/*
    子进程向父进程发送信息
    设置管道非阻塞
    int flags = fcntl(fd[0], f_getfl);  // 获取原来的flag
    flags |= o_nonblock;            // 修改flag的值
    fcntl(fd[0], f_setfl, flags);   // 设置新的flag
*/

#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
    int fd[2] ;
    int ret = pipe(fd);
    if(ret == -1){
        perror("pipe");
        exit(0);
    }

    pid_t pid = fork();
    
    int flags = fcntl(fd[0],f_getfl); //获取原来文件描述符标志信息
    flags |= o_nonblock;//给原来的文件描述符标志信息中添加非阻塞的标志信息
    fcntl(fd[0],f_setfl,flags);//设置新的标志信息

    if(pid > 0){ 
        close(fd[1]);//只读不写,关闭写端
        

        char buf[1024];
        while (1)
        {
            sleep(2);
            printf("这里是父进程,我的pid是:%d,正在接受来自子进程的信息...\n",getpid());
            int len = read(fd[0],buf,sizeof(buf)-1);
            printf("len: %d , %s\n", len, buf);
            memset(buf,0,1024);
        }
        

    }else if(pid == 0){
        close(fd[0]);//只写不读,关闭读端

        while(1){
            printf("这里是子进程,我的pid是:%d,正在向父进程发送数据...\n",getpid());
            char buf[] = "hello,i am child";
            write(fd[1],buf,sizeof(buf));
            sleep(10);
        }

    }else{
        perror("fork");
        return -1;
    }
    return 0; 
}

有些人可能会问,我明明已经有写的权限了,我为什么不直接用write函数往文件里写文件。而先要用fcntl去追加权限呢?

这是因为如果你只有写的权限的话,你直接写就是把原来文件里的数据完全覆盖掉。而有o_append权限则是可以在原来文件的末尾处添加数据,而不是覆盖了。

代码演示

将管道的文件描述符改为非阻塞,父进程读取子进程写入的数据:

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
/*
    设置管道非阻塞
    int flags = fcntl(fd[0], f_getfl);  // 获取原来的flag
    flags |= o_nonblock;            // 修改flag的值
    fcntl(fd[0], f_setfl, flags);   // 设置新的flag
*/
int main() {

    // 在fork之前创建管道
    int pipefd[2];
    int ret = pipe(pipefd);
    if(ret == -1) {
        perror("pipe");
        exit(0);
    }

    // 创建子进程
    pid_t pid = fork();
    if(pid > 0) {
        // 父进程
        printf("i am parent process, pid : %d\n", getpid());

        // 关闭写端
        close(pipefd[1]);
        
        // 从管道的读取端读取数据
        char buf[1024] = {0};

        int flags = fcntl(pipefd[0], f_getfl);  // 获取原来的flag
        flags |= o_nonblock;            // 修改flag的值
        fcntl(pipefd[0], f_setfl, flags);   // 设置新的flag

        while(1) {
            int len = read(pipefd[0], buf, sizeof(buf));
            printf("len : %d\n", len);
            printf("parent recv : %s, pid : %d\n", buf, getpid());
            memset(buf, 0, 1024);
            sleep(1);
        }

    } else if(pid == 0){
        // 子进程
        printf("i am child process, pid : %d\n", getpid());
        // 关闭读端
        close(pipefd[0]);
        char buf[1024] = {0};
        while(1) {
            // 向管道中写入数据
            char * str = "hello,i am child";
            write(pipefd[1], str, strlen(str));
            sleep(5);
        }
        
    }
    return 0;
}

输出结果:

nowcoder@nowcoder:~/linux/lession22$ ./noblock1 
这里是子进程,我的pid是:24822,正在向父进程发送数据...
这里是父进程,我的pid是:24821,正在接受来自子进程的信息...
len: 17 , hello,i am child
这里是父进程,我的pid是:24821,正在接受来自子进程的信息...
len: -1 , 
这里是父进程,我的pid是:24821,正在接受来自子进程的信息...
len: -1 , 
这里是父进程,我的pid是:24821,正在接受来自子进程的信息...
len: -1 ,
^c

因为我们将这个管道的读端这个文件描述符改为了非阻塞的。所以当父进程循环的时候,如果管道中没有数据,read函数不会阻塞在那里,而是会直接返回-1。也就是表示读取失败。紧接着printf继续输出内容。不会停止下来。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com