文件描述符与重定向

1. open系统调用

在 Linux 中, open() 系统调用用于打开一个文件或设备,并返回一个文件描述符,通过该描述符可以进行文件读写操作。open() 可以用于创建新文件或打开已存在的文件,具体行为取决于传递给它的参数。

需要包含的头文件:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

函数原型: 

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

int creat(const char *pathname, mode_t mode);

参数解释: 

pathname: 要打开或创建的⽬标⽂件
flags: 打开⽂件时,可以传⼊多个参数选项,⽤下⾯的⼀个或者多个常量进⾏“或”运算,构成
flags。
参数:
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR : 读,写打开
这三个常量,必须指定⼀个且只能指定⼀个

O_CREAT : 若⽂件不存在,则创建它。需要使⽤mode选项,来指明新⽂件的访问
权限
O_APPEND: 追加写
返回值:
成功:新打开的⽂件描述符
失败:-1
mode 参数用于设置文件权限,通常只有在使用 O_CREAT 标志时才会使用。它指定文件的新权限,控制谁可以读取、写入和执行该文件。mode 是一个表示文件权限的整数,使用常量来组合不同的权限位。

常见的 mode 权限:

S_IRUSR:用户可读权限。
S_IWUSR:用户可写权限。
S_IXUSR:用户可执行权限。
S_IRGRP:组用户可读权限。
S_IWGRP:组用户可写权限。
S_IXGRP:组用户可执行权限。
S_IROTH:其他用户可读权限。
S_IWOTH:其他用户可写权限。
S_IXOTH:其他用户可执行权限。
示例:
open("file.txt", O_CREAT, S_IRUSR | S_IWUSR);
这会创建一个新文件,并设置权限为文件所有者可读和可写。
也可以用二进制表示
open("myfile", O_WRONLY | O_CREAT, 00644);
文件所有者权限为读写。用户组和其他用户权限为只读。

myfile.c:

  • 打开文件
    使用 open() 函数以只读 (O_RDONLY) 和创建文件 (O_CREAT) 的模式打开 log.txt 文件。如果文件不存在,O_CREAT 会创建文件。若文件打开失败,程序会输出错误并返回 1。

  • 读取文件
    通过 read() 函数从文件描述符 fd 中读取数据到 buff 中。read() 函数将最多读取 strlen(msg) 字符(这是一个错误的参数,应该是缓冲区大小)。

  • 输出内容
    如果读取成功(s > 0),则通过 printf() 输出读取到的内容。否则,退出循环。

  • 关闭文件
    使用 close() 关闭文件描述符。

2. 文件描述符

2.1 文件描述符

文件描述符(File Descriptor,简称 FD)是操作系统为每个进程提供的一种标识符,用于访问打开的文件、设备或其他输入输出资源。文件描述符是一个非负整数,它在进程的文件表中对应着一个指向内核中文件信息的指针。

上面myfile.c中read之所以可以通过int类型的fd读取到long.txt文件的内容就是因为每个文件都有一个文件描述符。

文件描述符的工作原理

每个进程在打开文件时,操作系统会为该进程分配一个文件描述符。文件描述符不仅仅用于普通文件,还可以用于设备、管道和套接字等其他输入输出资源。操作系统通过文件描述符来管理文件的读写操作。

Linux进程默认情况下会有3个缺省打开的⽂件描述符,分别是:
  1. 标准输入(STDIN):文件描述符 0,用于从用户或其他程序读取数据。
  2. 标准输出(STDOUT):文件描述符 1,用于向用户或其他程序输出数据。
  3. 标准错误输出(STDERR):文件描述符 2,用于输出错误信息。

除了标准输入、输出和错误输出外,应用程序可以打开更多文件,操作系统会为每个打开的文件分配一个文件描述符。

打开文件与文件描述符

当你调用 open() 函数打开一个文件时,操作系统会返回一个文件描述符,这个描述符可以用于后续的文件操作(如读写)。

int fd = open("example.txt", O_RDONLY);

这行代码尝试以只读模式打开 example.txt 文件,并返回一个文件描述符 fd。之后,可以使用该文件描述符来执行文件读写操作。

文件描述符的使用

在文件描述符返回后,可以通过以下系统调用来进行操作:

  • read(fd, buffer, size):从文件描述符 fd 中读取最多 size 字节的数据到 buffer 中。
  • write(fd, buffer, size):向文件描述符 fd 写入 size 字节的数据。
  • close(fd):关闭文件描述符,释放操作系统资源。

文件描述符表

操作系统维护一个文件描述符表,每个进程有一个独立的文件描述符表。文件描述符表的每个条目指向一个内核中的文件表,该表保存了文件的详细信息(如文件位置指针、权限等)。通过文件描述符,操作系统可以访问这些信息。

