2-1文件描述符
文章目录
- 1 虚拟地址空间
- 1.1 为什么需要虚拟内存而不是直接加载进物理内存
- 1.2 分区
- 2 文件描述符
- 1.1 文件描述符表file
1 虚拟地址空间
- 可以用来加载程序数据
- 对应一段连续的内存地址,其实位置为0
- 这个内存地址是虚拟的,并不是物理内存的0地址
当运行磁盘上的一个可执行程序,就会得到一个进程,内核会给每一个进程创建一个虚拟地址空间,并将应用程序装载到虚拟地址空间对应的内存中
进程在运行过程中,程序内部的指令都是通过cpu完成的,cpu只进行数据运算,不具备数据存储能力,其处理的数据从物理内存中加载进到,到写入物理内存,都是依靠内存管理单元MMU实现的
1.1 为什么需要虚拟内存而不是直接加载进物理内存
首先明白一个规则,物理内存的使用是连续的,同样的进程占用内存也是连续的
假如直接写入物理内存:
- 每个进程之间不隔离
-
进程直接访问物理地址,如果程序通过内存地址修改其他程序的内存数据就会导致其他程序运行异常
如果是虚拟地址,虚拟地址分配到0 - 100,其他程序也分配0 - 100,进程不知道这里的0-100映射到物理地址是多少
- 内存效率低
-
如果某个程序的内存不够用了,需要拷贝到交换分区重新分配内存,那就需要拷贝整个进程占用的内存,效率低下
如果使用虚拟内存,操作系统将物理内存分成小块,将活跃的内存和不活跃的内存分开管》
- 地址变化,不利于数据加载
-
物理内存的使用是经常变化的,无法确定此时此刻起始地址在哪里,每次加载数据都要使用相对地址,影响效率
虚拟地址直接访问自己的0 -100 的地址就行了,操作系统会去做映射
所以虚拟地址空间就是一个中间层,程序访问的地址都是虚拟地址,然后操作系统来做虚拟地址到物理内存的映射
1.2 分区
虚拟地址主要分为内核区和用户区
内核区:
- 内核区的内容和函数不被程序读写和直接使用
- 内核是操作系统的一部分
- 虚拟地址空间的内核区是对所有进程共享的,只有一块物理空间
用户区:
- 保留区:没有物理内存,任何引用都是非法的,程序中的空指针NULL就是这块内存
- .text段:只读,存放cpu执行的机械指令,也就是程序执行的代码
- .data段:可读可写,程序中已经初始化并且初始值不为0的全局变量和静态变量,静态存储区
- .bss段:未初始化或初始化为0的全局变量和静态变量,系统自动将其初始化为0
- 堆heap:进程运行时动态分配的内存
- 堆中的内容是匿名的,只能通过指针访问
- 堆向高地址扩展,是不连续的(链式存储)
- 内存映射区mmap:加载磁盘文件,或者加载程序运行时调用的动态库
- 栈stack:存储函数内部声明的非静态局部变量、函数参数、函数返回地址等信息
- 内存由编译器分配,向下扩展,地址是连续的
- 命令行参数:传给main函数的参数 argc argv[]
- 环境变量:和进程相关的环境变量,如比:路径,进程所有者等信息
2 文件描述符
在linux中,一切都被抽象成了文件,,需要一个文件描述符来操作这些文件,同时,所有的文件描述符都维护在内核区的文件描述符表中
1.1 文件描述符表file
内核区有专门的进程管理模块(PCB),其本质是一个task_struct结构体,其中有一个结构体file,就是文件描述符表
-
终端被视为一个设备文件,当前终端文件可以用 /dev/tty 表示
-
每个进程默认分配的文件描述符
- STDIN_FILENO:标准输入,将输入输入到终端,宏值为0
- STDOUT_FILENO:标准输出,通过终端输出,宏值为1
- STDERR_FILENO:标准错误,将错误信息通过终端输出,宏值为2
以上文件描述符都可以通用close() 函数关闭,关闭后文件断开和终端的交互
-
给新打开文件分配文件描述符
- 因为0 1 2都被分配了,所以进程使用的新文件从3开始分配
- 通过open() 函数打开/hello.txt,分配文件描述符3
- 不关闭3 再次打开会分配文件描述符4
- 打开不同的新文件/world.txt 分配5
- 如果close()关闭了3,就会释放文件描述符3,这时候打开任何文件,都会使用3来分配
- 得出结论,打开文件会使用最小可用的文件描述符,不同的文件描述符可以对应同一个文件
-
文件描述符默认最大1024,可以修改,不会重复
件,都会使用3来分配
-
得出结论,打开文件会使用最小可用的文件描述符,不同的文件描述符可以对应同一个文件
-
文件描述符默认最大1024,可以修改,不会重复