Linux下“基础IO”相关内容整理分析

1. 复习C文件IO相关操作

#include<stdio.h>    
int main()                                                                                             
{                               
  FILE *fp = fopen("./log.txt","r");      
  if(NULL == fp) {                      
    perror("fopen");                    
    return 1;                           
  }                                     
                        
  char buffer[64];      
  while(fgets(buffer,sizeof(buffer),fp)){      
    printf("%s",buffer);                     
  }                                          
                                             
  if(!feof(fp)) printf("非正常退出!\n");      
  else printf("正常退出!\n");               
                                             
 // FILE *fp = fopen("./log.txt","w");       
 // if(NULL == fp)                           
 // {                                        
 //   perror("fopen");                    
 //   return 1;                           
 // }                                     
 //                                       
 // int cnt = 2;                                
 // while(cnt){                                 
 //   const char *msg = "hello sakeww\n";       
 //   fputs(msg,fp);                            
 //   cnt--;                                    
 // }                                           
 // fclose(fp);                                 
                                                
  return 0;                                     
} 

先运行注释部分,创建记事本
然后运行r,读取文件


c程序会默认打开三个输入输出流,stdin,stdout,stderr
在这里插入图片描述
因为他们三个的类型都是FILE,所以他们三个都是文件

他们对应的是:
stdin:键盘
stdout:显示器
stderr:显示器

测试:
在这里插入图片描述
在这里插入图片描述
stdout和stderr都可以完成上述操作

在这里插入图片描述
(>)输出重定向:本质是吧stdout的内容重定向的到文件中

总结:
fputs向一般文件或者硬盘设备都能写入!
一般文件在:磁盘,也是硬件
即:一切皆文件!!!
最终都是访问硬件:显示器,键盘,文件(磁盘)
OS是硬件的管理者!
所有的语言上的对“文件”的操作,都必须贯穿“OS”
OS不相信任何人,访问操作系统是需要通过系统调用接口的!
几乎所有的语言fopen,fclose,fwrite,fgets,fputs,fgets,fputc,等底层一定需要使用OS提供的系统调用
在这里插入图片描述

2. 文件相关系统调用接口

为什么要学习文件级别的系统调用接口

  1. 距离操作系统更近 ,就可通过学习这些接口,以语言的方式去揣测os的细节
  2. c/c++的文件操作虽然不一样,但是底层的接口是一样的,

在这里插入图片描述
在这里插入图片描述
问题:为什么是从3开始345?
0对应标准输入—键盘
1对应标准输出—显示器
2对应标准错误—显示器

联想:
当看见0123456789…这些小整数时,会联想到:
数组
------>
问题:
所有的文件操作表现上是不是都是进程执行的对应的函数?是
文件操作:进程的文件的操作
要操作文件必须先打开文件
打开文件的本质是:将文件相关的属性信息,加载到内存!
-------->
系统中是不是会存在大量的进程,进程可以打开多个文件吗?是
进程:打开文件 = 1:n
系统中存在可能更多的打开的文件
存在一个文件被多个进程打开
------>
那么操作系统要不要把打开的文件在内存中(系统中)管理起来呢?
必须要管理
-------->
OS如何管理打开的文件呢?
先描述,在组织!!!
---->
struct file{
包含了打开文件的相关属性
链接属性
}

3.认识文件描述符,理解重定向

3.1文件描述符fd

在这里插入图片描述fd:本质是内核中是进程和文件管理的数组的下标
在这里插入图片描述

简略图:(将上述图片进行简化)
在这里插入图片描述

3.1.1证明012也是可以被读写的

在这里插入图片描述
在这里插入图片描述
也可以在2里面写入
即:

write(2,msg,strlen(msg));

在这里插入图片描述
在这里插入图片描述
问题:为什么echo下一行是一个空行?
当我们输入你好的时候,我们按了一个 回车 ,回车是键盘的按键
读取的时候,也把这个回车读取了
在这里插入图片描述

3.2文件描述符的分配规则

在这里插入图片描述
显然这里输出: fd:3

当我们关闭0呢?
在这里插入图片描述
输出: fd:0

