当前位置:   article > 正文

Linux 基础I/O_o_rdwr

o_rdwr

Linux文件操作

系统调用接口:

1.open

#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);

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

O_RDONLY: 只读打开
O_WRONLY: 只写打开
O_RDWR: 读写打开

上述三个常量,必须指定一个且只能指定一个

O_CREAT:若文件不存在,则创建
O_APPEND: 追加写
O_TRUNC: 若文件存在,并且以只写/只读打开,则将清空文件内容

flags参数都是宏,这些标记为其实都是通过位图来传递的。

返回值是文件描述fd,稍后再说

2. read

#include<unistd.h>

ssize_t read(int fd,void *buf,size_t count);
  • 1
  • 2
  • 3

在这里插入图片描述

在这里插入图片描述

#include<unistd.h>

ssize_t write(int fd,void* buf,size_t count);
  • 1
  • 2
  • 3

在这里插入图片描述

文件描述符 fd

void test1()
{
        umask(0);
        int fd3 = open("love.txt",O_RDONLY|O_CREAT|O_APPEND,0666);
        printf("%d\n",fd3);

        char buffer[128];
        ssize_t s = read(fd3,buffer,sizeof(buffer)-1);
        if(s>0)
        {
                buffer[s] = '\0';
                printf("%s\n",buffer);
                printf("%d\n",s);
        }


        close(fd3);

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

当我们打开第一个文件的时候,运行会发现fd 为3 然后再打开多个文件,会发现fd依次为4,5,6,7,…

为什么会有这种现象呢?为什么从3开始呢?

Linux进程默认情况下会有三个缺省打开的文件描述符,分别是:
标准输入: 0 (键盘)
标准输入: 1 (显示器)
标准错误: 1 (显示器)

Linux下:一切皆为文件,这是Linux的哲学。

当文件没有被打开的时候,文件在磁盘中,但当一个文件被打开后,文件会被读取到内存中,一个进程可以打开多个文件,而这些打开的文件操作系统需要很好的管理,所以一个文件打开,会先创建对应的内核数据结构,而操作系统会把一个进程打开的所有文件通过链表连接起来,这样对打开文件的管理变成了对链表的增删查改,

strcut file
{

}

在这里插入图片描述

文件的重定向

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


int main()
{

        close(1);
        int fd = open("my.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
        printf("fd: %d\n",fd);

        close(fd);



        return 0;

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

看上面的代码,运行会发现没有任何的打印,这是为什么呢? 因为我们close(1),相当于把标准输出流(stdout)关闭了,这时候操作系统会遍历 fd_array[]数组,发现数组下标为1的 file是空的,这时候操作系统就会自动把1号下标的file 指向我们新打开的文件,这就是重定向的本质!!

在这里插入图片描述

但是这时候既然重定向了,那么说明printf的内容应该会输出到 my.txt的文件中,但是我们打开my.txt文件却发现是空的,这是为什么呢? 这里先卖个关子,只要加上 fflush(stdout) 刷新缓冲区,my.txt里面就有东西了,等下我们将缓冲区的时候再说这个,我们先把重定向讲完:

关于重定向,在这里我们介绍操作系统提供的接口: dup2

在这里插入图片描述

在这里插入图片描述

int dup2(int oldfd,int newfd);

dup2函数可以让 文件描述符为newfd变为文件描述符为oldfd的一份拷贝,也就是说,newfd会重定向为 oldfd 调用完函数后,只剩下oldfd了

若dup2调用成功则返回新的文件描述符,出错则返回-1.

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


int main()
{

        //close(1);
//      int fd = open("my.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
//      printf("fd: %d\n",fd);
//      
//      fflush(stdout);
//      close(fd);



        int fd = open("my.txt",O_RDONLY);
        int dp2 = dup2(fd,0);

        if(dp2 < 0 )
        {
                perror("DUP2");
                return 1;
        }

        char arr[128];
        read(0,arr,sizeof(arr));
        fprintf(stdout,"%s",arr);




        return 0;

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

缓冲区的理解:

什么是缓冲区?怎么理解?
  • 1

1. 缓冲区的本质就是一段内存
2. 缓冲区的存在能够解放进程的时间,缓冲区集中处理数据刷新以达到减少IO次数,提高整机的效率的目的

缓冲区在哪里?
  • 1

FILE的结构体里面有对应的缓冲区,而该FILE对应的是语言级别的缓冲区,也就是说每一个文件都有一个专属的fd和他的语言级别的缓冲区。

下面介绍关于刷新策略的问题:

  1. 无缓冲(立即刷新) :a. 进程退出 b. 用户强制刷新
  2. 行缓冲 (逐行刷新) : 显示器文件
  3. 全缓冲 (缓冲区满才刷新):块设备对应的文件(磁盘文件)

一个奇怪的问题:
在这里插入图片描述
观察上述代码,当我们执行代码并将其重定向到一个文件中,会发生什么呢?

在这里插入图片描述

为什么会有这样的情况呢????

首先,当我们重定向到一个文件时,缓冲区的刷新策略变为了全刷新,也就是缓冲区满,或者进程结束刷新。其次,我们所说的这些缓冲区都是语言级别的缓冲区,而write作为系统调用是没有缓存区的,像printf,fputs fprintf这些函数底层实际上是封装了write并且提供了缓冲区。因此刚开始的printf,fputs,fprintf都只是先写到缓冲区中,并不会直接写到文件中,而write直接写到文件中,那么为什么printf,fputs,fprintf会输出了两次呢?? 我们知道,当我们fork后,会产生子进程和父进程,缓冲区是FILE内部来维护,也就是说是父进程内部的数据区域,当fork时,父子进程会先共享这段数据段,当子进程要结束时,会刷新缓冲区,将缓冲区的内容刷新到文件中并且清空缓冲区,但此时父进程会立刻发生写时拷贝,保留原缓冲区的内容,当父进程结束时,会再刷新一次缓冲区,因此就会刷新两次!

磁盘的存储结构

在这里插入图片描述
当进行磁盘的读写时,磁头通过对磁盘的某一个面,某一个磁道的某一个扇区对磁盘进行定位,也就是CHS地址(C:Cylinders),(H:heads),(S:Sector)

在这里插入图片描述

而在操作系统中,可以把磁盘看成一个线性的数组,可以用LBA地址进行磁盘的定位。

注意:磁盘的一个扇区的大小是512字节,但是文件系统进行磁盘的访问的基本单位是4kb 也就是8个扇区,为什么这样设计呢? 这样做有两个目的:

  1. 提高 I/O效率
  2. 有助于解耦合

操作系统通过对磁盘分块,分组进行分治的管理,这样只要管理好部分就可以管理好全部了。

在这里插入图片描述
下面介绍每个磁盘文件系统:

Boot Block: 启动块,大小确定

Data blocks:以块为单位,进行文件数据的保存

inode Table:以128字节为单位,进行inode属性的保存(一般而言,一个文件对应一个独一无二的indoe编号)

struct inode
{
int id;
mode_t mode;
user name;
data d;

}(保存文件属性)
![在这里插入图片描述](https://img-blog.csdnimg.cn/e13be14a6cd64f118eaca931be0281c1.png#pic_center
Block Bitmap:位图,用于判断某个Block group是否已经被占用

indoe Bitmap:位图,用于判断某个inode块是否被占用

Group Descriptor Table:有多少inode,起始的inode编号,有多少个inode被使用,有多少block被使用,还剩多少,总group大小是多少等信息

Super Block:文件系统的顶层数据结构 存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了


我们知道,目录也是文件,那么文件就应该有它对应的 内容 + 属性,目录的属性应该就是它的inode的结构体,那目录的内容是什么呢?

目录的内容其实就是目录存储了该目录下的所有文件和其inode之间的一个映射关系

那么当我们创建一个文件时,操作系统都干了什么呢?

创建文件名,分配inode编号,找到该文件所处的目录,根据目录的inode,找到目录的data block,将文件名和inode的映射关系写到data block中


软硬链接:

  1. 软链接

软链接是一个独立的文件,有自己独立的inode编号,相当于Windows下的快捷方式,软链接的文件内容保存的是指向原文件的路径。

在这里插入图片描述
ln -s来创建软链接,unlink进行删除软链接

  1. 硬链接

硬连接并不是一个独立的文件,它和目标文件使用的是同一个inode,相当于在目录中添加一个新的文件名和inode编号的映射关系,而硬连接数,就相当于inode属性中的一个计数器,就是有几个文件名和inode建立了映射关系。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/182918
推荐阅读
相关标签
  

闽ICP备14008679号