首页 > 系统相关 >嵌入式Linux系统编程 — 1.5 文件描述符详解

嵌入式Linux系统编程 — 1.5 文件描述符详解

时间:2024-06-08 11:01:35浏览次数:37  
标签:fcntl 1.5 文件 标志 嵌入式 描述符 fd Linux flags

目录

1 文件描述符简介

1.1 文件描述符特点

1.2 标准文件描述符

1.3 文件描述符的生命周期

2 fcntl()函数

2.1 fcntl()函数简介

2.2 复制文件描述符(F_DUPFD)

2.3 获取/设置文件状态标志(F_GETFL/F_SETFL )


1 文件描述符简介

文件描述符(File Descriptor)是Linux和UNIX系统编程中的一个重要概念,它是一个用于标识打开文件或其他输入/输出资源的非负整数,文件描述符允许程序通过一个抽象的数字来引用文件和其他输入输出资源,而不是直接使用文件名或设备名。

从上面的示例代码中可以看到,调用 open 函数会有一个返回值, 譬如示例代码中的 fd1 和 fd2, 这是一个 int 类型的数据,在 open函数执行成功的情况下, 会返回一个非负整数, 该返回值就是一个文件描述符(file descriptor)。

1.1 文件描述符特点

  • 唯一性:每个打开的文件或设备在进程中都有一个唯一的文件描述符。文件描述符是从 3 开始分配的,譬如说进程中第一个被打开的文件对应的文件描述符是 3、第二个文件是 4……以此类推。为什么是从3开始?因为0、 1、 2 这三个文件描述符已经默认被系统占用了,分别分配给了系统标准输入(0)、 标准输出(1)以及标准错误(2)。

  • 抽象性:文件描述符提供了一个抽象层,使得对各种类型的输入输出设备的操作看起来像是对文件的操作。

  • 有限性:文件描述符的数量是有限的,通常由系统设置决定,例如Linux系统中可以通过ulimit -a 或 ulimit -a 命令查看文件描述符的限制。

该最大值默认情况下是 1024,也就意味着一个进程最多可以打开 1024 个文件,当然这个限制数其实是可以设置的。

1.2 标准文件描述符

在Linux中,每个进程启动时都会自动打开三个标准文件描述符:

  • 标准输入(stdin):文件描述符为0,通常用于从键盘接收输入。
  • 标准输出(stdout):文件描述符为1,通常用于向屏幕输出文本。
  • 标准错误(stderr):文件描述符为2,通常用于输出错误信息。

1.3 文件描述符的生命周期

  • 打开:使用open()openat()等系统调用打开文件时,系统会分配一个新的文件描述符。

  • 使用:通过文件描述符,可以使用read()write()lseek()等系统调用来读写数据或移动文件指针。

  • 关闭:使用close()系统调用关闭文件描述符,释放与该文件描述符关联的资源。

  • 重定向:可以将文件描述符重定向到其他文件描述符或文件,例如通过dup()dup2()

2 fcntl()函数

2.1 fcntl()函数简介

fcntl()函数可以对一个已经打开的文件描述符执行一系列控制操作,譬如复制一个文件描述符(与 dup、dup2 作用相同)、获取/设置文件描述符标志、获取/设置文件状态标志等,类似于一个多功能文件描述符管理工具箱。 fcntl()函数原型如下所示:

#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );

参数说明:

  • fd:文件描述符,即要控制的文件或设备的标识符。
  • cmd:控制命令,指定要执行的操作类型。
  • arg(可选):某些命令需要额外的参数,这些参数会通过arg传递给fcntl()

常见的控制命令(cmd):

  • F_DUPFD:复制文件描述符。如果提供了arg,则新文件描述符的最小值是arg,否则是当前进程的最大文件描述符加1。
  • F_GETFD:获取文件描述符的标志。FD_CLOEXEC:当执行exec系列函数时,关闭文件描述符;O_NONBLOCK:设置非阻塞模式;O_ASYNC:使文件描述符异步。
  • F_SETFD:设置文件描述符的标志。
  • F_GETFL:获取文件状态标志。
  • F_SETFL:设置文件状态标志。
  • F_GETLK:获取记录锁。
  • F_SETLK:设置记录锁。
  • F_SETLKW:等待直到可以设置记录锁。
  • F_GETOWN:获取文件描述符的所有者(通常用于异步I/O)。
  • F_SETOWN:设置文件描述符的所有者。

