基础IO详解

目录

C语言文件操作

文件的系统调用接口

文件管理

Linux下,一切皆文件

重定向

缓冲区

理解文件系统

动态库和静态库


C语言文件操作

C程序默认会打开三个输入输出流,stdin,stdtou,stderr,也被称为标准输入输出流

stdin:键盘;stdout:显示器;stderr:显示器

fputs向一般文件(磁盘中的)或者硬件设备都能写入数据 

stdout和stderr的区别

stdout在输出重定向后,会将输出内容从显示器转到文件中,而stderr则没有,输出重定向后还是

将输出内容输出到显示器上

文件操作,最终都是访问硬件:显示器,键盘,文件(磁盘),而os是硬件的管理者!所以所

有的语言上的对"文件"的操作,都必须贯穿os!而os不相信任何人,要访问操作系统,是需要通

系统调用接口的!

几乎所有的语言文件操作函数fopen,fclose,fread,fwrite,fgets,fputs,fgetc,fputc等底

层一定需要使用os提供的系统调用!

 

文件的系统调用接口

参数mode

如下图,创建并打开文件,如果没有指定权限,该文件的权限就会是乱的

指定权限后

参数flags

如下图,flags是传递标志位,表示以什么方式打开,它有32个bit位,一个bit位,代表一个标志!

O_WRONLY、O_CREAT等都是只有一个bit位是1的数据,而且不重复,下图是以8进制的形式 

返回值

如下图,连续打开多个文件,它们的返回值是连续的一串数字,但是却是从3开始的,这是因为0代

表的是标准输入:键盘,1代表的是标准输出:显示器,2代表的是标准错误:显示器,通过打印出

来的连续的数字,很容易想到数组的下标,而open的返回值又是os给的

文件管理

所有的文件操作,表现上都是进程执行对应的函数,即进程对文件的操作,而要操作文件就得先打

开文件,将文件相关的属性信息加载到内存

操作系统中存在大量的打开的文件,进程:打开的文件=1:n,既然系统中存在可能更多的打开的

文件,那os就必须对其进行管理,管理方式还是先描述,再组织

写文件

读文件

如果一个文件,没有被打开,它是被存放在磁盘里面的! 

空文件是要占磁盘空间的,因为文件有属性,属性也是数据

磁盘文件 = 文件内容 + 文件属性

如下图,打开的文件很多,如何确定哪些是我们进程的呢?所以就有了一个struct file* fd_array[]

数组,来保存struct file的地址

fd:本质是内核中进程和文件关联的数组的下标!!!

Linux下,一切皆文件

在struct file的上层看来,所有的文件,读写操作都是调用读->read,写->write,根本就不关心你

到底是什么文件! 

注意:上图中的每个硬件对应的read、write一定是不一样的!!!

默认的0、1、2也是可以直接读写的!

如下图,文件描述符的分配原则,给新文件分配的fd,是从fd_array中找一个最小的,没有被使用

的,作为新的fd!

如下图,如果关掉了1,把本来输出到显示器的内容,输出到了文件中

原因:

如下图,FILE是C语言层面上的结构体!而这个结构体里面一定包含了一个整数,是对应在系统层

面的,这个文件的打开的对应的fd!

printf、fprintf、cin——语言层

系统调用:open,write--fd —— 系统层

重定向

追加重定向:O_APPEND

 输入重定向  

 上图的关系一定存在,证明如下图

dup2:重定向的系统调用接口

 输出重定向

输入重定向

执行exec*程序替换的时候,不会影响我们曾经打开的所有的文件

创建子进程,files_struct会被拷贝一份,但是打开的文件不会

父进程如果曾经打开了标准输入,标准输出,标准错误,子进程也会继承下去!!!所以我们所有

的进程,都会默认打开标准输入,标准输出,标准错误

1与2的区别

如下图,在重定向后,只有标准输出会写入到文件中,而标准错误还是会打印到显示器上

如下图,是将标准输出和标准错误都写入文件中的方法

缓冲区

如下图,在close(fd)后,将本来打印在显示器中的内容变成写入文件的内容,文件中却也没有!

原因:

进程退出的时候,会刷新FILE内部的数据到os缓冲区

用户->os的刷新策略

