基础I/O--文件系统

文章目录

回顾C文件接口

C代码:

c 复制代码
#include<stdio.h>    
    
int main()    
{    
  FILE *fp=fopen("log.txt","w");    
  if(NULL==fp)    
  {    
    perror("fopen");    
    return 1;    
  }    
    
  fclose(fp);                                                                                                                            
  return 0;    
}  

我们要进行文件操作,前提是程序运行起来了,所谓的文件的打开和关闭是CPU执行我们的代码才被打开或者关闭的。
fopen()函数:

初步理解文件

打开文件:本质上是进程打开文件

文件没有被打开的时候,在哪里?在磁盘上

一个进程能打开很多文件吗? 可以

系统中可不可以存在很多进程? 可以

因此,在很对情况下,操作系统内部一定存在大量的被打开的文件

操作系统要不要对这些被打开的文件进行管理? 要

如何管理?先描述,再组织

如果在磁盘新建一个文件,里面什么内容都没有,大小为0KB,那么这个文件占用空间吗?占,因为文件对应的创建时间、文件类型等对应的文件属性都存在。

因此,文件=内容+属性

理解文件

操作文件,本质上是进程在操作文件

文件没有被打开时,文件在磁盘上,磁盘属于外部设备,磁盘本质是一个硬件,向文件写入,本质是向硬件中写入,但是用户没有权利直接向硬件写入,硬件的管理者是操系统,因此用户不能绕过操作系统直接访问硬件,必须通过操作系统写入。操作系统给用户提供系统调用,我们用的C/C++/其他语言,都是对系统调用接口的封装。

访问文件除了可以使用已经封装好的函数,还可以使用系统调用。

使用和并认识系统调用

open

概述
bash 复制代码
#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);

int creat(const char *pathname, mode_t mode);

pathname: 要打开或创建的目标文件
flags: 打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行"或"运算,构成flags。

参数:
O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR : 读,写打开

这三个常量,必须指定一个且只能指定一个
O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
O_APPEND: 追加写

返回值:

成功:新打开的文件描述符

失败:-1

open 函数具体使用哪个,和具体应用场景相关,如目标文件不存在,需要open创建,则第三个参数表示创建文件的默认权限,否则,使用两个参数的open。

标记位传参理解
c 复制代码
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

int main()
{
  umask(0);
  //system call
  int fd=open("log.txt",O_WRONLY|O_CREAT,0666);
  if(fd<0)
  {
    perror("open");
    return 1;
  }
}

操作系统本身就有一个权限掩码,现在在代码中又写了一个权限掩码。这里使用的是代码中的掩码,就近原则。自己设置了权限掩码就用自己设置的,没有就用系统的。因此最终创建的权限掩码就是666


int flag:是32个比特位的,用比特位来进行标志位的传递。flag标志位传递本质上是一个位图。

我们来设计一个传递为图标记位的函数:

c 复制代码
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

#define ONE 1            //1: 0000 0001
#define TWO (1<<1)       //2: 0000 0010
#define THREE (1<<2)     //3: 0000 0100
#define FOUR (1<<3)      //4: 0000 1000

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(TWO);
  printf("\n");

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

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

  print(ONE|FOUR);
  printf("\n");

  return 0;
}

如果我们将打印替换成其他功能,那么就可以写出一个向指定函数传递标记位的方法。

返回值
c 复制代码
RETURN VALUE
open() and creat() return the new file descriptor, or -1 if an error occurred (in which case, errno is set appropriately).

返回成功,就帮我们创建一个新的文件描述符,就是一个整数,失败返回-1

c 复制代码
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>