2.2 复制文件描述符(F_DUPFD)

dup 和 dup2用于复制文件描述符,除此之外,还可以通过 fcntl 函数复制文件描 述 符,可用的 cmd包括 F_DUPFD 和 F_DUPFD_CLOEXEC , 这里重点介绍 F_DUPFD 。

使用场景:使用 F_DUPFD 可以方便地在程序中创建文件描述符的副本,这在需要在不同的上下文中使用同一个文件,或者在需要临时改变文件描述符的属性(如设置非阻塞模式)时非常有用。

如何使用:当 cmd=F_DUPFD 时,它的作用会根据 fd 复制出一个新的文件描述符,此时需要传入第三个参数,第三个参数用于指出新复制出的文件描述符是一个大于或等于该参数的可用文件描述符(没有使用的文件描述符) ;如果第三个参数等于一个已经存在的文件描述符,则取一个大于该参数的可用文件描述符。测试示例如下:

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

int main() {
    int original_fd = open("myfile.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (original_fd == -1) {
        perror("open");
        return 1;
    }

    // 指定新文件描述符的最小值为 10
    int new_fd = fcntl(original_fd, F_DUPFD, 10);
    if (new_fd == -1) {
        perror("fcntl");
        close(original_fd);
        return 1;
    }

    printf("Original file descriptor: %d\n", original_fd);
    printf("New file descriptor: %d\n", new_fd);

    // 关闭文件描述符
    close(original_fd);
    close(new_fd);
    return 0;
}

在这个示例中,我们首先打开一个名为 "myfile.txt" 的文件,获取其文件描述符 original_fd。接着,我们使用 fcntl() 函数和 F_DUPFD 命令来复制这个文件描述符,并指定新文件描述符的最小值为 10。如果成功,fcntl() 将返回新的文件描述符 new_fd,它与 original_fd 指向同一个文件。最后,我们打印出原始和新的文件描述符,并关闭它们。运行结果如下:

2.3 获取/设置文件状态标志(F_GETFL/F_SETFL )

cmd=F_GETFL 可用于获取文件状态标志, cmd=F_SETFL 可用于设置文件状态标志。

  • cmd=F_GETFL 时不需要传入第三个参数,返回值成功表示获取到的文件状态标志;
  • cmd=F_SETFL 时,需要传入第三个参数,此参数表示需要设置的文件状态标志。

这些标志指的就是我们在调用 open 函数时传入的 flags 标志, 可以指定一个或多个(通过位或 | 运算符组合), 但是文件权限标志(O_RDONLY、 O_WRONLY、 O_RDWR)以及文件创建标志(O_CREAT、O_EXCL、 O_NOCTTY、 O_TRUNC)不能被设置;只有 O_APPEND、 O_ASYNC、O_DIRECT、 O_NOATIME 以及 O_NONBLOCK 这些标志可以被修改。 所以对于一个已经打开的文件描述符,可以通过这种方式添加或移除标志。

下面的示例代码,如何、首先获取文件状态标志,然后修改它们,并将修改后的标志应用到文件描述符上:

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

int main() {
    // 打开文件
    int fd = open("myfile.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (fd == -1) {
        perror("open");
        return 1;
    }

    // 获取当前文件状态标志
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1) {
        perror("fcntl F_GETFL");
        close(fd);
        return 1;
    }

    printf("Original file status flags: %o\n", flags);

    // 打印当前标志并设置为非阻塞模式
    printf("Setting O_NONBLOCK flag.\n");
    flags |= O_NONBLOCK;  // 添加非阻塞标志

    // 设置新的文件状态标志
    if (fcntl(fd, F_SETFL, flags) == -1) {
        perror("fcntl F_SETFL");
        close(fd);
        return 1;
    }

    // 再次获取文件状态标志以验证更改
    flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1) {
        perror("fcntl F_GETFL after F_SETFL");
        close(fd);
        return 1;
    }

    printf("New file status flags after setting O_NONBLOCK: %o\n", flags);

    // 关闭文件描述符
    close(fd);
    return 0;
}

程序中,首先使用 open() 函数打开一个文件,并获取其文件描述符 fd。然后,我们使用 fcntl() 函数和 F_GETFL 命令来获取文件的当前状态标志,并打印它们。

接着,通过逻辑或操作 flags |= O_NONBLOCK; 来设置 O_NONBLOCK 标志,这将文件描述符设置为非阻塞模式。之后,我们再次使用 fcntl() 函数和 F_SETFL 命令来应用这些更改。并再次获取并打印文件状态标志。