示例:打开、读取、写入和关闭文件

  • 程序会从标准输入读取 Hello, World! 字符串并存储到 buf 中。
  • 标准输出write(1, ...) 会将 Hello, World! 输出到标准输出(屏幕)。
  • 标准错误输出write(2, ...) 会将相同的 Hello, World! 输出到标准错误(通常也是屏幕,但会有不同的标记或颜色,取决于终端设置)。

程序会从标准输入读取 hello henu 字符串并存储到 buf 中。

write(1, ...) 会将 hello henu 输出到标准输出(屏幕)。

write(2, ...) 会将相同的 hello henu 输出到标准错误(通常也是屏幕,但会有不同的标记或颜色,取决于终端设置)。

 2.2 文件描述符分配规则

标准文件描述符
在 Linux 和 Unix 系统中,每个进程默认会打开三个文件描述符,分别是:

这些文件描述符默认已经为进程打开,并且对应终端设备或其他输入输出流。

  • 标准输入(STDIN): 文件描述符 0
  • 标准输出(STDOUT): 文件描述符 1
  • 标准错误输出(STDERR): 文件描述符 2

从 3 开始的文件描述符

  • 当一个文件第一次通过 open() 打开时,它会获得文件描述符 3
  • 第二个文件会分配文件描述符 4,以此类推。
  • 除了标准输入、输出和错误输出外,进程还可以打开更多文件或设备。这些文件描述符的分配从文件描述符 3 开始,并且会根据文件打开的顺序逐个递增。

文件描述符的分配是按顺序进行的,并且不会跳过数字。

    1 #include <stdio.h>
    2 #include <sys/types.h>
    3 #include <sys/stat.h>
    4 #include <fcntl.h>
    5 
    6 int main()
    7 {
    8   int fd = open("myfile", O_RDONLY);
    9   if(fd < 0)
   10   {
   11     perror("open");
   12     return 1;
   13   }
   14   printf("%d\n", fd);                                                                                                                                                                                                          
   15 
   16   close(fd);
   17   return 0;
   18 }

关闭0或者2:
发现是结果是: fd: 0 或者 fd 2 ,可见,文件描述符的分配规则:在记录文件描述符的files_struct数组当中,找到当前没有被使用的最小的⼀个下标,作为新的文件描述符。

3. 重定向

如果将标准输出(1)关闭,会发生什么呢?

    1 #include <stdio.h>
    2 #include <sys/types.h>
    3 #include <sys/stat.h>
    4 #include <fcntl.h>
    5 #include <stdlib.h>
    6 
    7 int main()
    8 {
    9   close(1);
   10   int fd = open("log.txt", O_RDWR | O_CREAT, 00644); 
                                                        
   11   if(fd < 0)                                                                                                                                                                                                                   
   12   {
   13     perror("open");
   14     return 1;
   15   }
   16   printf("d: %d", fd);
   17   fflush(stdout);
   18 
   19   close(fd);
   20   return 0;
   21 }

 此时,我们发现,本来应该输出到显示器上的内容,输出到了文件 myfile 当中,其中,fd=1。这

种现象叫做输出重定向。常见的重定向有: > , >> , < 。

几个细节:

使用fflush  close(1);: 关闭了标准输出文件描述符,导致标准输出流会失效。而 fflush  则起到了一个作用,它会强制刷新缓冲区,并把缓冲区中的数据写入到文件或终端中。

open参数:第一个0表示这是八进制,第二个零是 特殊权限设置,644分别是文件所有者、所属组、其他用户的权限。

常见的几种重定向:
>:将标准输出重定向到文件,覆盖原文件内容。
>>:将标准输出重定向到文件,追加到文件末尾。
<:将文件内容作为标准输入提供给命令。
2>:将标准错误重定向到文件,覆盖原文件内容。
2>>:将标准错误追加到文件末尾。
&>:同时将标准输出和标准错误重定向到同一个文件,覆盖原文件内容。
>&:将标准输出和标准错误输出重定向到同一个文件,功能与 &> 类似。
2>&1:将标准错误重定向到标准输出,通常用于将两者合并到同一个文件。
那重定向的本质是什么呢?

4. dup2系统调用

dup2 是一个在 Unix-like 操作系统中用于文件描述符复制的系统调用。它可以将一个文件描述符复制到另一个指定的文件描述符,通常用于重定向输入输出。

参数: 

  • oldfd:现有的文件描述符,通常是一个已打开的文件或设备(如标准输入、标准输出等)。
  • newfd:目标文件描述符。dup2 会将 oldfd 的副本复制到 newfd,并关闭 newfd(如果它已经打开)。

返回值:

功能:

dup2 会将 oldfd 指向的文件或设备重定向到 newfd