int main()
{
  int fda=open("loga.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
  printf("fda:%d\n",fda);

  int fdb=open("logb.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
  printf("fdb:%d\n",fdb);

  int fdc=open("logc.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
  printf("fdc:%d\n",fdc);

  int fdd=open("logd.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
  printf("fdd:%d\n",fdd);

  return 0;
  
}

运行结果:

返回值是3 4 5 6,怎么不见0 1 2呢?

因为 0 1 2分别对应标准输入(键盘)、标准输出(显示器)、标准错误(显示器)

下文有具体介绍

close

c 复制代码
#include <unistd.h>

int close(int fd);

write

man 2 write可查看write函数

第一个参数 fd:表示待写入文件的文件描述符。

第二个参数 buf:指向待写入的文件内容。

第三个参数 count:待写入内容的大小,单位是字节。

返回值:实际上写入的字节数。

c 复制代码
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
c 复制代码
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
int main()
{
  umask(0);
  //system call
  int fd=open("log.txt",O_WRONLY|O_CREAT,0666);
  if(fd<0)
  {
    perror("open");
    return 1;
  }

  const char *message="hello linux file!\n";
  write(fd,message,strlen(message));

  close(fd);
  return 0;
}

会发现确实是写入了hello linux file!

将上述代码中,写入hello linux file!,修改成aaa

c 复制代码
  const char *message="aaa";
  write(fd,message,strlen(message));

运行结果:

可见,默认不清空文件

那么,如何再次写入文件时,之前文件会被清空呢?

需要添加一个O_TRUNC

c 复制代码
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
int main()
{
  umask(0);
  //system call
  int fd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
  if(fd<0)
  {
    perror("open");
    return 1;
  }

  const char *message="aaa";
  write(fd,message,strlen(message));

  close(fd);
  return 0;
}

运行结果:


追加写入:

c 复制代码
int fd=open("log.txt",O_WRONLY|O_CREAT|O_APPEND,0666);

read

c 复制代码
#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

第一个参数 fd:要读取文件的文件描述符。

第二个参数 buf:指向一段空间,该空间用来存储读取到的内容。

第三个参数 count:参数二指向空间的大小。

总结

c 复制代码
intfd=open("log.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);

写方式打开,不存在就创建,存在就先清空


c 复制代码
int fd=open("log.txt",O_WRONLY|O_CREAT|O_APPEND,0666);

追加形式写入

文件描述符fd

0&1&2

  • Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2.
  • 0,1,2对应的物理设备一般是:键盘,显示器,显示器

理解

在一个操作系统中有很多文件,操作系统需要对被打开文件进行管理,也就是需要创建对应的数据结构(struct_file),对文件的管理转换成对链表的增删查改。

任何一个文件=内容+属性,磁盘中文件的属性来初始化struct_file,将文件内容写入文件内核缓存里。

一个进程可以打开多个文件,对于进程来说怎么知道被打开文件与自己有关系,因此对应的task_struct存在一个属性struct files_struct *files,指向该类型的一个对象,该类型对象记录了当前进程所打开的所有文件新信息,操作系统中就存在一个结构体:struct file_struct,里面存在一个指针数组,数组的内容指向当前进程所打开的文件结构对象,也就是指向当前进程打开的文件。我们称这个数组为文件描述符表 ,数组下标称为文件描述符

文件描述符fd的本质是什么?

内核进程:文件映射关系的数组下标。

无论读写,都必须在合适的时候,让操作系统把文件内容读到文件缓冲区中。

open在干什么??

  1. 创建file
  2. 开辟文件缓冲区的空间,加载文件数据(延后)
  3. 查进程的文件描述符表
  4. file地址填入对应的下标中
  5. 返回下标

读写函数本质是拷贝函数

相关推荐
wowocpp7 分钟前
ubuntu 22.04 硬件配置 查看 显卡
linux·运维·ubuntu
山河君19 分钟前
ubuntu使用DeepSpeech进行语音识别(包含交叉编译)
linux·ubuntu·语音识别
鹏大师运维23 分钟前
【功能介绍】信创终端系统上各WPS版本的授权差异
linux·wps·授权·麒麟·国产操作系统·1024程序员节·统信uos
xinghuitunan24 分钟前
蓝桥杯顺子日期(填空题)
c语言·蓝桥杯
筱源源25 分钟前
Elasticsearch-linux环境部署
linux·elasticsearch
Half-up27 分钟前
C语言心型代码解析
c语言·开发语言
懒大王就是我1 小时前
C语言网络编程 -- TCP/iP协议
c语言·网络·tcp/ip
半盏茶香1 小时前
【C语言】分支和循环详解(下)猜数字游戏
c语言·开发语言·c++·算法·游戏
小堇不是码农1 小时前
在VScode中配置C_C++环境
c语言·c++·vscode
小肥象不是小飞象1 小时前
(六千字心得笔记)零基础C语言入门第八课——函数(上)
c语言·开发语言·笔记·1024程序员节