IO
1.文件描述符fd
1.1 0&1&2
- Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0(stdin),标准输出(stdout),标准错误2(stderror)。
- 0,1,2对应的物理设备一般是:键盘,显示器,显示器
1.2 理解文件描述符的本质
文件描述符的本质就是数组下标
- 当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个** **!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件
1.3文件描述符分配规则
- 在files_struct数组当中,找到下标最小且未被使用的下标来作为新的文件描述符
2. 重定向
2.1 什么是重定向
就是本来要写入某个文件的内容写到了另一个文件当中。
2.2 重定向的本质
2.3 使用dup2系统调用
函数原型如下:
#include<unistd.h>
int dup2(int oldfd,int newfd)// 两个文件描述符指向同一个文件
示例代码:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
int fd = open("./log", O_CREAT | O_RDWR);
if (fd < 0) {
perror("open");
return 1;
}
close(1);
dup2(fd, 1);
for (;;) {
char buf[1024] = {0};
ssize_t read_size = read(0, buf, sizeof(buf) - 1);
if (read_size < 0) {
perror("read");
break;
}
printf("%s", buf);
fflush(stdout);//清空缓冲区将缓冲区的内容输出到屏幕上
}
return 0;
}
结果展示:
3.理解文件系统
3.1 了解文件系统
Linux ext2文件系统,上图为磁盘文件系统图(内核内存映像肯定有所不同),磁盘是典型的块设备,硬盘分区被划分为一个个的block。一个block的大小是由格式化的时候确定的,并且不可以更改。例如mke2fs的-b选项可以设定block大小为1024、2048或4096字节。而上图中启动块(Boot Block)的大小是确定的,
- Block Group:ext2文件系统会根据分区的大小划分为数个Block Group。而每个Block Group都有着相同的结构组成。政府管理各区的例子
- 超级块(Super Block):存放文件系统本身的结构信息。记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个
文件系统结构就被破坏了- GDT,Group Descriptor Table:块组描述符,描述块组属性信息,有兴趣的同学可以在了解一下
块位图(Block Bitmap):Block Bitmap中记录着Data Block中哪个数据块已经被占用,哪个数据块没
有被占用- inode位图(inode Bitmap):每个bit表示一个inode是否空闲可用。
- i节点表:存放文件属性 如 文件大小,所有者,最近修改时间等
- 数据区:存放文件内容
我们再来谈谈创建一个新文件的操作:
我们来创建一个文件
看图理解一下:
主要有以下四个操作:
1.存储属性内核先找到一个空闲的i节点(这里是1186652).内核把文件信息记录其中
2.存储数据
该文件需要存储在三个磁盘块,内核找到了三个空闲块:300,500,800.将内核缓冲区的第一块数据复制到300,下一个500,以此类推。
3.记录分配情况
文件内容按顺序300,500,800存放。内核在inode上的磁盘分布区记录了上述块列表。
4.添加文件名到目录
内核将入口(1186652,abc)添加到目录文件。文件名和inode之间的对应关系将文件名和文件的内容及属性连接起来
4. 软硬连接
4.1 硬连接
- 硬连接不具备独立的inode
我们看到,真正找到磁盘上文件的并不是文件名,而是inode,其实在Linux中可以让多个文件名对应同一个inode
- abc和def的链接状态完全相同,他们被称为指向文件的硬链接。内核记录了这个连接数,inode 1186409的硬连接数为2。
- 我们在删除文件时干了两件事情:1.在目录中将对应的记录删除,2.将硬连接数-1,如果为0,则将对应的磁盘释放
4.2软连接
软链接是通过名字引用另一个文件,在shell中的做法是:
5 动态库和静态库
5.1 概念:
- 静态库(.a):程序在编译链接的时候把库的代码连接到可执行文件中。程序运行的时候不在需要静态库
- 动态库(.so):程序 运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
- 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接。
- 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库要被用到该库的所有进程共用,节省了内存和磁盘空间。
5.2 理解动静态库
我们先来简单看一下:
我们来看一下该代码的执行过程:
系统层面上理解:
5.2.1 生成静态库
有如下文件:
//add.h
#pragma once
int add(int a,int b);
// add.c
#include"add.h"
int add(int a,int b)
{
return a+b;
}
// sub.h
#pragma once
int sub(int a,int b);
// sub.c
#include"sub.h"
int sub(int a,int b)
{
return a-b;
}
将静态库打包好给别人用,需要将静态库和头文件一同给别人:别人拿到之后直接通过头文件引入可以直接使用库里面的方法:这里使用 test.c 测试
在编译的时候就要注意了:
Makefile这样写更方便:
5.2.2 动态库
生成动态库
- shared:表示生成共享库格式
- fPIC:产生位置无关码
- 库名规则:礼包xxx.so
示例:gcc -fPIC -c sub.c add.c
gcc -shared -o libmymath.so*.o
使用动态库
编译选项
- I :链接动态库,只要库名即可(去掉lib以及版本号)
- L:链接库所在的路径。
与静态库基本相同:来看一下Makefile文件:
运行动态库
因此我们这里需要将库所在路径导入:
- 1.拷贝.so文件到系统共享库路径下, 一般指/usr/lib
- 2.更改 LD_LIBRARY_PATH=(这里是动态库所在的目录)
- 3.ldconfig 配置/etc/ld.so.conf.d/,ldconfig更新
5.3 动静态库的对比
- 动态库:体积小 ,但是依赖性强
运行的时候加载,系统里只有一份 - 静态库: 将对应的代码拷贝进对应程序,可移植性强,但是提交较大。
在编译链接的时候纳入进来,可能会比较占资源(硬盘,内存 )。