【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中的指针数组的下标,文件描述符的分配规则就是优先从最小的下标开始分配

目录
打赏
0
0
0
0
0
分享
相关文章
|
12天前
|
Linux系统下快速批量创建和删除文件的方法
总的来说,使用shell脚本来批量处理文件是一种非常强大的工具,只要你愿意花时间学习和实践,你会发现它能大大提高你的工作效率。
66 19
|
2月前
|
Linux基础:文件和目录类命令分析。
总的来说,这些基础命令,像是Linux中藏匿的小矮人,每一次我们使用他们,他们就把我们的指令准确的传递给Linux,让我们的指令变为现实。所以,现在就开始你的Linux之旅,挥动你的命令之剑,探索这个充满神秘而又奇妙的世界吧!
76 19
|
2月前
|
Linux 常用文件查看命令
`cat` 命令用于连接文件并打印到标准输出,适用于快速查看和合并文本文件内容。常用示例包括:`cat file1.txt` 查看单个文件,`cat file1.txt file2.txt` 合并多个文件,`cat &gt; filename` 创建新文件,`cat &gt;&gt; filename` 追加内容。`more` 和 `less` 命令用于分页查看文件,`tail` 命令则用于查看文件末尾内容,支持实时追踪日志更新,如 `tail -f file.log`。
76 5
Linux 常用文件查看命令
|
1月前
|
如何创建Linux交换文件?Linux交换文件最新创建方法
Swap是Linux中的虚拟内存空间,用于在物理内存不足时将非活动进程移至磁盘,从而优化活动进程的性能。通过创建交换文件(如1GB),可灵活调整交换空间而无需重新分区。步骤包括:使用`fallocate`或`dd`创建文件、设置权限 (`chmod 600`)、格式化 (`mkswap`)、启用交换 (`swapon`)、修改`/etc/fstab`以持久化配置,以及调整`vm.swappiness`值(默认60,建议从10开始)来平衡内存与交换的使用。最后通过`swapon -s`检查状态并重启生效。此方法适用于VPS和专用服务器,需以root用户操作。
51 2
Linux|Transfer.sh 轻松实现文件共享
Linux|Transfer.sh 轻松实现文件共享
Linux|Transfer.sh 轻松实现文件共享
【Linux】进程IO|系统调用|open|write|文件描述符fd|封装|理解一切皆文件
本文详细介绍了Linux中的进程IO与系统调用,包括 `open`、`write`、`read`和 `close`函数及其用法,解释了文件描述符(fd)的概念,并深入探讨了Linux中的“一切皆文件”思想。这种设计极大地简化了系统编程,使得处理不同类型的IO设备变得更加一致和简单。通过本文的学习,您应该能够更好地理解和应用Linux中的进程IO操作,提高系统编程的效率和能力。
130 34
linux怎么把文件传到docker里面
在现代应用开发中,Docker作为流行的虚拟化工具,广泛应用于微服务架构。文件传输到Docker容器是常见需求。常用方法包括:1) `docker cp`命令直接复制文件;2) 使用`-v`选项挂载宿主机目录,实现数据持久化和实时同步;3) 通过SCP/FTP协议传输文件;4) 在Dockerfile中构建镜像时添加文件。选择合适的方法并确保网络安全是关键。
210 1
|
3月前
|
Linux文件与目录的日常
目录的切换 一般使用(”pwd“)显示当前所在的目录 比如:当前目录是在home下面的,与用户名相同的文件夹,可以使用(”cd“)命令来切换目录; 进入下载目录(”cd home/a/下载“)这种从给目录开头的一长串路经”叫做绝对路径“; 进入图片目录(”cd .. /图片/“)".."代表当前路径的上级路径,相对于当前的目录而言的”叫做相对路径“,(”.“)代表当前路径; 如果,想快速切换,上一个所在目录可以(”cd - / cd..“); 如果,想快速切换,追原始的目录可以(”cd --“); 查看目录及文件
59 14
Linux 将所有文件和目录名重命名为小写
Linux 将所有文件和目录名重命名为小写
|
5月前
|
linux积累-core文件是干啥的
核心文件是Linux系统在程序崩溃时生成的重要调试文件,通过分析核心文件,开发者可以找到程序崩溃的原因并进行调试和修复。本文详细介绍了核心文件的生成、配置、查看和分析方法
302 6
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等