如果 newfd 已经打开,dup2 会先关闭 newfd,然后将 oldfd 的文件描述符复制到 newfd

如果 oldfd 等于 newfddup2 不会做任何操作,只会返回 newfd

  1 #include <stdio.h>
  2 #include <sys/types.h>
  3 #include <sys/stat.h>
  4 #include <fcntl.h>
  5 #include <stdlib.h>
  6 #include <unistd.h>
  7 
  8 int main()
  9 {
 10   close(1);
 11   int fd = open("log.txt", O_RDWR | O_CREAT, 00644);//使用O_APPEND则是追加写入
 12   if(fd < 0)
 13   {
 14     perror("open");
 15     return 1;
 16   }
 17   dup2(fd, 1);
 18   while(1)                                                                                                                                                                                                                       
 19   {
 20     char buf[1024] = {0};
 21     ssize_t read_size = read(0, buf, sizeof(buf) - 1);
 22     if(read_size < 0)
 23     {
 24       perror("read");
 25       return 1;
 26     }
 27     printf("%s", buf);
 28     fflush(stdout);
 29   }
 30 
 31   return 0;
 32 }

返回值: 

  • 成功时,返回 newfd,即新的文件描述符。
  • 失败时,返回 -1,并设置 errno

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/979910.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【WPF】绑定报错:双向绑定需要 Path 或 XPath

背景 最开始使用的是 TextBlock: <ItemsControl ItemsSource"{Binding CameraList}"><ItemsControl.ItemsPanel><ItemsPanelTemplate><StackPanel Orientation"Horizontal"/></ItemsPanelTemplate></ItemsControl.Item…

【Linux高级IO】五种IO模型 多路转接(select)

目录 1. 五种IO模型 1.1 阻塞式IO 1.2 非阻塞IO 1.3 信号驱动IO 1.4 多路转接 1.5 异步IO 2. 同步通信与异步通信 3. 多路转接 3.1 select 总结 1. 五种IO模型 1.1 阻塞式IO 阻塞式IO最为常见&#xff0c;在内核将数据准备好之前, 系统调用会一直等待&#xff0c;所有的…

Linux服务升级:Almalinux 升级 DeepSeek-R1

目录 一、实验 1.环境 2.Almalinux 部署 Ollama 3.Almalinux 升级 DeepSeek-R1 4.Almalinux 部署 docker 5. docker 部署 DeepSeek-R1 6.Almalinux 部署 Cpolar (内网穿透) 7.使用cpolar内网穿透 二、问题 1.构建容器失败 一、实验 1.环境 &#xff08;1&#xff09…

深度剖析数据分析职业成长阶梯

一、数据分析岗位剖析 目前&#xff0c;数据分析领域主要有以下几类岗位&#xff1a;业务数据分析师、商业数据分析师、数据运营、数据产品经理、数据工程师、数据科学家等&#xff0c;按照工作侧重点不同&#xff0c;本文将上述岗位分为偏业务和偏技术两大类&#xff0c;并对…

CosyVoice2整合包 特殊声音标记,声音克隆更逼真,新增批量生成

新增批量生成,可用于制作直播话术音频 特殊声音标记 符号示例1_语气加强<strong> </strong>每天都<strong>付出</strong>和<strong>精进</strong>&#xff0c;才能达到巅峰。2_呼吸声[breath][breath] 吸气,[breath] 呼气! [breath] 吸,[b…

vector习题

完数和盈数 题目 完数VS盈数_牛客题霸_牛客网 一个数如果恰好等于它的各因子(该数本身除外)之和&#xff0c;如&#xff1a;6321。则称其为“完数”&#xff1b;若因子之和大于该数&#xff0c;则称其为“盈数”。 求出2到60之间所有“完数”和“盈数”。 输入描述&#xff…

如何保证 Redis 缓存和数据库的一致性?

如何保证 Redis 缓存和数据库的一致性&#xff1f; 1. 问题出现场景 先修改数据库&#xff0c;再删除缓存 删除数据库数据成功了&#xff0c;但是删除缓存却失败了&#xff0c;缓存中仍保留的是旧数据 先删除缓存&#xff0c;再删除数据库 如果 Redis 缓存删除成功后&#xf…

信刻光盘安全隔离与信息交换系统让“数据摆渡”安全高效

随着数据传输、存储及信息技术的飞速发展&#xff0c;信息安全保护已成为重中之重。各安全领域对跨网数据交互的需求日益迫切&#xff0c;数据传输的安全可靠性成为不可忽视的关键。为满足业务需求并遵守保密规范&#xff0c;针对于涉及重要秘密信息&#xff0c;需做到安全的物…

网络原理--TCP/IP(2)

