【Linux】基础IO----系统文件IO & 文件描述符fd & 重定向(上)

简介: 【Linux】基础IO----系统文件IO & 文件描述符fd & 重定向(上)

> 作者:დ旧言~

> 座右铭:松树千年终是朽,槿花一日自为荣。

> 目标:了解在Linux下的系统文件IO,知道什么是文件描述符,什么是重定向

> 毒鸡汤:白日莫闲过,青春不再来。

> 专栏选自:Linux初阶

> 望小伙伴们点赞👍收藏✨加关注哟💕💕



🌟前言

最早我们在C语言中学习关于如何用代码来管理文件,比如文件的输入和文件的输出,一些文件的接口,掌握上述的知识只能说是对文件入门而已,在Linux中我们是一切接文件的,如何深入学习文件的知识这是一个难题,今天我们所探讨就是Linux的基础I/O。


⭐主体

学习【Linux】基础IO咱们按照下面的图解:



🌙 回顾C文件接口

💫 C 读写文件

文件操作:

  • 首先要打开文件:打开成功,返回文件指针;打开失败,返回NULL
  • 最后要关闭文件

代码操作:

FILE *fopen(const char *path, const char *mode);
int fclose(FILE *fp);


1.C 写文件

采用方法:

我们可以fputs/fgets以字符串形式读写;也可以fprintf/fscanf格式化读写

代码操作:

int fputs(const char *s, FILE *stream);  向特定文件流写入字符串
int fprintf(FILE *stream, const char *format, ...);


举个栗子:

①如果以"w"模式打开文件,默认是文本读写,且会把原始内容清掉再写。

代码如下:

#include <stdio.h>
 
int main()
{
  FILE *fp = fopen("log.txt","w");
  if(fp == NULL)
  {
    perror("fopen");
    return 1;
  }
  // 进行文件操作
  
  fclose(fp);
  return 0; 
}

运行结果:



②如果要以追加方式写,则要以"a" append模式打开文件

代码如下:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
 
 
int main()
{
  FILE *fp = fopen("log.txt","a"); // 追加
  if(fp == NULL)
  {
    perror("fopen");
    return 1;
  }
  // 进行文件操作
  const char* s = "hello world\n";
  fwrite(s,strlen(s),1,fp);
  return 0; 
}

运行结果:



2.C 读文件

解读:

fgets从特定文件流中按行读取,内容放在缓冲区。读取成功返回字符串起始地址,读失败返回NULL.

代码演示:

char *fgets(char *s, int size, FILE *stream); //size:为缓冲区大小
int fscanf(FILE *stream, const char *format, ...);


举个栗子:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
 
 
int main()
{
  FILE *fp = fopen("./log.txt","r");
  if(fp == NULL)
  {
    perror("fopen");
    return 1;
  }
 
  // 进行文件操作
  char buffer[64];
  while(fgets(buffer,sizeof(buffer),fp))
  {
    printf("%s",buffer);//把我们读到的东西打出来
  }
  
  return 0; 
}


运行结果:


💫 关于stdin stdout stderr

概念分析:

C语言默认会打开三个输入输出流:stdin、stdout、stderr,它们的类型都是FILE*,C语言把它们当做文件看待,本质上我们最终都是访问硬件。C++中也有cin、cout、cerr,几乎所有语言都提供标准输入、标准输出、标准错误。

  • stdin对应的硬件设备是键盘
  • stdout对应显示器
  • stderr对应显示器


总结分析:

既然fputs是向文件写入,stdout也是FILE*类型,我们是不是可以向显示器标准输出打印了?这说明显示器被看做文件即:Linux下,一切皆文件。

举个栗子:



问题拓展:

fputs可以向一般文件(磁盘,也是硬件)或者硬件设备写入。这反映着Linux下一切皆文件


🌙 系统文件I/O

文件操作最终都是访问硬件(显示器、键盘、文件(磁盘))。众所周知,OS是硬件的管理者。所有语言上对“文件”的操作,都必须贯穿操作系统。然而OS不相信任何人,访问操作系统,就必须要通过系统接口!!


其实我们学过的几乎所有的语言中,fopen/fclose,fread/fwrite,fputs/fgets,fgets/fputs 等底层一定需要使用OS提供的系统调用接口,下面咱们就来学习文件的系统调用接口,才能做到万变不离其宗!!


图解:



💫 open & close

采用 man 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);


三个参数:

pathname: 要打开或创建的目标文件文件名
flags:    打开方式。传递多个标志位,下面的一个或者多个常量进行“或”运算,构成flags.
             O_RDONLY: 只读打开
             O_WRONLY: 只写打开
             O_RDWR  : 读写打开
          以上这三个常量,必须指定一个且只能指定一个
             O_CREAT : 若文件不存在,则创建它。同时需要使用mode选项,来指明新文件的访问权限
             O_APPEND: 追加写
mode:     设置默认权限信息 


返回值(int):

return the new file descriptor, or -1 if an error occurred (in which case, errno is set appropriately).
     成功: 新打开的文件描述符 
     失败: -1


采用 man close 指令查看相关资料

#include <unistd.h>
 
int close(int fd);


举个栗子:

