linux之基础IO

一、文件操作回顾

1.代码回顾

include <stdio.h>


int main()
{
  FILE* fp = fopen("log.txt", "w");
  if(fp == NULL)
  {
    perror("fopen");
    return 1;
  }
  fclose(fp);
  return 0;
}

2.提炼对文件的理解,初期

打开文件,实质是进程打开文件。

文件没有被打开时,他在磁盘上

进程可以打开很多文件,

文件=属性+内容

> “w”权限打开文件
>> “a”权限打开文件

3.理解文件

1.操作文件,实质是进程在操作文件。进程和文件的关系。

文件->在磁盘上->磁盘是外设->外设是硬件->向文件写入,本质上是向硬件中写入

->用户没有权利向硬件中写入,OS是硬件的管理者->通过OS向文件中写入

->OS必须给我们提供系统调用(OS不允许用户访问)->访问文件,我们也可以使用系统调用!

->fopen/fwrite/fread/fprintf/scanf/printf/cin/cout ?->我们使用的语言都是对系统调用接口的封装

二、使用系统调用

1.open函数

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

函数打开失败时返回-1, 成功时返回一个文件描述符

1.flags: (位图)

        32比特位。用比特位来进行标志位的传递,是OS设计很多系统调用接口的常见方法。

系统默认掩码0002。

参数说明

O_WRONLY  写方式打开
O_CREAT 不存在就创建
O_TRUNC 存在就清空
O_APPEND 追加写入

  2.id说明

0

标准输入,键盘
1 标准输出,显示器
2 标准错误,显示器

下面的数字则是用户创建的文件描述符

0 1 2默认打开   ---  linux一切皆文件

而C语言中

        FILE是C语言提供的结构体类型,一定封装文件描述符

FILE* stdin 标准输入
FILE* stdout                 标准输出
FILE* stderr 标准错误
int main()
{
  FILE* fp = fopen("log.txt", "w");
  if(fp == NULL)
  {
    perror("fopen");
    return 1;
  }
  int fd = fp->_fileno;
  printf("id/fd:%d\n", fd);
  fwrite("hello", 1, 5, fp);
  fclose(fp);
  return0;
}

文件描述符的本质:      内核的进程:文件映射关系数组的下标。 

 证明:内核代码

2.标记位传参

#define ONE 1
#define TWO (1<<1) 
#define THREE (1<<2)
#define FOUR (1<<3)


void Print(int flag)
{
  if(flag&ONE)
    printf("ONE\n");
  if(flag&TWO)
    printf("TWO\n");
  if(flag&THREE)
    printf("THREE\n");
  if(flag&FOUR)
    printf("FOUR\n");
}

int main()
{
  Print(ONE);
  printf("\n");

  Print(ONE|TWO);
  printf("\n");

  Print(ONE|TWO|THREE);
  printf("\n");

  Print(ONE|TWO|THREE|FOUR);
  printf("\n");
}

 3.write函数

ssize_t write(int fd, const void *buf, size_t count);
#include <stdio.h>

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

int main()
{
  umask(0);
  int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);//写方式打开|不存在就创建|存在就清空
  if(fd < 0)
  {
    perror("open");
    return 1;
  }

  const char* message = "hello linux txt!\n";
  write(fd, message, strlen(message));
  return 0;
}

三、输出重定向

重定向的本质,是在内核中改变文件描述符表示特定下标的内容

1.本来应该输出到显示器上内容,却输出到了文件中

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
  close(1);
  //这里的fd分配规则是:最小的,没有被占用的文件描述符
  int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
  if(fd < 0) { perror("open"); return 1;}

  //输出重定向
  printf("fd:%d\n", fd);
  printf("fd:%d\n", fd);
  printf("fd:%d\n", fd);
  printf("fd:%d\n", fd);
  printf("fd:%d\n", fd);

  fprintf(stdout, "hello fprintf!\n");
  const char* s = "hello write!\n";
  fwrite(s, strlen(s), 1, stdout);

  fflush(stdout);
  close(fd);
  return 0;
}

 2.本来应该从键盘输入的内容,却从文件中输入了

int main()
{
  close(0);
  int fd = open("log.txt",O_RDONLY);
  if(fd < 0) return 1;
  printf("fd: %d\n", fd);
  char buffer[64];
  fgets(buffer, sizeof buffer, stdin);
  printf("%s\n",buffer);
  return 0;
}

1.dup

in(int argc, char *argv[])
{
  int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
  if(fd < 0) {perror("open"); return 1;}

  dup2(fd, 1);

  fprintf(stdout, "%s\n",argv[1]); //stdout -> 1 -> screen
  return 0;
}

四、缓冲区

1.什么是缓冲区?

        就是一段内存空间 (这个空间谁提供?) --- 初步理解

2.为什么需要缓冲区

        主要是为了提高用户的响应速度!

缓冲区的刷新策略:

        1. 立即刷新

        2. 行刷新(行缓冲) \n

        3. 满刷新(全缓冲) 

特殊情况

        1. 用户强制刷新(fflush)

        2. 进程退出

C语言结构体FILE 不仅封装了文件描述符,还封装了缓冲区。

当往显示器上打印时,默认行刷新。

当往磁盘上打印时,默认全刷新。

所以当我们重定向将内容打印到文件时,如果在结尾处加上fork(),因为刷新也算改变,子进程默认发生写时拷贝,从而将数据copy两份。而我们只要在fork()前加上一个fflush(stdout),将数据提前强制刷新,就没有问题了。系统调用函数则也没有这个问题,因为数据是去内核缓冲区,到操作系统内核了。

1 - stdout  2 - stderr。 1和2对应的都是显示器文件,但是他们两个是不同的,如同认为,同一个显示器文件,被打开了2次。

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-04-01 05:00:04       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-01 05:00:04       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-01 05:00:04       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-01 05:00:04       20 阅读

热门阅读

  1. 测试方法--一起学习吧之测试

    2024-04-01 05:00:04       19 阅读
  2. postcss的安装与使用

    2024-04-01 05:00:04       19 阅读
  3. 《外观模式(极简c++)》

    2024-04-01 05:00:04       18 阅读
  4. Android intent 应用场景

    2024-04-01 05:00:04       13 阅读
  5. 住宅IP是什么?与机房IP有哪些区别?

    2024-04-01 05:00:04       16 阅读
  6. 力扣 599.两个列表的最小索引和

    2024-04-01 05:00:04       20 阅读
  7. 交换奇偶位

    2024-04-01 05:00:04       17 阅读
  8. wpf ContentPresenter

    2024-04-01 05:00:04       14 阅读
  9. vue2使用axios封装请求数据

    2024-04-01 05:00:04       19 阅读
  10. SQLAlchemy修改postgres表的jsonb字段失效

    2024-04-01 05:00:04       15 阅读