IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> Linux环境编程 day02 内存管理、进程映像、虚拟内存、管理内存的API、系统调用、文件操作 -> 正文阅读

[C++知识库]Linux环境编程 day02 内存管理、进程映像、虚拟内存、管理内存的API、系统调用、文件操作

内存管理的层次划分

STL    智能指针,可以自动分配/释放内存资源  调用C++
C++    new/delete运算符,构造/析构函数     调用标准C
标准C  malloc/calloc/realloc/free         调用POSIX接口
POSIX  brk/sbrk                           调用Linux
Linux  mmap/munmap                        调用kernel
Kernel kmalloc/vmalloc                    调用驱动
驱动   get_free_page 

进程映像

程序就是保存在磁盘上的可执行文件,进程就是被加载到内存中正在执行的程序。
进程在内存空间中的分布情况叫作进程映像,从低到高依次是:
代码段 text
    可以执行的二进制指令、字面值常量、以及被const保护的原data、bss区的变量,该内存段的权限是只读的,如果强行修改会产生段错误。 
全局数据段 data
    初始化的全局变量、静态变量。
静态数据段 bss
    末初始化的全局变量、静态变量。
    进程一旦被加载,该段内存就会被清理为0。
    data和bss合称为全局区或静态区
堆区 heap
    动态内存分配,从低地址向高地址扩展。
    C语言没有管理堆内存的语句,只能通过标准库函数和系统函数对它进行管理。
栈区 stack
    局变量变量、块变量、函数参数、返回值等都存储在该段内存。
    由系统自动管理,由高到低扩展
命令行参数和环境变量表:指向命令行参数的指针和指向环境变量的指针。

#include <unistd.h>
pid_t getpid(void);
功能:获取当前进程的编号,俗称进程号
cat /proc/进程号/maps 可以查看到当前进程的分布情况。

虚拟内存

1、在32位系统下,每个进程都有0~4G虚拟内存。(注意,是32位系统下,64位是不一样的)
2、这些虚拟内存不能直接使用,需要与物理内存建立映射关系后才能使用,否则就会出现段错误。
3、虚拟内存与物理内存的映射由操作系统动态维护。
4、用户永远无法直接使用物理内存,只能使用虚拟内存。
5、4G内存空间分为两部分
    [0~3G] 用户空间 如某栈内存地址为0xbfe95000,约等于3G
    [3G~4G] 内核空间
6、用户空间的代码是不能访问内核空间的代码和数据,但可以通过系统调用进入内核状态,间接的与系统内核交互。
7、每个进程都对应一个用户空间,进程一切换用户空间随之变化,每个进程都有一个独一无二的进程号,进程之间是互相独立的。

虚拟内存机制有哪些优点?

    安全,防止进程之间的冲突,也可以避免操作系统被进行破坏。
    可以让进程使用到比物理内存更大的内存空间(把硬盘文件与虚拟内存进行映射)。

管理内存的API

遵循POSIX标准的内存管理

brk和sbrk在内部维护一个指针p,指向当前堆内存最一个字节的下一个位置地址。

void *sbrk(intptr_t increment);
功能:根据参数来调整p的位置(p+/-increment),既可以映射虚拟内存也可以取消映射。
返回值:调整前的p的位置。

int brk(void *addr);
功能:根据指针参数修改p的位置(把p设置为addr)。
返回值:成功返回0,失败返回-1。


注意:brk和sbrk都可以单独分配/释放内存,但一般配合使用,sbrk用于分配,brk用于释放。
brk/sbrk只是Linux了为遵循POSIX标准而提供的一套内存映射接口,但它实际上调用的是mmap/munmap。了解即可,基本不用。
注意:使用brk的过程中不要使用printf,比如brk(...);printf("...");brk(..);这样的用法会出现报错,但是如果在最后printf则不会出错。

Linux系统的内存管理

