Linux系统调用之fcntl函数

前言

如果,想要深入的学习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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿宋同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值