昨天的复习:环境标 就是内存中环境变量的首地址,是一个 字符指针数组,以NULL元素作为结束,环境表的引入方式:extern char ** environ;
main()函数的第三个参数也是环境表的首地址
今天:关于环境变量的标C库函数Unix/LInux的内存管理(机制和实现函数)环境变量库函数:(只针对本进程)getenv()/setenv()/putenv()unsetenv()/clearenv()
getenv() -按环境变量的名 取得 环境变量的值
setenv() 新增环境变量或修改/不改变 已经存在的环境变量
putenv() 新增环境变量或是修改已存在环境变量
unsetenv() 删除一个环境变量
clearenv() 删除全部的环境变量
Unix/Linux的内存管理(UC的真正的开始)
内存分配和回收的相关函数及实现关系进程的内存空间划分
虚拟内存地址空间机制内存管理的相关函数:
自动管理内存 STL容器(各种数据结构)/JAVA
|
new分配 C++的内存管理delete回收
|
malloc()分配free()回收 C程序员的内存管理
|
sbrk() brk() Unix系统函数 UC程序员 (既能分配也能回收)
|
mmap()分配munmap()回收 Unix系统函数 UC程序员
| (用户层)
------------------------------------
kmalloc() vmalloc() (内核层)
进程的内存空间划分进程是什么?-运行在内存中的程序
程序是什么?-就是硬盘上的可执行文件,是编译连接的产物
为了沟通的方便,很多情况下,把进程也统称为程序
进程的内存空间划分为以下部分:
1.代码区 -存放代码(函数)的区域,函数指针就是指向代码区,是函数在代码区的地址。是一个只读区域。
2.全局区域 -存放全局变量和static修饰的局部变量,能读能写
3.BSS段-存放未初始化的全局变量。(没有写=的)
注:全局区和BSS段都会在main()函数执行前创建,区别在于BSS段在main()函数执行之前 会自动清0一次。
int a;int main(){int b;printf("%d,%d",a,b);//0,和随机数}
4.栈区/堆栈区(- stack)-没有用static修饰的局部变量,包括函数的形参。栈区的内存系统 系统自动管理。程序员也可以痛过特定函数进行管理的参与,但是不建议使用。
5.堆区(heap)-也叫自由区,是程序员全权管理的区域。系统不会自动对堆区做任何的事情。malloc()等函数都是在操作堆区。堆区内存的分配和回收都是由程序员完成。
6.只读常量区 - 很多书上把这个区域并入了代码区,字符串的字面值(“”括起来的)和const修饰的全局变量
虚拟内存地址空间
在Unix系统中,程序员所接触的内存地址都是虚拟内存地址,而不是真实的物理内存地址。虚拟内存地址 其实就是一个整数,本质上是不能存储数据的,需要做内存映射后才能存储数据。每个进程在启动时就先天具备了0-4G的虚拟内存地址空间(32位系统),这些地址都是一个整数,而无法存储数据。虚拟内存地址必须映射到物理内存/硬盘文件 后才能存储数据。所谓的内存分配就是 先分配未使用的虚拟内存地址,再用虚拟内存地址映射到物理内存/硬盘文件。如果使用了未映射的虚拟内存地址 存储数据就会引发 段错误。
进程之间即使是相同的虚拟内存地址 对应的物理内存也是不一样的。 Unix/Linux系统 用一个整数(虚拟内存地址) 代表一块物理内存。
虚拟内存地址是按字节管理的(一个字节有一个虚拟地址),但是在做内存映射时不是以字节为单位做映射,而是用内存页做映射的基本单位。一个内存页(32位系统)是4096(4k),函数getpagesize()可以获取内存页的大小。(空间换时间)先分配4k的空间,等用完了4k的空间再分配 malloc(4)-》分配了4字节的虚拟地址,但是映射了33页物理内存
0-4G的虚拟内存地址 分为用户空间和内核空间,用户空间是0-3G,3G-4G是内核空间,用户空间是给用户使用,内核空间是给系统内核使用,用户空间的程序不能直接访问内核空间,但可以通过Unix提供的系统函数(系统调用)进入内核空间。
关于数组和指针:大多数情况下,数组和指针可以混合使用,但是有区别:
1.sizeof()不一样,数组的sizeof()是数组的长度乘以元素的大小,而指针的sizeof()是4(32位系统)。
2.指针可以改变地址的指向,而数组是常指针(指向地址不能改变);
3.函数的返回值不能是数组,因此如果返回数组时,返回值类型必须写指针。void fa(){char *s1 = "abc";//只读常量区 因为指针的指向可以该,所以直接指向了“abc“所在的只读常量区
char s2[4] = "abc";//在堆区,因为数组是常量指针,一开始就指向了堆区,由于常量指针的指向是不可以更改的,故直接把”abc“复制到了堆区}
进程内存空间的排序次序(由小到大)
代码区 只读常量区 全局区 BSS段 堆区 栈区
堆区的分配方式是从小到大
栈区的分配方式是从大到小其中,栈区在3G左右的位置。
Linux系统 中,文件可以代表 几乎一切,内存有文件与之对应,输入输出设备 也有文件与之对应,目录 也是 文件。
cat /proc/pid/maps 可以查看内存映射的情况,其中pid就是进程编号ID,可以用getpid()函数取得(进程不结束就有效)
字符串处理和数据结构是每个程序员的基本功,作为c程序员操作字符串的基本代码。(超重要)
出 段错误的可能:1.使用了未映射的虚拟内存地址2.对内存进行了没有权限的操作,比如:修改了只读区
//字符串的基本代码
5 //c语言中,字符串只能用char *或char s[]
6 int main()
7 {
8 //1.字符串赋值 等号赋值,该的是地址,strcpy()该值,不改变指向
9 char* s1 = "abc";
10 char s2[4] = "abc";//初始化是例外,改值
11 s1 = "123";//y
12 strcpy(s1,"123");//n 段错误 只读区域是不能改值的
13 s2 = "123";//n 数组是常指针,常指针不能该地址
14 strcpy(s2,"123");//y 数组可以改元素值
15
16 char* s3 = malloc(4);
17 s3 = "123";//n 不行的原因是因为等号改变了堆区的指向,指向了只读常量区,> 虽然不会报错,但是会导致释放不了 指向只读区,堆区内存泄漏 堆区和栈区分配了> 地址,所以一般都用strcpy
18 strcpy(s3,"123");//y 直接改变值,不改变指向,所以堆区还有权限释放自己
19 printf("s4 = %s\n",s3);
20 free(s3);
21 }