Linux篇11基础IO

1.回顾c语言的文件操作

#include <stdio.h>

int main()
{

 // FILE* fp = fopen("log.txt", "r");
  FILE* fp = fopen("log.txt", "a");//a追加 log.txt不存在就会在当前路径下创建一个
  //所谓当前路径,不是指当前可执行程序所处的路径,而是进程运行时所处的路径
  if(fp == NULL)//打开失败
  {
    perror("fopen");
    return 1;
  }
//  char buffer[100];
//  int ct = 5;
//  while(ct--)
//  {
//   // fgets(buffer, sizeof(buffer), fp);
//   // fgets(buffer, sizeof(buffer), stdin);//stdin--从键盘输入
//    printf(buffer);
//
//  }
  int count = 5;
  while(count--)
  {
    fputs("hello Linux\n", fp);
  }
  fclose(fp);
  return 0;
}

  • stdin & stdout & stderr

    C默认会打开三个输入输出流,分别是stdin, stdout, stderr。这三个流的类型都是FILE*, fopen返回值类型,文件指针

    stdin:键盘 标准输入

    stdout:显示器 标准输出

    stderr:显示器 标准错误

2.系统文件I/O

在c语言里面,有fopen,fclose,fread,fwrite这些函数。而open,close,read,write都属于系统提供的接口,称之为系统调用接口。实际上,Linux环境使用fopen底层调用的还是open。那么c语言为什么还要弄出来一个fopen呢。这是为了保证c语言的跨平台性,比如在win下面,也有与fopen对应的系统调用接口。这样就可以实现c语言在Linux下掉Linux对应的系统调用接口,在win下调用win对应的系统调用接口。

3.文件描述符fd

文件描述符是一个整数,实际上是一个数组的下标

  • 0 & 1 & 2

    Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2。0,1,2对应的物理设备一般是:键盘,显示器,显示器。虽然1 2 都对应显示器,但是他们是不同的。标准错误是不能被重定向的,只会打印到显示器上。

image-20220703144747837

image-20220703133411679

  • fopen到底在干什么?
    1. 给用户申请struct file结构体,并返回地址file*
    2. 在底层通过open打开文件,并返回fd,把fd填充进struct file结构体里面的fileno。

4.缓冲区

凡是显示到显示器上面的内容,都是字符,凡是键盘读取的内容都是字符,所以键盘和显示器一般被称为“字符设备”

缓冲:

  • 无缓冲

  • 行缓冲(常见的对显示器刷新数据时)

    一行输入满了再一起刷新到显示器上面。所以我们在printf打印的时候,如果不加\n是不会立刻打印到显示器上面的,如果想立刻打印到显示器上面可以使用fflush

    image-20220703180552367

    进程在退出的时候会将用户区的缓冲区内容刷新到os缓冲区

  • 全缓冲(对文件写入的时候)

    整个缓冲区满了再刷新到磁盘

以上所说的缓冲区是c语言自带的,其实操作系统也有缓冲区,上面说的刷新到显示器,刷新到磁盘,其实不是一步完成的,而是c语言的缓冲区刷新到os的缓冲区,再由os的缓冲区刷新到显示器/硬盘。这里我们不关心os的缓冲区是如何刷新的

image-20220703181835839

下面我们看一下这段代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main()
{
  //c语言函数
  printf("hello printf\n");
  fprintf(stdout, "hello fprintf\n");
  //system
  const char* msg = "hello write\n";
  write(1, msg, strlen(msg));
  fork();
  return 0;
}

我们对上面代码做以下4种测试

  1. 注释fork()不创建子进程,不重定向

    image-20220703185106267

  2. 注释fork()不创建子进程,重定向

  3. 创建子进程,不重定向

    image-20220703185251208

  4. 创建子进程,重定向

    image-20220703185327860

对比结果我们发现:

  • 不重定向的打印顺序正常,而重定向之后先打印了hello write。
  • 创建子进程并重定向,hello printf 和hello fprint打印两次

原因:重定向之后,由于hello write是系统调用,没有缓冲区,直接打印出来,而hello printf和 hello fprint重定向之后,由行缓冲变成全缓冲,不会立刻打印出来,要等到进程结束才刷新缓冲区。当创建子进程不重定向,子进程相当于什么也没做。而创建子进程并且重定向时,父进程的hello printf和hello fprintf还在缓冲区,子进程由于写时拷贝,也有一份hello printf和hello fprintf。当父子进程结束,刷新缓冲区,父子进程的hello printf和hello fprintf都写入到log.txt。

5.dup2

#include <unistd.h>
int dup2(int oldfd, int newfd);
dup2() makes newfd be the copy of oldfd, closing newfd first if  necessary, but note the following:

       *  If  oldfd  is  not a valid file descriptor, then the call fails, and
          newfd is not closed.

       *  If oldfd is a valid file descriptor, and newfd has the same value as
          oldfd, then dup2() does nothing, and returns newfd.

 int main()
 {
     umask(0);
     int fd = open("log.txt", O_WRONLY|O_CREAT, 0666);
     dup2(fd, 1);  
     printf("haha\n");//原本打印到显示器就是写入到log.txt
     close(fd);
     return 0;
 }


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逃跑的机械工

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值