程序运行结果如下:


 

标签:fcntl,1.5,文件,标志,嵌入式,描述符,fd,Linux,flags
From: https://blog.csdn.net/qq_41921826/article/details/139453491

相关文章

  • Linux(Windows)网络配置(超详细)
    Linux网络配置三种模式(以下是理论,不想看可直接跳过):        桥接模式(bridged):也就是将虚拟机的虚拟网络适配器与主机的物理网络适配器进行交接,虚拟机中的虚拟网络适配器可通过主机中的物理网络适配器直接访问到外部网络。简而言之,这就好像在局域网中添加了一台新的、......
  • RT-Thread和Infineon主持的嵌入式网络应用开发沙龙
    主题会议由RT-Thread&&Infineon共同主持,PSoc62开发板现场演示从0到1搭建智能数据网关RT-Thread介绍rt-thread社区负责人郭占鑫郭工介绍RT-Thread英飞凌合作伙伴介绍英飞凌产品负责人介绍英飞凌的产品动态、分享未来的一些嵌入式技术发展方向以及应用案例技术分享(钩子函......
  • 嵌入式代码优化技巧
    内存管理技巧1.C/C++工程应尽量避免深拷贝,尽量用浅拷贝(指针或者引用),如果指针需要频繁拷贝,用智能指针是一种不错的选择2.启用内存池管理线程的内存开销,事先在堆里边分配好,然后快速使用避免复杂的浮点运算1.复杂的浮点运算尽量避免,有些芯片是不支持硬件双精度浮点数的,比如全志T3......
  • Linux下的C语言编程(指针)
    目录1一级指针1.1定义1.2指针的内存大小1.3指针的偏移1.3.1加法偏移1.3.2减法偏移2二级指针2.1定义2.2如何理解二级指针3三级指针3.1定义4数组和指针的关系4.1一维数组与指针的关系4.1.1数组名的性质4.1.2数组名与指针的地址偏移4.2二维数组与......
  • 物理机安装Linux操作系统
    1.物理机安装Linux环境这里以Ubuntu系统为例,Centos系统同理,windows同理1.1下载系统镜像官网下载速度慢的话可以直接百度各大学的镜像下载网站去下载,如下以清华镜像网站为例:https://mirrors.tuna.tsinghua.edu.cn/1.2下载工具ventoyVentoy官网:https://www.ven......
  • linux内存管理(一)物理内存的组织和内存分配
    从这一篇开始记录以下我看有关内存管理的内核代码的笔记.内容很长,很多是我自己的理解,请谨慎观看.伙伴系统的工作的基础是物理页的组织,组织结构有小到大依次为page->zone->node。下面从源码里看看各个结构是如何组织的。typedefstructpglist_data{structzonenode_z......
  • linux安装mysql8并配置主从
    服务器10.213.3.68DBmaser10.213.3.69DBslave都安装mysql8安装包,解压mysql-8.0.35-linux-glibc2.17-x86_64.tar.xz[root@DB-mastermysql8]#lsbindocsincludelibLICENSEmanREADMEsharesupport-files优化过的配置文件[mysqld]server-id=1log-bin=mys......
  • fs.1.10 ON rockylinux8 docker镜像制作
    概述freeswitch是一款简单好用的VOIP开源软交换平台。rockylinuxdocker上编译安装fs1.10版本的流程记录。环境dockerengine:Version24.0.6rockylinuxdocker:8freeswitch:v1.10.7手动模式rockylinux准备dockerhub拉取rockylinux镜像。sudodockerpullrockylinux:8......
  • Linux 获取系统开机/启动时间
    Linux下如何查看系统启动时间和运行时间以及安装时间-努力哥-博客园(cnblogs.com)cat/proc/uptime输出:5113396.94575949.85第一数字即是系统已运行的时间5113396.94秒,运用系统工具date即可算出系统启动时间代码:[root@localhost~]#date-d"$(awk-F.'{print......
  • 操作系统发展历史与Linux
    操作系统发展历史与Linux随着计算机技术的迅猛发展,操作系统作为计算机系统的核心软件,经历了从单一到多样、从封闭到开放的演变过程。从最初的批处理系统,到分时操作系统的兴起,再到个人计算机操作系统的崛起,最后到Linux的诞生和广泛应用,每一步都见证了操作系统领域的重大变革。早......