如果关闭2
输出: fd:2

基本结论:
文件描述符的基本规则:
给新文件分配的fd,是从fd_array中找一个最小的,没有被使用的,作为新的fd!

如果关闭0和2
那么就会输出: fd:0

3.3重定向

3.3.1重定向

当关闭1 时:
在这里插入图片描述
在这里插入图片描述
本来应该显示到显示器中,但是却被显示到文件内部–输出重定向

原因解释:
这个代码被编译运行之后,就会变成进程 ,在内核中就会产生task_struct
最终默认会打开012 这样的文件(struct file)
task_struct为了能指向这些文件,内部包含了一个指针,指向了一个结构(files_struct)
在这里插入图片描述

追加重定向
在这里插入图片描述
在这里插入图片描述

输入重定向
在这里插入图片描述

在这里插入图片描述

打印stdin,stdout,stderr
在这里插入图片描述
在这里插入图片描述

3.3.2 重定向的另一种办法 dup2

问题:
是不是必须要关闭1 然后打开文件,才能重定向?不是
有一个系统调用,可以很方便的帮助我们完成重定向–>dup / dup2

在这里插入图片描述

dup2()使newfd成为oldfd的副本,必要时先关闭newfd,但请注意以下几点:
*如果oldfd不是有效的文件描述符,则调用失败,newfd未关闭。
*如果oldfd是有效的文件描述符,并且newfd与oldfd具有相同的值,则dup2()不执行任何操作,并返回newfd。

在这里插入图片描述
在这里插入图片描述
发现没有清空原本文件的内容,而是直接覆盖
int fd = open(“./log.txt”,O_WRONLY|O_TRUNC);

延伸问题:

  1. 执行exec*程序替换的时候,会不会影响我们曾经打开的所有文件?不会
  2. 子进程会共享文件描述符吗?不会
    父进程如果曾经打开了标准输入,标准输出,标准错误,子进程也会继承下去
    为什么我们的所有进程,都会默认打开标准输入,标准输出,标准错误?
    我们命令行所有的进程他的父进程都是bash
    bash命令行解释器,需要打开标准输入,让你输入,标准输出,给你显示,标准错误,当你输入错误给你错误信息

延伸:
引用计数
struct file里面就包含了一个 cnt
cnt表示:有多少个进程指向我
指向就++,close就–
只有cnt==0 才会被释放

问题:
在这里插入图片描述因为你的重定向是输出重定向,只是把本来应该显示到1号文件描述符的内容直接显示到指定位置2号没变,二号照样指向标准错误,两个文件描述符,你重定向只能重定向一个,最终我们重定向的是标准输出,只有标准输出才能打印到文件当中,而标准错误照样会显示到显示器。问题:如何将标准输出和标准错误都进行重定向?在这里插入图片描述

4.文件缓冲区

4.1引入:

问题:
我们在上面测试代码的时候,都是打开文件,很多时候都是没有关闭文件
在这里插入图片描述
进程退出的时候,会刷新FILE内部的数据到OS缓冲区

用户->OS:
刷新策略:

  1. 立即刷新(不缓冲)
  2. 行刷新(行缓冲\n),例如:显示器打印
  3. 缓冲区满了,才刷新(全缓冲)例如:往磁盘文件中写入
  4. 123从OS->硬件也是同样适用的

默认情况下:
printf,fprintf是默认将数据拷贝到c语言拷贝区
当我们遇见\n时,如果是往显示器,那么会消息直接显示到“文件的内核缓冲区”
但是如果我们发生重定向
显示器->log.txt
行缓冲->全缓冲

答:这些数据通过printf,fprintf写到用户缓冲区,stdout指向的缓冲区中,里面包含的数据没有立即刷新到系统层面上,关闭文件时,就不可能刷新到log.txt中,所以不会显示

write是系统调用,写入的内容不会再c缓冲区停留,而是直接再文件的内核缓冲区

在这里插入图片描述

问题:
在这里插入图片描述
系统接口,不受影响,重复出现的是使用c接口
printf,fprintf,fputs的内容都在c缓冲区,fork之后函数退出的时候会刷新文件内核缓冲区
从而主函数,fork函数都会刷新
当在fork上面加入fflush(stdout)就会直输出一遍内容,