立即刷新(不缓冲)

行刷新(行缓冲\n),比如,显示器打印

缓冲区满了,才刷新(全缓冲),比如,往磁盘中写入

注意:上述的刷新策略,对于os->磁盘,同样适用!!!

如下图,可知,因为printf、fprintf都是C语言的接口,所以它们的内容会被写入C缓冲区中,而这

里又发生了重定向,显示器->log.txt,即由行缓冲变成了全缓冲,此时内容还在C缓冲区,又因为

close(fd),所以C缓冲区也就无法将内容刷到文件的内核缓冲区

如下图,如果在close(fd)前,加一个fflush,就会立即刷新

如下图,在结尾加上一个fork后,输出到显示器中的内容都只有一份,而重定向后,输出到文件的

用C语言的接口输出的内容,则有两份。这是因为发生重定向后,由行缓冲变为了全缓冲,C缓冲

区的内容没有被刷新到os缓冲区,子进程发生写时拷贝,就有了两份!

 如下图,在加了fflush后,变为立即刷新后,就变为了一份!

理解文件系统

磁盘(机械硬盘)

如下图,磁盘中有一个光盘,用来存储数据的,我们可以把它想象成磁带,卷起来的时候是一个

圆,其实是一个线性结构!

磁盘写入的基本单位是:扇区,512字节,但是在与os交互的时候是4KB

Linux中,文件名在系统层面没有意义!是给用户用的!

Linux中,真正标识一个文件,是通过文件的inode编号!!!一个文件一个inode编号

  

如下图,因为磁盘太大,管理成本太高,所以需要将磁盘分区,划分为几块,便于管理,同时给每

一个区都写入文件系统,即格式化。比如中国这么大,为了便于管理,就划分为了多个省,而每个

省都有领导班子。同时每个区的空间也太大,就分为了多个组,每个组都有文件属性表和文件内容

表,其中都有很多数据块,存放着文件的内容和属性,有了inode编号,就能在inode Table中找

到对应的属性块,又通过blocks[]在Data blocks中找到对应的数据块。因为文件太多,在需要创

建一个文件时,不可能把所有的件都去遍历一遍,所以就有了inode Bitmap,用位图来判断

某个文件"是否"被占用!

目录是文件,有inode,而且有数据,存放的是文件名:inode编号!

查看文件内容

cat test.c -> 先查看文件所在的目录lesson -> data block -> 12345:test.c -> 12345 -> inode

table -> inode->block[] -> 打印文件内容!

删除文件

只需要将inode Bitmap中,将inode编号所对应bit位的大小由1变为0即可!

你所创建的所有文件,全部一定在一个特定的目录下!!!

软链接

创建软链接

删除软链接

应用场景

如下图,当我们要执行某个目录下的某个文件,且它的路径很长时,我们就可以给它创建一个软链

接,这样就不用输入一长串路径来执行,只需要执行创建的软链接即可!这就类似于在

Windows中创建快捷方式

软链接是有自己独立的inode的!软链接是一个独立文件!有自己的inode属性,也有自己的数据

(保存的是指向文件的所在路径+文件名)

 

硬链接 

硬链接本质上根本就不是一个独立的文件,而是一个文件名和inode编号的映射关系,因此自己没

有独立的inode!

创建硬链接,本质是在特定的目录下,填写一对文件名和inode的映射关系!

如下图,3是test.c所创建的硬链接数 

如下图,创建一个目录时,默认的硬链接数是2,是因为该目录下有一个.的文件,是它的其中一个

硬链

 

如果在此目录下再创建一个目录,那就会变为3,因为新创建的目录中的..文件也是它上级目录的一

个硬链接

文件时间

如下图,一个文件对应有三个时间,分别是Access、Modify、Change

 Access:文件最近被访问的时间,实际操作后,可能文件实际没有变化

原因:在较新Linux内核中,Access时间不会被立即更新,而是有一定的时间间隔,os才会自动

进行更新时间!

Modify:最近一次修改文件内容的时间

Change:最近一次修改文件属性的时间

如下图,在test.c中写了一句printf语句之后,它的三个时间都发生了变化,而Change时间会变,

因为修改了文件的大小属性,所以当我们修改文件内容的时候,是有可能会修改文件的属性的!

