基础I/O

Posted 任我驰骋°

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基础I/O相关的知识,希望对你有一定的参考价值。

基础IO

前言

文件 = 内容+属性

FIFE*, C
stdin: 标准输入,键盘
stdout:标准输出,显示器
stderr:标准错误,显示器

Linux 下一切皆文件。

为什么语言都要开放这些标准流?
语言也是需要进行交互的。

一、回顾C文件接口

写文件


将内容写到log.txt中,


将内容输出到显示器中,将fp改为stdout即可。

stdin & stdout & stderr

C默认会打开三个输入输出流,分别是stdin, stdout, stderr

仔细观察发现,这三个流的类型都是FILE*, fopen返回值类型,文件指针

二、系统文件I/O

操作文件,除了上述C接口(当然,C++也有接口,其他语言也有),我们还可以采用系统接口来进行文件访问,先来直接以代码的形式,实现和上面一模一样的代码。

接口介绍

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 : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
 O_APPEND: 追加写
 返回值:
 成功:新打开的文件描述符
 失败:-1

mode_t理解:直接 man 手册,比什么都清楚。
open 函数具体使用哪个,和具体应用场景相关,如目标文件不存在,需要open创建,则第三个参数表示创建文件的默认权限,否则,使用两个参数的open。

0 & 1 & 2

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

运行的结果为:

可以看到这里是直接从3开始的,为什么没有0,1,2呢?
因为0,1,2这三个接口是默认被打开的。

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

文件描述符的分配规则



我们发现文件描述符默认的输出是3。‘

关闭0的话再看:


发现是结果是: fd: 0 或者 fd 2 可见,文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

重定向

那如果关闭1呢?看代码:


可以看到应该要输出在显示屏上的内容输出到了log.txt文件中,这就是重定向输出。其中,fd=1。这种现象叫做输出重定向。常见的重定向有:>, >>, <。

那么,重定向的本质是什么呢?

三、FIFE缓冲区

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

所以C库当中的FILE结构体内部,必定封装了fd。

先来看代码:



这里输出到显示屏上时都只输出一次。

这里我们再把close(1)打开。



这里我们看到write打印了一次而printf和fprintf打印了两次。

由此我们总结:
printf,fprintf是库函数。
write是系统调用。

缓冲区
显示器:行缓冲(遇到\\n就直接打印)
文件:全缓冲(最后全一起打印)

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

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

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

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

所以用户级缓冲区是C标准库提供的。

重定向:会影响缓冲方式。

C语言有缓冲区其本质上可提高程序的效率。 printf fwrite 是库函数, write 是系统调用,库函数在系统调用的“上层”, 是对系统调用的“封装”,但是 write 没有缓冲区,而 printf fwrite 有,足以说明,该缓冲区是二次加上的,又因为是C,所以由C标准库提供。

使用 dup2 系统调用

先看段代码:


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

其中O_TRUNCS将内容先清空
O_APPEND将内容追加

四、理解文件系统

文件=属性+内容
inode 只有1个
属性集合

block 可有n个
内容

filesystem
//基本情况
//空间一共是多大
//已经使用&&没有被使用
//inode
//block
//group

当我们使用ls -l读取存储在磁盘上的文件信息,然后显示出来时,其内部工作原理如下图所示:

通过ls -l进程获得文件的信息内容。

inode

inode:文件的属性 文件:inode==1:1

Data blocks:数据 文件:inode:datablock == 1:1:n

inode必须包含datablocks对应的映射关系
unsigned map[n]

inode id:表示一个inode

前面的923744就是文件的inode
一个文件的inode是多少取决于它在位图上的位置。inode bitmap。

在硬盘中创建一个文件的步骤为:先找到分区和块组,在i位图上找到一个未被使用的inode,然后再把文件的属性信息填到inode中映射的数据块上。

另外,目录也是文件=inode+数据块(文件名:inode id)

stat命令:能够看到更多信息

五、软硬链接

软连接:具有独立的inode,是一个独立的文件。软链接可以直接把需要运行的项目进程建立连接使运行变得方便:

硬链接:和指向的文件共享同一个inode,不是一个独立的文件。相当于一个相同的文件的另一个名字,与原文件建立一个链接。

六、动静态库

静态库与动态库

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

库静态链接,将对应代码拷贝进bin,体积比较大,bin可移植性强。链接的时候纳入进来,可能比较占用资源(硬盘,内存)。

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

生成静态库


现将静态库打包,然后在查看静态库中的目录列表:


这里-I选项可查找到头文件所在目录,-L可查找到lib文件所在目录,最后生成静态库文件。

生成动态库


其中生成动态库时要注意以下三点:
shared: 表示生成共享库格式
fPIC:产生位置无关码(position independent code)
库名规则:libxxx.so

在Makefile中配置make output发布版本可直接生成mylib的文件库。

使用动态库

1、拷贝.so文件到系统共享库路径下, 一般指/usr/lib。
2、更改 LD_LIBRARY_PATH。

可看到所依赖的库为我们自己写的动态库。

3、ldconfig 配置/etc/ld.so.conf.d/,ldconfig更新。

以上是关于基础I/O的主要内容,如果未能解决你的问题,请参考以下文章

2017-2018-1 20155331 《信息安全系统设计基础》第十四周学习总结

2018-2019-1 20165203 《信息安全系统设计基础》第六周学习总结

20145216史婧瑶《信息安全系统设计基础》第九周学习总结

20145239 《信息安全系统设计基础》第9周学习总结

2018-2019-1 20165226 《信息安全系统设计基础》第6周学习总结

2017-2018-1 20155315 《信息安全系统设计基础》第13周学习总结