【linux】:linux下文件的使用以及文件描述符(下)

简介: 【linux】:linux下文件的使用以及文件描述符(下)

二、文件描述符



1.0 & 1 & 2


通过对open函数的学习,我们知道了文件描述符就是一个小整数。linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0,标准输出1,标准错误2。0 1 2对应的物理设备一般是:键盘,显示器,显示器,所以输出还可以有以下方式:

下面我们用文件的读写完成简单的输入输出:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main()
{
  char buf[1024];
  ssize_t s = read(0,buf,sizeof(buf));
  if (s>0)
  {
    buf[s] = 0;
    write(1,buf,strlen(buf));
    write(2,buf,strlen(buf));
  }
  return 0;
}


1和2代表标准输出和标准错误,我们先去读一下缓冲区,当返回值是缓冲区的最后一个字符,我们在这个位置放入\0,然后写入刚刚读取到的字符,下面我们运行一下:

47b5b5784b704676aeda8962fb08b2cb.png

通过这个小例子我们就知道了文件描述符就是从0开始的小整数,当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件,于是就有了file结构体,表示一个已经打开的文件对象,而进程执行open系统调用,所以必须让进程和文件关联起来,每个进程都有一个指针*files,指向一张表files_struct,该表最重要的部分就是包含一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件。


文件描述符的分配规则


我们写一个只读的代码来获取文件的fd:

int main()
{
  int fd = open("my.txt",O_RDONLY);
  if (fd<0)
  {
    perror("open");
    return 1;
  }
  printf("fd:%d\n",fd);
  close(fd);
  return 0;
}


ca683570a2d34bda840101b991def3a4.png 我们发现fd是3,那么这个时候我们把0关闭了再看看是什么结果:

int main()
{
  close(0);
  int fd = open("my.txt",O_RDONLY);
  if (fd<0)
  {
    perror("open");
    return 1;
  }
  printf("fd:%d\n",fd);
  close(fd);
  return 0;
}


04da7ea731724a0fb444113ae6b9bd55.png


通过运行结果我们发现,当我们将0关闭后文件描述符立马变成了0,我们再看看把2关闭了如何:

int main()
{
  //close(0);
  close(2);
  int fd = open("my.txt",O_RDONLY);
  if (fd<0)
  {
    perror("open");
    return 1;
  }
  printf("fd:%d\n",fd);
  close(fd);
  return 0;
}


ec82e3f6f34a42588786d6d5f60f46bf.png


当我们将2关闭后文件描述符又变成了2,这就说明文件描述符的分配规则是:在file_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。


2.重定向


还是刚刚的代码,如果我们将1关闭了会出现什么情况呢?

int main()
{
  //close(0);
  close(1);
  int fd = open("my.txt",O_WRONLY | O_CREAT,00644);
  if (fd<0)
  {
    perror("open");
    return 1;
  }
  printf("fd:%d\n",fd);
  fflush(stdout);
  close(fd);
  exit(0);
}


ca0bd90ac21443b88a3f895cf31fd55c.png


通过运行结果我们可以发现,本来应该输出到显示器上的内容,输出到了文件中,其中fd=1,这种现象叫做输出重定向,常见的重定向有> >> <。那么重定向的本质是什么呢?我们看下图:


e03cac692e2e4c45a1bdec2d05d94bea.png


当然对于重定向来说还有一个系统调用接口,这个函数叫dup2:

1. #include <unistd.h>
2. int dup2(int oldfd, int newfd);


对于这个函数的参数1来说是被换的那个描述符,参数2是像0,1,2,这样的默认描述符,接下来我们使用这个函数完成以下重定向:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
int main()
{
  int fd = open("my.txt",O_WRONLY | O_CREAT,00644);
  if (fd<0)
  {
    perror("open");
    return 1;
  }
  dup2(fd,1);
  printf("fd:%d\n",fd);
  close(fd);
}


a3c84da75e0748109c7702f3690d4404.png

通过上图我们可以看到成功的重定向到文件中。下面我们看看文件缓冲区的概念:

我们先编写一段代码看看现象:

int main()
{
  //c库中的
  fprintf(stdout,"hello fprintf\n");
  //系统调用
  const char* msg = "hello write\n";
  write(1,msg,strlen(msg));
  //fork();
  return 0;
}

5dedd5dd9bdf43f08646b979766a759e.png

可以正常打印,那么我们调用一下子进程会变成什么呢?

269261c8d26a4f9d8e665a2273f42e1c.png8cced6275c8a430d81155683837ca885.png

我们发现运行结果和刚刚是一样的,难道没有什么用吗?我们接下来重定向到文件中:

88fdddb8526441c58b93c3215debcda3.png


这个时候我们发现了,居然会多打印一个fprintf,为什么会出现这样的现象呢?


一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲。

printf fwrite 库函数会自带缓冲区(进度条例子就可以说明),当发生重定向到普通文件时,数据

的缓冲方式由行缓冲变成了全缓冲。

而我们放在缓冲区中的数据,就不会被立即刷新,甚至fork之后

但是进程退出之后,会统一刷新,写入文件当中。

但是fork的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新的时候,子进程也就有了同样的 一份数据,随即产生两份数据。

write 没有变化,说明没有所谓的缓冲

所以:


printf fwrite 库函数会自带缓冲区,而 write 系统调用没有带缓冲区。另外,我们这里所说的缓冲区,都是用户级缓冲区。其实为了提升整机性能,OS也会提供相关内核级缓冲区,不过不再我们讨论范围之内。 那这个缓冲区谁提供呢? printf fwrite 是库函数, write 是系统调用,库函数在系统调用的“上层”, 是对系统调用的“封装”,但是 write 没有缓冲区,而 printf fwrite 有,足以说明,该缓冲区是二次加上的,又因为是C,所以由C标准库提供。

行缓冲:碰到\0就刷新缓冲区

全缓冲:缓冲区满了才刷新

显示器采用的刷新策略:行缓冲。普通文件采用的刷新策略:全缓冲

那么缓冲区在哪里呢?在你进行fopen打开文件的时候,你会得到FILE结构体,缓冲区就在这个FILE结构体中。


总结



对于linux下的文件操作而言,C语言等对于文件操作的函数都是经过linux系统文件接口来封装的,我们在用C语言文件操作的时候看着很简单的一句代码在系统调用中会有很多的操作,对于文件描述符实际上就是file_struct中的指针数组的下标,文件描述符的分配规则就是优先从最小的下标开始分配

目录
相关文章
|
7月前
|
Linux 开发工具
7种比较Linux中文本文件的最佳工具
7种比较Linux中文本文件的最佳工具
7种比较Linux中文本文件的最佳工具
|
5月前
|
存储 数据管理 Linux
区分Linux中.tar文件与.tar.gz文件的不同。
总之,".tar"文件提供了一种方便的文件整理方式,其归档但不压缩的特点适用于快速打包和解压,而".tar.gz"文件通过额外的压缩步骤,尽管处理时间更长,但可以减小文件尺寸,更适合于需要节约存储空间或进行文件传输的场景。用户在选择时应根据具体需求,考虑两种格式各自的优劣。
824 13
|
6月前
|
安全 Linux
Linux赋予文件000权限的恢复技巧
以上这些步骤就像是打开一扇锁住的门,步骤看似简单,但是背后却有着严格的逻辑和规则。切记,在任何时候,变更文件权限都要考虑安全性,不要无谓地放宽权限,那样可能
204 16
|
7月前
|
Linux
【Linux】 Linux文件I/O常见操作技巧
以上就是Linux文件I/O操作的一些技巧,接纳它们,让它们成为你在Linux世界中的得力伙伴,工作会变得轻松许多。不过记住,技巧的运用也需要根据实际情况灵活掌握,毕竟,最适合的才是最好的。
240 28
|
6月前
|
存储 Linux 数据处理
深入剖析Linux中一切即文件的哲学和重定向的机制
在计算机的奇妙世界中,Linux的这套哲学和机制减少了不同类型资源的处理方式,简化了抽象的概念,并蕴藏着强大的灵活性。就像变戏法一样,轻轻松松地在文件、程序与设备之间转换数据流,标准输入、输出、错误流就在指尖舞动,程序的交互和数据处理因此变得既高效又富有乐趣。
111 4
|
7月前
|
Ubuntu Linux
"unzip"命令解析:Linux下如何处理压缩文件。
总的来说,`unzip`命令是Linux系统下一款实用而方便的ZIP格式文件处理工具。本文通过简明扼要的方式,详细介绍了在各类Linux发行版上安装 `unzip`的方法,以及如何使用 `unzip`命令进行解压、查看和测试ZIP文件。希望本文章能为用户带来实际帮助,提高日常操作的效率。
1055 12
|
8月前
|
Linux Shell
Linux系统下快速批量创建和删除文件的方法
总的来说,使用shell脚本来批量处理文件是一种非常强大的工具,只要你愿意花时间学习和实践,你会发现它能大大提高你的工作效率。
477 19
|
6月前
|
Linux
linux文件重命名命令
本指南介绍Linux文件重命名方法,包括单文件操作的`mv`命令和批量处理的`rename`命令。`mv`可简单更改文件名并保留扩展名,如`mv old_file.txt new_name.txt`;`rename`支持正则表达式,适用于复杂批量操作,如`rename &#39;s/2023/2024/&#39; *.log`。提供实用技巧如大小写转换、数字序列处理等,并提醒覆盖风险与版本差异,建议使用`-n`参数预览效果。
|
9月前
|
监控 Linux
Linux基础:文件和目录类命令分析。
总的来说,这些基础命令,像是Linux中藏匿的小矮人,每一次我们使用他们,他们就把我们的指令准确的传递给Linux,让我们的指令变为现实。所以,现在就开始你的Linux之旅,挥动你的命令之剑,探索这个充满神秘而又奇妙的世界吧!
177 19
|
8月前
|
缓存 Linux
如何创建Linux交换文件?Linux交换文件最新创建方法
Swap是Linux中的虚拟内存空间,用于在物理内存不足时将非活动进程移至磁盘,从而优化活动进程的性能。通过创建交换文件(如1GB),可灵活调整交换空间而无需重新分区。步骤包括:使用`fallocate`或`dd`创建文件、设置权限 (`chmod 600`)、格式化 (`mkswap`)、启用交换 (`swapon`)、修改`/etc/fstab`以持久化配置,以及调整`vm.swappiness`值(默认60,建议从10开始)来平衡内存与交换的使用。最后通过`swapon -s`检查状态并重启生效。此方法适用于VPS和专用服务器,需以root用户操作。
233 2