5. 理解文件系统中inode的概念

引入:
文件=文件内容+文件属性
如果一个文件没有被打开?这个文件在磁盘上

磁盘是一个什么设备?
磁盘是我们计算机中的一个机械设备(例外:SSD。FLASH卡,usb)

问题:目录是文件吗?是,目录也有inode
目录有数据吗?有
目录的数据块里面有什么?
目前创建的所有文件,全部都在一个特定的目录下!
文件名:inode编号!

在这里插入图片描述

6. 认识软硬链接,对比区别

软连接:
在这里插入图片描述
软连接是有自己独立inode的!软连接是一个独立文件!有自己的inode属性,也有自己的数据块(保存的是指向文件所在路径+文件名)

硬链接:
在这里插入图片描述
硬链接本质上根本就不是一个独立的文件,而是一个文件名和inode编号的映射关系,因为自己没有独立的inode!
因为没有独立的inode,所以没有独立的属性和数据

创建硬链接做了什么?
创建硬链接本质是在特定的目录下,填写一对文件名和inode的映射关系,(完成了重命名)

在这里插入图片描述

acm文件的三个时间:
Access 最后访问时间
Modify 文件内容最后修改时间
Change 属性最后修改时间

补充:
Access:在较新的Linux内核中,Access时间不会被立即刷新,而是有一定的时间间隔,OS才会自动进行更新时间
makefile与gcc会根据时间问题,来判定源文件和可执行程序谁更新,从而指导系统那些文件需要被重新编译!

7. 认识动态静态库,学会结合gcc选项,制作动静态库

查看文件调用了那些库?
在这里插入图片描述
Linux中,一般库分为两种:动态库和静态库
在Linux中,
如果是动态库:库文件是以.so作为后缀的!
如果是静态库:库文件是以.a 作为后缀的!
库文件的命名:lib开头加名字+.so/.a
库的真实名字:去掉lib前缀,去掉后缀,剩下的就是库名称

一般服务器,可能没有内置语言的静态库,而只有动态库

库本身 就是2进制文件,我们如何得知,一个库给我们提供了哪些方法?

一套完整的库:

  1. 库文件本身
  2. 头文件.h (文本的,会说明库中暴露出来的方法的基本使用!)
  3. 说明文档

我们在C/C++中,为什么有时候写代码的时候,有时候是.h里面放上声明,.c/.cpp放入实现?
为什么要这样设计?
因为我们要制作库! 方便使用,私密

在这里插入图片描述

制作静态库

下图部分解释:
%是一个通配符
作用:将当前目录下所有的.o结尾的文件进行展开

程序在翻译的时候会经过:预处理,编译, 汇编之后形成.o ,然后链接
.o叫做目标文件在这里插入图片描述

我们之前写的代码,也用了库,为什么就没有指名这些选项呢?
之前的库,在系统的默认路径下:/lib64,/usr/lib,/usr/include
编译器是能识别这些路径的

如果我不想带这些选项,我们是可以把对应的库和头文件拷贝到默认路径下
但是严重不推荐!
上面的过程也就是一般软件的安装过程

制作动态库

在这里插入图片描述

延伸:
我们其实一直都在直接或者简介使用者第三方库(C/C++)
如何使用呢?
拿别人的库和头文件,加入到自己的项目中
如何制作呢?所有的源代码(.c/.cpp)都需要先被编译成为.o(可重定向目标文件)

  1. 可以先把自己的所有的源文件,全部编译成为.o
  2. 制作动静态库的本质:就是将所有的.o打包,使用ar或者gcc来进行打包
  3. 交付:include+.a 或者 .so 文件

如果你提供静态库,我们只能将我们的库,静态链接到我们的程序中
如果你只提供动态库,我们只能将我们的库,采用动态链接 -static
如果你此时只有动态库,你是无法-static静态链接的!
一般只需要提供两种版本的库文件!

如果我既有动态库又有静态库,优先链接哪一个呢?
一定是优先动态!

  • 22
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 17
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值