深入理解IO底层

上层使用

向文件写入

文件的写入
#include <stdio.h>
#include <string.h>

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

结果:
在这里插入图片描述

从文件读取

文件的读取
#include <stdio.h>
#include <string.h>
                                   
int main()
{  
  FILE* fp = fopen("log.txt", "r");
  if(fp == NULL)                        
  {              
    perror("fopen");
  }   
  char buf[1024];                       
  // while(1)    
  //{  
  //  ssize_t s = fread(buf, 1, 5, fp);
  //  if(s > 0)           
  //  {
  //    buf[s] = 0;
  //    printf("%s", buf);
  //  }
               
  //  if(feof(fp))
  //  {
  //    break;
  //  }                             
  //}       
     
    ssize_t s = fread(buf, 1, 5, fp);
    if(s > 0)           
    {              
      buf[s] = 0;
      printf("%s", buf);
      printf("\n");
    }
  fclose(fp);
  return 0;
}                 

结果:
在这里插入图片描述

函数解释

fopen函数

在这里插入图片描述
参数path:是要被打开文件的路径
参数mode:是打开模式,指明打开后是要读还是写
如果被打开的文件不存在就创建该文件
返回值是一个结构体指针,该结构体中包含文件描述符

fclose函数

在这里插入图片描述
参数是一个结构体指针,该结构体中包含文件描述符,该函数用于关闭一个文件
在这里插入图片描述
返回值FILE是C语言自己封装的结构体,该结构体中包含文件描述符

fread函数

用于从文件中读取
参数ptr:指从文件中读取的数据要放到的位置的地址
参数size:是指一次要读取几个字节,它是按块读取的
参数nmemb:是要读取几个块这么大的数据
参数stream:是指要从哪个文件流中读取
返回值:实际读取了几个这么大的块

fwrite函数

用于向文件中写入
参数ptr:是指要读取的那一段数据的地址
参数size:是一次要写入的块大小
参数nmemb:是要写入几个size这么大的数据
参数stream:是指要从哪个文件流中写入
返回值:实际写入了几个这么大的块
向文件中写入时要+1吗?也就是加上’\0’?
不用,至于为什么在下面的底层原理讲解

系统接口

其实上述几个函数都是一些系统调用函数接口进行封装提供给用户的库函数,那么为什么系统不直接把那些函数提供给用户呢?因为操作系统不信任用户。
接下来我们来学习一下上面这些函数的系统调用接口

向文件写入

使用:

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

int main()
{
  umask(0);
  int fd = open("mytext", O_WRONLY | O_CREAT, 0644);
  if(fd < 0)
  {
    perror("open");
    return 1;
  }

  int count = 5;
  const char* str = "hello Linux!\n";
  int len = strlen(str);

  while(count--)
  {
    write(fd, str, len);
  }
  return 0;

}

结果:
在这里插入图片描述

从文件读取

函数解释

open函数

原理:
在这里插入图片描述
参数pathname:指要打开或创建的文件的路径
参数flags:该参数其实类似于位图,通过不同比特位的位置进行按位与或或等操作代表着不同的选项,因此通过这一个数字就可以代表着多个选项
在这里插入图片描述
打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。
参数:
O_RDONLY: 只读打开----read
O_WRONLY: 只写打开-----write
O_RDWR : 读,写打开-----read+write
这三个常量,必须指定一个且只能指定一个
O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
O_APPEND: 追加写,不覆盖之前已有的----append
O_WRONLY和O_RDWR的区别就是O_WRONLY不能进行读,也就是不能进行cat等操作
在这里插入图片描述
参数mode:
mode有模式的意思,用于设置要被创建的这个文件的权限,如果要被写入的文件已经有了,则这个参数就不没用了,上述代码中我们也用到了权限掩码umask,它默认是0002,我们在创建时给的参数是0446,如果想要结果就是0446,那么就需要把这个掩码改为0000,我们有两种做法,一种是改变整个环境的掩码,另一种就是改变该进程中的掩码,上面函数中设置的umask(0)就是只在这个进程中有效
返回值
成功:新打开的文件描述符
失败:-1

close函数

该函数用于关闭一个已被打开的文件
原理:

在这里插入图片描述
参数fd:
参数fd就是open函数的返回值(文件描述符)

文件描述符

什么是文件描述符?
通过刚才的open函数的返回值,我们知道了这个文件描述符就是一个int类型的数,那么这个数指的是什么呢?
先来了解一下文件被创建的过程,一个进程可以打开或关闭,创建多个文件,而在这个过程中又是怎么对这么多的文件进行管理的呢?
每个进程里面都有一个files的指针,指向一张叫做files_struct的表,该表最重要的部分就是包涵一个指针数组,数组中每个元素都是一个指向打开文件的指针。所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件
每一个文件结构体是用链表进行连接起来的,如果想要删除或打开某一个文件就把该文件指针从该链表添加或删除,并且也从对应的指针数组中添加或删除,这个结构体中包含文件被谁打开,属性,权限,指向下一个文件结构体的指针等,那么如果一个文件被多个进程打开,怎么解决什么时候关闭的问题呢,这里就要用到引用计数了,当这个文件不再被某一个进程打开时,这个文件才会被关闭
为什么我们打开电脑就能使用键盘和显示器灯硬件设备进行写和读呢?那是因为操作系统会给我们默认打开三个文件描述符,分别是stdin,stdout,stderror而他们对应的下标分别是标准输入0,标准输出1,标准错误2。
0,1,2对应的物理设备一般是:键盘,显示器,显示器
那么怎么去证明呢?通过write和read函数来证明:

write函数和read函数

在这里插入图片描述
在这里插入图片描述
参数fd从哪个文件流中读取或写入
buf就是把buf里的数据写入放到文件或者从文件中读取数据放到buf里
count:要读取或写入多少字节
返回值ssize_t:实际写入或读取多少字节,例如你想写入20字节但buf里只有15字节,那么如果写入成功,则返回值ssize_t就是15,读取数据也是一样,返回值是实际读取或写入的字节个数

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <unistd.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;
}

在这里插入图片描述
传递的参数fd,其实就是文件描述符,而文件描述符中0,1,2分别对应的就是标准输入,标准输出,标准错误

FILE

上面一直说FILE是C语言自己封装的一个结构体,该结构体中包含文件描述符,上层函数传递给系统调用接口的FILE结构体指针,本质上传递的是文件描述符
上层函数和系统接口对比:
1.
在这里插入图片描述
在这里插入图片描述
2.
在这里插入图片描述
在这里插入图片描述
3.
在这里插入图片描述

在这里插入图片描述
4.

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

梦想很美

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

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

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

打赏作者

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

抵扣说明:

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

余额充值