如下图,在一次make之后,如果继续make,也无法再编译

原因:

Makefile与gcc会根据时间问题,来判定源文件和可执行程序谁的时间更新,从而指导系统哪些

文件需要被重新编译

如下图,当文件test的时间比文件test.c的时间早一些的时候,就可以make,重新编译

动态库和静态库

一套完整的库:库文件本身;头文件(文本的,会说明库中暴露出来的方法的基本使用);说明文档

库文件命名

如下图,ldd是显示可执行程序依赖的库,在Linux中,如果是动态库,库文件是以.so作为后缀

的!如果是静态库,库文件是以.a作为后缀的!动态库和静态库就是文件,和我们所创建的一样!

库文件的命名:libXXXX.so  or  libYYY.a-..

库的真实名字:去掉lib前缀,去掉.a-或.so-后缀,剩下的就是库名称!

在Linux中,C++的源文件有三种格式,.cc、.cpp、.cxx!

gcc默认动态链接编译

gcc静态链接编译

在C/C++中,为什么有时候编写代码时,我们会在.h文件放声明,在.c/.cpp文件放入实现方法?

因为我们要制作库!方便使用,以及保证源码的私密性

如下图,多文件的程序编译方法

另一种方法,则是采用Makefile,将test_lib中的.c文件全部生成.o文件放在当前目录,然后链

接。<是将.c文件全部展开,一个一个地生成.o文件

制作静态库 

将所有的.o文件打包就是静态库文件

如下图,用ar -rc将.o文件全部打包放入静态库文件libmymath.a中,而如果想要给别人使用,则

创建一个目录,里面放有静态库文件,以及.h文件,然后将这个目录给他,用ar -tv则能查看静态

库文件

使用静态库

我们给别人交付的其实就是一个库文件+一套头文件

如下图,将静态库给我的朋友之后,里面包含了库文件和.h文件,他想使用时,只需调用即可!

-I(i):指明头文件搜索路径

-L:指明库文件搜索路径

-l(L):指明要链接哪一个库,后面的库是它的真实名字

如果不想每次都gcc ...,也可以写一个Makefile文件!!!

注意:我们之前写的代码,也使用了库,没有指明这些选项,是因为是在系统的默认路径下:比

如/lib64,/usr/lib,/usr/include等,编译器是能识别这些路径的。也就是说,如果我不想带这些

选项,也是可以把对应的库和头文件拷贝到默认路径下,但是不推荐这样做!

制作动态库

如下图,制作了一个 动态库文件,同时制作了一个目录,将全部.h文件和动态库文件放入其中

shared:形成一个动态链接的共享库

fPIC:产生.o目标文件,程序内部的地址方案是:与位置无关,库文件可以在内部的任何位置加

载,而且不影响和其他程序的关联性

使用动态库

如下图,在编译时没有问题,但是在运行时却找不到库文件,这是因为下图中Makefile的那些选项

只是告知编译器头文件库路径在哪里,当程序编译好的时候,此时已经和编译器无关了!所以需要

在运行的时候,进一步告知系统,我们的库在哪里

解决方法一:

配置环境变量LD_LIBRARY_PATH,将lib目录的路径拷贝给它

解决方法二:

如下图,用root身份,进入/etc/ld.so.conf.d/创建一个.conf文件,将路径拷贝到这个文件

 

总结

我们其实一直都在直接或间接使用库(C/C++)

如何使用

拿到别人的库文件和头文件,加入到自己的项目中!

如何制作

注意:所有的源代码都需要先被编译成为.o(可重定向目标文件)

可以先把自己的所有的源文件编译成为.o

制作动静态库的本质:就是将所有的.o打包,使用ar或者gcc来进行打包

交付:头文件+.a or .so文件

注意

如果你只提供静态库,我们只能将我们的库,静态链接到我们的程序中

如果你只提供动态库,我们只能将我们的库,采用动态链接,是无法-static静态链接的

如果既想动态链接,又想静态链接,一般需要提供两种版本的库文件!!!

gcc、g++优先动态链接!

我们之前写的所有的代码都没有报错,因为默认是动态链接的,因为我们一定有动态库(因为系

统中有很多的命令是用C语言写的,而且是动态链接的!)

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值