ext2文件系统
- 磁盘是用来储文件的,但是必须先把磁盘格式化为某种格式的文件系统,才能存储文件。文件系统的目的就是组织和管理磁盘中的文件。在 Linux 系统中,最常见的是 ext2 系列的文件系统
1. 如何存储文件数据
1. 先将文件分成不同的block块, 从Block Bitmap位图当中查找Data blocks区域当中空闲的块, 将文件存储在不同的空闲块中
2. 需要对文件分成不同的block块进行管理, 从inode Bitmap当中查找空闲的inode节点, 从inode Table获取inode 节点, 使用inode 节点描述文件存储信息,
3. 文件名称 + inode节点名称作为目录的目录项保存起来; Bitmap是获取Data blocks/inode Table中那个块/节点是空闲的
2. 如何获取文件数据:
5. 在目录当中根据文件名称和inode节点号, 找到对应的inode节点
6. 根据inode节点信息, 查找到文件在Data blocks当中对应的块(block)的信息, 将不同的block拼接起来, 就获取到了文件的信息;
Block Bitmap: 描述Data blocks区域当中哪一个块是空闲的, 哪一个块是使用的;
inode Bitmap: 描述inode节点在inode Table当中哪里是空闲的;
inode: 可以描述文件在Data blocks区域当中都占用哪些块
软/硬链接
软连接:
创建: ln -s[源文件][待创建出来的软链接名称]
注意:
1. 创建出来的软链接文件, 指向源文件, 修改软链接文件或者源文件是一回事
2. 软链接文件和源文件具有不同的inode,
3. 如果使用ll -i 查看到的软链接文件指向的源文件一直在闪烁 --> 说明源文件不存在 找不到了
4. 不推荐删除软链接文件指向的源文件, 要删除就一起删除, 若删除了源文件, 再次向软链接里写内容, 会再次创建一个源文件
硬链接:
创建: ln [源文件][待创建出来的硬链接文件名称]
注意:
1. 所创建出来的硬链接文件和源文件具有相同的inode 节点信息
2. 若删除源文件 就真的删除了, 再次向硬链接里写内容时不会再创建新的源文件
进程间通信–进程和进程之间交换数据
管道(数据传输)
共享内存(数据共享 A B进程都能看见 同时也进行了传输)
消息队列(数据传输)
信号量(进程控制) 这四个是应用于同一台计算机
1. 为什么会有进程间通信?
每一个进程都是拥有自己的独立的虚拟地址空间和页表结构, 促使了进程独立, 同时带来了进程和进程之间相互协作的问题,所以就引入了进程间通信, 让进程和进程之间可以进行通信; 最大的进程间通信 --> 网络 (发送的信息是先到腾讯服务器中 然后再到别人的微信客户端)
2. 管道:
ps aux|grep Angela "|"就是管道
匿名管道:
2.1 管道: 就是内核当中的一块内存, 相当于操作系统内核为进程间通信创建且维护的一块缓冲区;
2.2
用户态(用户空间) ps aux | grep root 将ps的结果写(操作fd[1])到缓冲区中, 然后grep去缓冲区读(操作fd[0])
内核态(内核空间) 缓冲区(相当于是个介质)
2.3 创建匿名管道的接口
int pipe(int fd[2]);
fd[2]: 具有两个元素的整型数组, 数组当中的每一个元素都是一个文件描述符
fd[2]是一个出参, 内核返回给用户两个文件描述符
fd[0]:表示读端, 操作fd[0]可以对匿名管道进行读 读端是往fd里读的
fd[1]:表示写端, 操作fd[1]可以对匿名管道进行写 写端是fd往管道里写 内核返回文件描述符
所以pipe是在fork之前的, 创建管道是在创建子进程之前,创建子进程时 将读写端的文件描述符也拷贝了, 所以子进程也可以对匿名管道进程操作, 父子进程一个读,一个写,
返回值:
成功返回0
失败返回-1 可以直接判断小于0
2.4 匿名管道的特性
a. 内核开辟的缓冲区并没有标识, 所以只能用于具有亲缘关系的进程之间
创建一个进程--创建一个PCB--里面有struct files_struct* files指针--指向files_struct--里有fd_array[],
父子进程 这两个具有亲缘关系的进程指向的files_struct里都有对应的写端和读端,
b. 管道的数据流向是一个方向, 数据流向只能从写端到读端; 管道是一个半双工
c. 提供字节流服务, 如果多次写入后, 读端没有及时读走管道中的数据, 然后就会造成每次写的数据之间是没有明确的数据边界的, 读端进行读的时候, 可以一次性将数据读走, 也可以按照自己的想法读任意大小的数据(read)
读:
read(int fd, void* buf, size_t size), 可以读任意大小的字符
写:
write(int fd, void* buf, size_t size); 往管道当中写的时候, 只要管道当中的容量是够的, 就可以写入; -- 管道大小
文件只是一个标识, 主要是通过文件描述符往管道里写
d. 管道大小
管道大小: PIPE_SIZE = 64K
当读端不读, 写端一直在写时, 当管道写满,不能再往缓冲区里写的时候, 写端就会进行阻塞;
当写端不写, 读端一直在读时, 当管道被读空的时候, 读端就会阻塞
e. 创建匿名管道返回的文件描述符属性 默认是阻塞的
f. 也可以设置文件描述符的属性为非阻塞,
int fcntl(int fd, int cmd, ...);
fd: 文件描述符
cmd: 想让fcntl函数执行什么操作,
F_GETFL: 获取当前文件描述符属性
F_SETFL: 设置当前文件描述符属性
...: 可变参数列表 (可以传递要改变文件描述符的属性数据)
返回值:
返回文件描述符的属性, 使用的是位图的方式; 将其结果或一个O_NONBLOCK
设置非阻塞属性:
只需要给原来的属性 按位或上 O_NONBLOCK (04000八进制 二进制为 00000000 00000000 00001000 00000000)
read: 00000000 00000000 00000000 00000000 fd[0] 读端的文件描述符 只是设置了O_RDONLY
write: 00000000 00000000 00000000 00000001 fd[1] 写端的文件描述符 只是设置了O_WRONLY
设置非阻塞相当于把第12位比特位设为1,
g. 创建一个匿名管道, 更改对应写端的文件描述符为非阻塞属性
1. 不进行读, 但是一直去写, 需要设置写端的文件描述符为非阻塞属性, 不用设置读端文件描述符 因为没有用到
1.1 读端不关闭, 写端一直写, write会返回-1, 报错当前资源不可用
1.2 读端直接关闭, 写端一直写, 当前的进程收到了SIGPIPE信号, 写端程序被杀死, 称为管道破裂,写第一个字符就直接退出了 收到SIGPIPE信号 就直接进程退出了,
问题: 如何去佐证 退出的进程是收到了SINPIPE信号,
可以创建一个子进程, 将读端全部关闭, 在子进程逻辑中, 将写满的文件描述符设置为非阻塞属性, 在父进程当中调用waitpid/wait函数, 这两个函数就可以看出来我们退出的子进程是由什么信号退出的
2. 不写,但是一直读, 只需要将我们的读端设置为非阻塞, 不用关心写端, 因为没有用到写端
2.1 写端不关闭, 读端进行读, read调用也返回-1, 返回资源不可用
2.2 所有的写端关闭, 读端进行读; read是正常调用的, read返回的是读到的字节数量, 没有收到SIGPIPE信号