代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
 
int main()
{
  int fd = open("./log.txt",O_WRONLY | O_CREAT);
  // int fd = open("./log.txt",O_WRONLY | O_CREAT,0644);
  if(fd < 0)
  {
    printf("open error\n");
    // return 1;
  }
  close(fd);
  return 0; 
}


运行结果:



问题分析:

可以看到权限完全是混乱的!这是因为,没有这个文件,要创建它,系统层面就必须指定权限是多少!我们采用权限设置的八进制方案

代码再次更新:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
 
int main()
{
  //int fd = open("./log.txt",O_WRONLY | O_CREAT);
  int fd = open("./log.txt",O_WRONLY | O_CREAT,0644);
  if(fd < 0)
  {
    printf("open error\n");
    return 1;
  }
  close(fd);
  return 0; 
}


运行更新结果:



分析结果:

之前我们在语言层面,创建时就是一个正常权限,我根本就不关心什么只写、创建、权限这些与系统强相关的概念。语言为我们做了封装,我用就好了

fopen("./log.txt", "w");
int fd = open("./log.txt", O_WRONLY | O_CREAT, 0644);

那第二个参数flags(int)为什么要把模式 | 在一起呢?这是一种用户层给内核传递标志位的常用做法。int有32个bit位,一个bit代表一个标志,就可以传递多个标志位且位运算效率较高。这些O_RDONLY、O_WRONLY、O_RDWR 都是只有一个比特位是1的数据,并且相互不重复,这样 |在一起,就能传递多个标志位。


我们可以来打开/usr/include/bits/fcntl-linux.h这个文件查看




【Linux】基础IO----系统文件IO & 文件描述符fd & 重定向(下)      https://developer.aliyun.com/article/1565672

目录
相关文章
|
11月前
|
Ubuntu Linux Anolis
Linux系统禁用swap
本文介绍了在新版本Linux系统(如Ubuntu 20.04+、CentOS Stream、openEuler等)中禁用swap的两种方法。传统通过注释/etc/fstab中swap行的方式已失效,现需使用systemd管理swap.target服务或在/etc/fstab中添加noauto参数实现禁用。方法1通过屏蔽swap.target适用于新版系统,方法2通过修改fstab挂载选项更通用,兼容所有系统。
948 3
Linux系统禁用swap
|
11月前
|
Linux
Linux系统修改网卡名为eth0、eth1
在Linux系统中,可通过修改GRUB配置和创建Udev规则或使用systemd链接文件,将网卡名改为`eth0`、`eth1`等传统命名方式,适用于多种发行版并支持多网卡配置。
1554 3
|
Ubuntu Linux 网络安全
Linux系统初始化脚本
一款支持Rocky、CentOS、Ubuntu、Debian、openEuler等主流Linux发行版的系统初始化Shell脚本,涵盖网络配置、主机名设置、镜像源更换、安全加固等多项功能,适配单/双网卡环境,支持UEFI引导,提供多版本下载与持续更新。
1030 3
Linux系统初始化脚本
|
12月前
|
运维 Linux 开发者
Linux系统中使用Python的ping3库进行网络连通性测试
以上步骤展示了如何利用 Python 的 `ping3` 库来检测网络连通性,并且提供了基本错误处理方法以确保程序能够优雅地处理各种意外情形。通过简洁明快、易读易懂、实操性强等特点使得该方法非常适合开发者或系统管理员快速集成至自动化工具链之内进行日常运维任务之需求满足。
712 18
|
11月前
|
安全 Linux Shell
Linux系统提权方式全面总结:从基础到高级攻防技术
本文全面总结Linux系统提权技术,涵盖权限体系、配置错误、漏洞利用、密码攻击等方法,帮助安全研究人员掌握攻防技术,提升系统防护能力。
1182 1
|
11月前
|
监控 安全 Linux
Linux系统提权之计划任务(Cron Jobs)提权
在Linux系统中,计划任务(Cron Jobs)常用于定时执行脚本或命令。若配置不当,攻击者可利用其提权至root权限。常见漏洞包括可写的Cron脚本、目录、通配符注入及PATH变量劫持。攻击者通过修改脚本、创建恶意任务或注入命令实现提权。系统管理员应遵循最小权限原则、使用绝对路径、避免通配符、设置安全PATH并定期审计,以防范此类攻击。
1457 1
|
缓存 监控 Linux
Linux系统清理缓存(buff/cache)的有效方法。
总结而言,在大多数情形下你不必担心Linux中buffer与cache占用过多内存在影响到其他程序运行;因为当程序请求更多内存在没有足够可用资源时,Linux会自行调整其占有量。只有当你明确知道当前环境与需求并希望立即回收这部分资源给即将运行重负载任务之前才考虑上述方法去主动干预。
2649 10
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
10月前
|
Java Unix Go
【Java】(8)Stream流、文件File相关操作,IO的含义与运用
Java 为 I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中。!但本节讲述最基本的和流与 I/O 相关的功能。我们将通过一个个例子来学习这些功能。
382 1
|
Java 大数据
解析Java中的NIO与传统IO的区别与应用
解析Java中的NIO与传统IO的区别与应用