#include<sys/mman.h>是以下两函数的头文件
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
功能:让用户空间的虚拟地址与物理地址建立映射。
addr:映射内存的首地址,如果是NULL则操作系统自动计算。
length:要映射的字节数,
prot:映射的内存权限(想要多个权限就之间用|)
    PROT_EXEC  执行权限
    PROT_READ  读权限
    PROT_WRITE 写权限
    PROT_NONE  无权限
flags:
    MAP_SHARED 共享映射,映射的内容对其它进程是可见的。(一般不用)
    MAP_PRIVATE 私有映射,映射的内容对其它进程不可见的.
    MAP_DENYWRITE 拒绝其它文件写入操作
    MAP_ANON 映射的是内存而非文件(如果映射的是内存,则后两个参数给0即可)
fd:文件描述符,类似于文件指针FILE
offset:映射文件时使用到的偏移值
返回值:映射成功后的虚拟内存地址,如果失败返回值为0xffffffff(~0)
    
int munmap(void *addr, size_t length);
功能:取消映射
addr:映射内存的首地址
length:内存的字节数
返回值:成功返回0,失败返回-1。

注意:系统的内存映射是以页为单位的(4096byte),最多映射一页多一点(NULL的情况),(如果是自己给的地址,则严格按页给)再多就段错误。

系统调用

1、UNIX/Linux大分部的系统功能是通过系统调用实现的,有些是脚本,有些是信号....
2、这些系统调用被封装成了C函数的形式,但它们并不是真正的函数。
3、当程序员所编写的代码使用系统调用时,此时会与内核进行交互(发消息),借助软中断进入内核态,这是一种中断机制而不是函数调用。
4、标准库函数大部分工作在用户态,部分函数会使用系统调用进入内核态(fopen/malloc)。
可以使用 time 可执行文件名 统计程序的运行时间:
real    0m0.002s 总执行时间
user    0m0.000s 用户态执行时间
sys     0m0.000s 内核态执行时间
strace 可执行文件名 路径函数的调用过程

一切皆文件

1、在UNIX、Linux环境下文件具有特别重要的意义,它们把设备、服务都抽象成了文件,而在控制这些设备、使用这些服务时就用一套简单而统一的接口。
2、程序可以像访问磁盘上文件一样简单的控制串口、网络、打印机等设备。
3、大多数情况下只需要五个基本的系统调用(open/close/read/write/ioctl)就可以对设备进行控制。
4、系统中的任何对象都可以被当作特殊类型的文件,如:目录。

文件描述符(在很多函数中作为形参时名字是fd)

1、非负整数
2、表示一个打开的文件,像FILE*一样。
3、由open函数返回,被内核空间引用。
4、它有很多称呼,如:文件句柄,内核对象。
注意:内核对象都是以整数的形式提供给用户态。
5、有三个默认已经打开的文件描述符:现在它们统统代表终端
    0   标准输入 stdin  STDIN_FILENO
    1   标准输出 stdout STDOUT_FILENO
    2   标准错误 stderr STDERR_FILENO
注意:所谓的文件指针就包含的文件描述符的结构指针。
6、文件描述符的范围介于:0~OPEN_MAX(4096)

FILE *fdopen(int fd, const char *mode);
功能:把文件描述符转换成FILE结构指针
fd:文件描述符
mode:权限,a、a+、w、w+、r、r+

文件的打开、关闭、创建

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
功能:打开文件
pathname:文件路径
flags:打开文件的方式
返回值:文件描述符,失败返回负值

int open(const char *pathname, int flags, mode_t mode);
功能:打开或创建文件
pathname:文件路径
flags:打开方式
mode:创建文件时的权限
返回值:文件描述符,失败返回负值

注意:一般需要用到创建文件才加上第三个参数,不然就用两个参数。

int creat(const char *pathname, mode_t mode);
功能:专门用来创建文件,但基本不使用,因为open函数完全具备它的功能。