我们在之前已经介绍到TCP协议的核心机制二,接下来我们将继续介绍其他的核心机制。 核心机制三:连接管理 即建立连接,断开连接,在正常情况下,TCP要经过三次握⼿建⽴连接,四次挥⼿断开连接。 建立连接:TCP是通过“三次握手” 在生活中的握手就是打招呼,,但握手操作没有…

Windows PicPick Professional-v7.3.2-中文版

Windows PicPick Professional-中文版 链接&#xff1a;https://pan.xunlei.com/s/VOKGwGVGWUDl7L8cW4D1A1W4A1?pwdw5qz# - 更新了中文翻译&#xff0c;默认取消检测升级&#xff0c;删除多国语言

校园二手交易微信小程序的设计与实现(论文源码调试讲解)

第4章 系统设计 一个成功设计的系统在内容上必定是丰富的&#xff0c;在系统外观或系统功能上必定是对用户友好的。所以为了提升系统的价值&#xff0c;吸引更多的访问者访问系统&#xff0c;以及让来访用户可以花费更多时间停留在系统上&#xff0c;则表明该系统设计得比较专…

数据结构之各类排序算法代码及其详解

1. 排序的概念 排序是一种常见的算法概念&#xff0c;用于将一组数据按照特定的顺序进行排列。排序算法的目的是将一组数据按照递增或递减的顺序重新排列。常见的排序算法包括冒泡排序、插入排序、选择排序、快速排序、归并排序等。排序算法的选择通常取决于数据规模、数据分布…

6.6.6 嵌入式SQL

文章目录 2个核心问题识别SQL语句主语言和SQL通信完整导图 2个核心问题 SQL语句嵌入高级语言需要解决的2个核心问题是&#xff1a;如何识别嵌入语句&#xff1f;如何让主语言&#xff08;比如C,C语言&#xff09;和SQL通信&#xff1f; 识别SQL语句 为了识别主语言中嵌入的SQL…

keil主题(vscode风格)

#修改global.prop文件&#xff0c;重新打开keil即可 # Keil uVision Global Properties File # This file is used to customize the appearance of the editor# Editor Font editor.font.nameConsolas editor.font.size10 editor.font.style0# Editor Colors editor.backgro…

医疗AR眼镜:FPC如何赋能科技医疗的未来之眼?【新立电子】

随着科技的飞速发展&#xff0c;增强现实&#xff08;AR&#xff09;技术在医疗领域的应用逐渐成为焦点。医疗AR眼镜作为一种前沿的智能设备&#xff0c;正在为医疗行业带来深刻的变革。它不仅能够提升医生的工作效率&#xff0c;还能改善患者的就医体验&#xff0c;成为医疗科…

【异地访问本地DeepSeek】Flask+内网穿透,轻松实现本地DeepSeek的远程访问

写在前面&#xff1a;本博客仅作记录学习之用&#xff0c;部分图片来自网络&#xff0c;如需引用请注明出处&#xff0c;同时如有侵犯您的权益&#xff0c;请联系删除&#xff01; 文章目录 前言依赖Flask构建本地网页访问LM Studio 开启网址访问DeepSeek 调用模板Flask 访问本…

GPIO(嵌入式学习)

GPIO 通用输入输出口&#xff1a; 可分为八种输入输出模式 输出模式 下端可控制端口输出高低电平&#xff0c;用以驱动LED&#xff0c;控制蜂鸣器&#xff0c;模拟通信协议输出时序 输入模式 读取高低电平或电压&#xff0c;用与读取按键输入&#xff0c;外界模块电平信号…

AI助理精准匹配------助力快速搭建Stable Difussion图像生成应用

AI助理精准匹配------助力快速搭建Stable Difussion图像生成应用 背景信息搭建Stable Difussion图像生成应用释放资源 背景信息 过去你在阿里云社区搭建Stable Difussion图像生成应用&#xff0c;你可能还需要去在线实验室或者是官方文档去查找部署步骤&#xff0c;找到部署步…

火山引擎 DeepSeek R1 API 使用小白教程

一、火山引擎 DeepSeek R1 API 申请 首先需要三个要素&#xff1a; 1&#xff09;API Key 2&#xff09;API 地址 3&#xff09;模型ID 1、首先打开火山引擎的 DeepSeek R1 模型页面 地址&#xff1a;账号登录-火山引擎 2、在页面右下角&#xff0c;找到【推理】按钮&#…

排序算法(3):

这是我们的最后一篇排序算法了&#xff0c;也是我们的初阶数据结构的最后一篇了。 我们来看&#xff0c;我们之前已经讲完了插入排序&#xff0c;选择排序&#xff0c;交换排序&#xff0c;我们还剩下最后一个归并排序&#xff0c;我们今天就讲解归并排序&#xff0c;另外我们还…