错误处理
1、通过函数返回值表示错误
1、返回值合法表示成功,非法表示失败
2、返回有效指针表示成功,空指针(NULL/0xffffffff)表示失败
3、返回0表示成功,-1表示失败
4、永远成功
2、通过errno表示错误 <errno.h>
perror("fopen");
error是一个全局变量,它的声明在error.h文件中,它的值随时可能发生变化。
可以将它转换成有意义的字符串,strerror(error)<=>perror("msg")
注意:在函数执行成功的情况下,不会修改errno的值,因此不能以error的值不等于0就判断函数执行出错了。所以通常会和函数的返回值配合,通过返回值判断是否出错,而通过perror查询出了类型的错误。
环境变量
以字符串形式存在的,绝大多数据记录的时路径信息,它表示了当前操作系统的资源配置,环境设置等相关信息。
1、环境变量表
每个程序运行时操作系统都会把所有的环境变量记录到一张表中,传给程序。
1、通过main函数参数获取 int main(int argc,char* argv[],char* environ[])
2、通过声明为全局变量获取 extern char** environ
2、环境变量函数
char *getenv(const char *name);
功能:根据环境变量名,获取环境变量的值
int putenv(char *string);
功能:以name=value设置环境变量的值,如果name存在则更新,不存在则添加。
返回值:成功反hi0,不成功返回-1
int setenv(const char *name,const char *value,int overwrite);
功能:设置name环境变量的值为value,如果name存在且overwrite不为0则更新,否则不变。
int unsetenv(const char *name);
功能:从环境变量表中删除name
int clearenv(void);
功能:清空环境变量表
操作系统提供的环境变量的数据记录一块特殊的存储空间,而在程序自己添加的环境变量需要自己准备存储空间。
注意:对于环境变量的修改,只能影响自己,不能影响别人。
内存管理
自动分配/释放内存(智能指针auto_ptr) STL 调用标准c++中的new/delete
new/delete C++ 标准C中的malloc/free
malloc/free 标准C 调用POSIX
brk/sbrk POSIX 调用Linux系统接口
mmap/munmap Linux 调用内核接口
kmalloc/vmalloc 内核 调用驱动
get_free_page 驱动 ...
进程映像
- 程序是保存在磁盘上的可执行文件,加载到内存中,被操作系统调用执行的程序叫进程(一个程序可以被同时执行多次形成身份不同的进程)。
- 进程在内存空间中的分布情况叫进程映像,从低地址到高地址一次排列的是:
代码段/只读段:二进制指令、字符串字面值、具有const属性且被初始化过的全局、静态变量。
数据段:被初始化过的全局变量和静态变量。
BSS段:未被初始化过的全局变量和静态变量,进程一旦加载成功就会把这段内存清理为零。
堆:动态分配及管理,需要程序员手动操作。
栈:非静态的局部变量,包括了函数的参数、返回值。(栈不能通过size查看是因为有变长数组)。
命令行参数和环境变量表:存放命令行参数和环境变量。操作系统自动添加,可以被修改。int main(int argc,char* argv[],char* environ[])
虚拟内存(32位)
每个进程都有各自独立的4G字节的虚拟地址空间。我们在编程时,使用的永远都是这4G的虚拟地址空间中的地址,永远无法直接访问物理地址。
操作系统不让程序直接访问物理内存而只能使用虚拟地址空间一方面为了操作系统自身安全,另一方面可以让程序使用到比物理内存更大的地址空间(把硬盘上的特殊文件与虚拟地址空间进行映射)。
4G的虚拟地址空间被分为两个部分:
0~3G 用户空间
3G~4G 内核空间
注意:用户空间的代码不能直接访问内核空间的代码和数据,但可以通过系统调用(不是函数,但以函数形式调用),进入到内核态间接与内核交换数据。
如果使用了没有映射过的或者访问没有权限的虚拟内存地址,就会产生段错误(非法内存访问)。
一个进程对应一个用户空间,进程一旦切换,用户空间也会发生变化,但内核空间由操作系统管理,它不会随着进程的切换而变化,内核空间由内核所管理的一张独立且唯一的init_mm表进行内存映射,而用户空间的表是每个进程一张。
注意:每个进程的内存空间完全独立,不同的进程间交换虚拟内存地址没有任何意义,所以进程之间不能直接进行通讯,需要由内核中转、协调。
虚拟内存到物理内存的映射以页为单位,1页=4k=4096字节。
内存管理API
#include <unistd.h>
void *sbrk(intptr_t increment);
返回值:未分配前的内存首地址,以字节为单位。
increment:
0 获取未分配前的内存首地址,也就是已经分配内存的尾地址
>0 增加内存空间
<0 释放内存空间
int brk(void *addr);
功能:设置未分配内存的首地址
返回值:成功返回0,失败返回-1
它们背后维护着一个指针,该指针记录的是未分配内存的首地址(当前堆内存的最后一个字节的下一个位置)。
它们都可以进行映射内存的取消映射(系统级的内存管理),为了方便起见,sbrk一般用于分配内存,brk用于释放内存。
#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_READ|PROT_WRITE
flags:映射标志
fd:文件描述符(与内存映射无关)
offset:文件映射偏移值
返回值:映射成功后的虚拟内存地址,如果出错返回值为0xffffffff
int munmap(void *addr, size_t length);
功能:取消映射
addr:需要取消映射的内存首地址
length:需要取消的字节数
返回值:成功返回0,失败返回-1
注意:sbrk/brk分配和释放的都是使用权,真正的映射工作由其他系统调用(mmap/munmap)完成。