错误处理:
1、通过函数的返回值表示错误
a、合理值表示成功,非法值表示失败
例如:计算大小、查找
b、指针类型的返回值NULL或者0xFFFFFFFF表示失败
例如:malloc mmap
c、返回0表示成功,-1表示失败,一般都是系统函数
d、永远成功 例如:printf
2、通过影响全局的错误编码 errno ,定义在 errno.h中
char *strerror(int errnum);
功能:根据错误编码的值,获取详细的错误信息,通过返回值返回,记录到日志中,或者通过网络通信发送出去
注意:errno是一个全局变量,不能根据它的值就轻易判断是否产生错误
void perror(const char *s);
功能:直接显示出系统错误原因
作业:
1、实现一个函数计算文件的大小
2、实现一个字符串查找函数
如果在A字符串中找到了 B字符串,则返回字串第一次出现的位置,如果找不到...
char* find_str(const char* str,const char* sub);
3、实现一个求平均值的函数,考虑溢出风险
double avg(int num1,int num2);
一、内存管理
用户层
STL 自动分配/释放内存 调用C++
C++ new/delete 调用C
C malloc/free 调用POSIX
POSIX brk/sbrk 调用Linux系统函数
Linux mmap/munmap 调用内核 kernel
系统层
内核 kmalloc/vmalloc 调用驱动
驱动 get_free_page
二、进程映像
程序是存储在磁盘上的可执行文件,当程序运行时,系统会将可执行文件加载到内存中,形成了进程(一个程序可以同时加载出多个进程的)
进程的内存空间分布情况就是进程映像,从低地址到高地址依次分布为:
text 代码段 二进制指令、常量(字符串字面值、被const修饰过的原data段数据)
只读,如果强行修改段错误
data 数据段 初始化过的全局变量和静态局部变量
bss 静态数据段 未初始化过的全局变量和静态局部变量
该段内存会被自动清理为0
heap 堆 体量较大的数据,例如结构变量
手动管理、释放时间可控、空间大
使用麻烦,与指针配合使用,可能产生内存泄漏和、内存碎片
stack 栈 局部变量、块变量
操作系统自动管理、使用方便、不产生内存泄漏、内存碎片
大小有限、释放不受控制
argv 命令行参数 程序运行开始时附加的参数 例如cp
environ 环境变量表 环境变量
每个进程都有一份,修改不会影响其他进程
练习1:打印出各个段的数据的地址,与该进程的内存记录文件对比
获取进程号:
1、ps -aux 查看a.out的进程号 去/proc/pid/maps 对比&
2、getpid() 获取当前进程的进程号
scanf("%*c");
三、虚拟内存
1、系统会为每个进程分配4G的虚拟内存空间
32个0~32个1
0xFFFFFFFF
2、用户只能使用是虚拟内存地址,无法直接使用真实的物理地址
3、虚拟地址与物理内存进行映射后才能使用,否则就会产生段错误
4、虚拟地址与物理内存的映射是由操作系统动态维护
5、让用户使用虚拟地址一方面为了内存安全,另一方面操作系统可以让每个进程使用比实际物理内存更大的地址空间
6、4G的虚拟地址分为两个部分
[0,3G) 用户空间
[3,4G) 内核空间
注意:所有进程的内核映射都是同一块,统一管理所有的用户映射内存
7、用户空间的代码不能直接访问内核空间的代码和数据,可以通过系统调用(API)的方式切换到内核态,间接地与内核交换数据
8、如果对虚拟地址越界访问(使用了没有映射成功的虚拟内存),将导致段错误
四、映射虚拟内存与物理内存的函数
#include <unistd.h>
注意:系统映射内存时是以页(1页=4096)为单位的
系统映射内存时会维护一个指针,该指针指向内存映射的最后一个字节的下一个位置
void *sbrk(intptr_t increment);
功能:通过增量参数的方式调整该指针的位置,从而达到映射或者取消映射的目的
increment:增量
0 获取原来的位置
>0 映射内存
<0 取消映射
返回值:原来的位置
int brk(void *addr);
功能:直接使用addr的值修改位置指针的位置
addr:
> 原来位置指针 映射内存
< 原来位置指针 取消映射
返回值:成功返回0,失败返回-1
注意:sbrk/brk都具有映射、取消映射的功能,诞生配合使用(sbrk映射 brk取消映射)是最方便的
练习2:计算前1000个素数,存储到堆内存中,但尽量不要浪费内存
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
功能:映射虚拟地址与物理内存,brk和sbrk底层调用的就是用它们
addr:映射内存区域的首地址,可以自己指定,如果是NULL,则系统会自动帮你指定
length:映射的字节数长度
prot:映射的权限
PROT_EXEC 执行权限
PROT_READ 读权限
PROT_WRITE 写权限
PROT_NONE 没有权限
PROT_READ | PROT_WRITE 读写权限
flags:
MAP_SHARED 对映射区域的写操作直接反映到文件中
MAP_PRIVATE 对映射区域的写操作只反映到缓冲区中
注意:两个必选其一
后面的根据情况自己选择
MAP_ANONYMOUS 映射虚拟内存到物理内存中,而不是映射文件,相当于忽略fd、offset
MAP_FIXED 如果提供的addr无法映射,则直接失败,系统不会自动调整
fd:文件描述符
注意:如果不想要映射文件与物理内存,则写0
offest:
文件内容的偏移量,如果不映射文件,也给0
返回值:成功返回映射后的内存地址,失败返回0xFFFFFFFF 或者 (void*)-1
int munmap(void *addr, size_t length);
功能:取消映射
addr:取消映射区域的起始地址
length:字节数
返回值:成功返回0,失败返回-1
注意:命令 strace ./a.out 可以跟踪程序的执行过程,从而发现函数底层调用了谁