Linux------基础IO

系统文件I/O:

1. 接口介绍

写文件:

读文件:

 

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

open函数返回值

1. 系统调用和库函数

(1) fopen,fclose,fread,fwrite都是C标准库当中的函数,我们称之为库函数(libc)。

(2) 而open,close,read,write,lseek都属于系统提供的接口,称之为系统调用接口

(3) 系统调用接口和库函数的关系

所以,可以认为:f#系列的函数,都是对系统调用的封装,方便二次开发。

2. 文件描述符fd(就是一个整数)

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

    而现在知道,文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files,指向一张表files_struct,该表最重要的部分就是包含一个函数指针数组,每个元素数组都是一个指向文件的指针。所以,本质上,文件描述符就是数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件。

3. 文件描述符的分配规则

    正常运行结果是 fd: 3,当关闭了0,输出结果是 fd: 0。可见,文件描述符的分配规则:在files_struct数组中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

4. 重定向

如果关闭了1:

    此时,我们发现。本来应该输出到显示器上的内容,输出到了文件myfile当中,其中,fd = 1。这种现象叫做输出重定向。常见的重定向有:>  >>  <。

重定向的本质:

    printf是C库当中的IO函数,一般往stdout中输出,但是stdout底层访问文件的时候,找的还是fd: 1,但此时,fd: 1下标所表示的内容,已经变成了myfile的地址。不再是显示器文件的地址。所以,输出的任何消息都会在文件中写入。进而完成输出重定向。

FILE

>> 因为IO相关函数与系统调用接口对应,并且库函数封装系统调用。所以本质上,访问文件都是通过fd访问的。

>> 所以C库当中的FILE结构体内部必须封装fd。

 

我们可以发现printf和fwrite(库函数)都输出了2次,而write只输出了一次(系统调用)。这是因为什么呢?肯定和fork有关。

>> 一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲。

>> printf  fwrite库函数会自带缓冲区,当发生重定向到普通文件时,数据的缓冲由行缓冲变成了全缓冲。

>> 而我们放在缓冲区里的数据,就不会被立即刷新,甚至fork之后

>> 但是进程退出之后,会统一刷新,写入文件当中。

>> 但是fork的时候,父子数据会发生写时拷贝,所以当父进程准备刷新的时候,子进程也就有了同样的一份数据,随即产生两份数据。

>> write没有变化,说明没有所谓的缓冲。

      综上,printf  fwrite库函数会自带缓冲区,而write系统调用没有缓冲区。另外,我们这里所说的缓冲区,都是用户级缓冲区。其实为了提升整机性能,OS也会提供相关内核级缓冲区,不过不在我们的讨论范围之内。

     那这个缓冲区该谁提供?printf fwrite是库函数,write是系统调用,库函数在系统调用的"上层",是对系统调用的"封装",但是write没有缓冲区,而printf  fwirte有,足以说明,该缓冲区是二次追加上的,又因为是C,所以由C标准库提供。

理解文件系统  

我们使用 ls -l 的时候看到的除了看到的文件名,还看到了文件元数据。

每列包含7列:

- 模式

- 硬链接数

- 文件所有者

- 组

- 大小

- 最后修改时间

- 文件名

ls -l读取存储在硬盘上的文件信息,然后显示出来:

其实这个信息除了通过这种方式来读取,还有一个stat命令能够看到更多信息:

上面的执行结果有几个信息需要解释清楚:

inode

为了能解释清楚inode我们先简单了解以下文件系统

>> 超级块:存放文件系统本身的结构信息

>> i节点表:存放文件属性,如文件大小,所有者,最近修改时间等

>> 数据区:存放文件内容

将属性和数据分开存放的想法看起来很简单,但实际上是如何工作的呢?

1. 创建一个新文件主要有以下4个操作

>>  存储属性

内核先找到一个空闲的节点(这里是656732)。内核把文件信息记录到其中。

>>  存储数据

该文件需要存储在三个磁盘块,内核共找到了三个空闲块:300,500,800。将内核缓冲区的第一块数据复制到300,下一块复制到500,以此类推。

>>  记录分配情况

文件内容按顺序300,500,800存放。内核在incode上的磁盘分布区记录了上述块列表。

>>  添加文件名到目录

新的文件名abc。Linux如何在当前目录中记录这个文件?内核将入口(656732,abc)添加到目录文件。文件名和inode之间的对应关系将文件名和文件的内容及属性连接起来。

2. 理解硬链接

我们看到,真正找到磁盘上文件的并不是文件名,而是inode。其实在Linux中可以让多个文件名对应于同一个inode。

(1) abc和def的链接状态完全相同,他们被称为指向文件的硬连接。内核记录了这个连接数,inode 656732 的硬连接数为2.

(2) 我们在删除文件时干了两件事情:>>  在目录中将对应的记录删除

                                                              >>  将硬连接数-1,如果为0,则将对应的磁盘释放。

3. 软连接

硬连接是通过inode引用另外一个文件,软连接是通过名字引用另一个文件。

动态库和静态库

1. 静态库与动态库

(1) 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要动态库

(2) 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。

(3) 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码。

(4) 在可执行文件开始运行以前,外部函数的机器码由操作系统从键盘上的该动态库中复制到内存中,这个过程陈威动态链接。

(5) 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。

测试程序:

生成静态库:

ar是gnu归档工具,rc表示(replace and create)

查看静态库中的目录列表

t:列出静态库中的目录列表

v:verbose 详细信息

-L:指定库路径

-l:指定库名

测试结束后,静态库删除,程序照样可以运行。

2. 库搜索路径

(1) 从左到右搜索-L指定的目录

(2) 由环境变量指定的目录(LIBRARY_PATH)

(3) 由系统指定的目录>*/usr/lib>* /usr/local/lib

3. 生成动态库

(1) shared:表示生成共享库格式

(2) fPIC:产生位置无关码(position independent code)

(3) 库名规则:libxxx.so

4. 使用动态库

(1) 编译选项

-l:链接动态库,只要库名即可 (去掉lib以及版本号)

-L:链接库所在的路径

(2) 运行动态库

>>  拷贝so文件到系统共享库路径下,一般指/usr/lib

>> 更改LD_LIBRARY_PATH

>> idconfig配置/etc/ld.so.conf.d/,ldconfig更新

5. 使用外部库

系统中其实有很多库,它们通常后一组互相关联的用来完成某项常见工作的函数构成。比如用来处理屏幕显示情况的函数(ncurses库)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值