flags:想要多个权限就用|相连放在参数位置
    O_APPEND 打开文件后位置指针指向末尾
    O_CREAT 文件不存在时创建
    O_RDONLY 只读权限
    O_WRONLY 只写权限
    O_RDWR 读写权限
    O_TRUNC 清空文件内容
    O_EXCL 如果文件存在则创建失败
mode:一般赋予目录0755权限,文件0644权限(8进制数字)(以后见到函数参数是mode可以猜到和这个一样)
	R:读 W:写 X:执行 U:用户(属主) G:组 O:其他用户
    S_IRWXU  00700     
    S_IRUSR  00400
    S_IWUSR  00200
    S_IXUSR  00100
    S_IRWXG  00070
    S_IRGRP  00040
    S_IWGRP  00020
    S_IXGRP  00010
    S_IRWXO  00007   
    S_IROTH  00004
    S_IWOTH  00002
    S_IXOTH  00001

int close(int fd);
功能:关闭文件
fd:文件描述符
返回值:成功返回0,失败返回负1

问:C语言可以定义重名函数吗?
可以,在不同的作用域下可以定义同名函数,但在同一个作用域下的函数不能重名。
问:系统调用为什么可以重名?
系统调用不是真正的C函数,而通过软中断实现。

    r,w,a,r+,w+,a+分别对应哪些flags标志。
    r O_RDONLY
    r+  O_RDWR
    w O_WRONLY|O_CREAT|O_TRUNC
    w+ O_RDWR|O_CREAT|O_TRUNC
    a O_WRONLY|O_CREAT|O_APPEND
    a+ O_RDWR|O_CREAT|O_APPEND

文件读写

操作系统中的读写:

ssize_t write(int fd, const void *buf, size_t count);
功能:写入文件内容
fd:文件描述符
buf:要写入的数据的内存首地址
count:要写入的字节数
返回值:成功写入的字节数

ssize_t read(int fd, void *buf, size_t count);
功能:读取文件内容
buf:存储数据的内存首地址
count:想读取的内存字节数
返回值:成功读取到的字节数

它们与标准C的fwrite/fread很像,但更纯粹,只有这一套读写函数,没有文本文件的读写方式。

系统IO与标准IO

1、当系统调用被执行时,需要从用户态切换到内核态,执行完毕后再从内核态切换到用户态,频繁的切换就会导致性能损失。
2、标准IO在内部维护一个缓冲区,只有在满足特定条件才会把缓冲区与内核同步,因此降低了系统调用的使用频率,减少用户太和内核态的来回切换次数,因此速度比系统IO要快。
3、如果想提高系统IO的速度,可以尝试维护一个更大的缓冲区,这样它会比标准IO更快。
4、系统IO中没文本文件读写的函数,可以配合缓冲区+ssanf/sprintf来实现。
注意:普通情况建议使用标准IO(比直接使用系统IO要快),如果对速度有很高的要求,可以使用系统IO+大缓冲区。

文件位置指针

1、每个打开的文件都有一个记录读写位置的变量,它可能是整数,但都习惯的称为位置指针。
2、文件的读写操作都是从位置指针所指向地操作的。
3、lseek可以设置文件的位置指针,与标准C不一样的是它的返回值是它调整后的位置指针,所以系统调用中没有与ftell对应的函数,因此lseek就包含fseek和ftell的功能。
off_t lseek(int fd, off_t offset, int whence);
fd:文件描述符
offset:偏移值
whence:基础位置(可以挑下面三个宏使用)
功能:调整文件位置指针,用法与标准C的fseek基本一致。
配合使用的三个系统创建的宏
    SEEK_SET 文件开头
    SEEK_CUR 当前位置
    SEEK_END 文件结尾

文件黑洞

在超过文件末尾的位置写入数据就会形成文件黑洞,黑洞不会占用磁盘空间,但会计算成文件的大小,而且也会影响文件的读写。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-07 10:38:51  更:2021-09-07 10:39:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:55:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码