目录
一、进程创建
二、进程状态
1. 运行状态R
2. 睡眠状态S
3. 僵尸状态Z
4. 孤儿进程
三、进程优先级 PRI
四、地址空间的层次结构
五、虚拟地址和物理地址
一、进程创建
- fork()函数创建子进程,若创建成功,则给父进程返回子进程的pid,给子进程返回0
- 父子进程关系:子进程ppid == 父进程pid
- 父子进程可同时运行,互不干扰
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>int main()
{pid_t id = fork();if (id == 0) {while (1) {printf("这是一个子进程,pid=%d, ppid=%d\n", getpid(), getppid());sleep(1);}}else {while (1) {printf("这是一个父进程,pid=%d, ppid=%d\n", getpid(), getppid());sleep(1);}}return 0;
}
进程特性:
- 竞争性:系统进程数目众多,但cpu只有少数,所有进程之间根据优先级有了竞争性
- 独立性:多进程运行,需要独享各种资源,各个进程之间互不干扰
- 并行:多个进程在多个cpu上,分别同时进行运行
- 并发:多个进程在一个cpu上,采用进程切换的方式,在一段时间内让多个进程得以推进
进程在切换的时候,进行上下文保护,上下文是寄存器内的数据
进程在恢复的时候,进行上下文修复
二、进程状态
查看进程状态: ps ajx | head -1 && ps ajx | grep "myproc"
- 杀死进程: kill -9 pid
- 暂停进程: kill -19 pid
- 继续进程: kill -18 pid
- 进程状态带+号,代表是前台进程,否则是后台进程
- 前台进程在终端运行,后台进程不受终端控制
进程状态STAT:
- R:running,运行状态
- X:dead
- Z:zombie,僵尸状态,进程退出了但是还没被(父进程/os)回收
- T:stopped,暂停状态
- D:disk sleep,深度睡眠状态,改状态下的进程无法被os杀死
- S:sleeping,睡眠状态
1. 运行状态R
int main()
{while (1);return 0;
}
2. 睡眠状态S
程序运行的时候,99%以上的时间在等待加载,1%的时间在执行
int main()
{while (1) {printf("pid = %d\n", getpid());}
}
3. 僵尸状态Z
子进程结束了,但是父进程和os并没有回收子进程,子进程进入僵尸状态
int main()
{pid_t id = fork();if (id == 0) {while (1) {printf("子进程:pid=%d, ppid=%d\n", getpid(), getppid());sleep(5);exit(1);}}while (1) {printf("父进程:pid=%d, ppid=%d\n", getpid(), getppid());sleep(1);}
}
4. 孤儿进程
父进程结束了,但是子进程还在运行
孤儿进程会被os领养,变成后台进程
int main()
{pid_t id = fork();if (id == 0) {while (1) {printf("子进程:pid=%d, ppid=%d\n", getpid(), getppid());sleep(1);}}else {while (1) {printf("父进程:pid=%d, ppid=%d\n", getpid(), getppid());sleep(5);exit(1);}}
}
三、进程优先级 PRI
- linux支持进程运行中,进行优先级调整(修改nice值)
- 最终优先级 = 原优先级 + nice值
- 每一次设置,原优先级都默认变为80
- 优先级的数值越小,优先级别越高
- nice的取值范围[-20, 19]
四、地址空间的层次结构
测试代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>struct AddRoom
{int code_start;int code_end;int init_start;int init_end;int heap_start;int heap_end;int stack_start;int stack_end;//...
}; //虚拟地址空间数据结构int g_unval;
int g_val = 100;int main(int argc, char* argv[], char* env[])
{printf("code addr: %p\n", main);const char* str = "hello";printf("the const str addr: %p\n", str);printf("init globle addr: %p\n", &g_val);printf("uninit globle addr: %p\n", g_unval);char* heap_mem0 = (char*)malloc(10);char* heap_mem1 = (char*)malloc(10);char* heap_mem2 = (char*)malloc(10);char* heap_mem3 = (char*)malloc(10);printf("heap addr: %p\n", heap_mem0);printf("heap addr: %p\n", heap_mem1);printf("heap addr: %p\n", heap_mem2);printf("heap addr: %p\n", heap_mem3);printf("stack addr: %p\n", &heap_mem0);printf("stack addr: %p\n", &heap_mem1);printf("stack addr: %p\n", &heap_mem2);printf("stack addr: %p\n", &heap_mem3);for (int i = 0; i < argc; ++i) {printf("argv[%d]: %p\n", i, argv[i]);}for (int i = 0; env[i]; ++i) {printf("env[%d]: %p\n", i, env[i]);}return 0;
}
运行结果:
五、虚拟地址和物理地址
测试代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>int g_val = 100;int main()
{pid_t id = fork();if (id < 0) {printf("子进程创建失败\n");return 1;}else if (id == 0) {int cnt = 0;while (1) {printf("子进程: pid=%d, ppid=%d, g_val=%d, &g_val=%p\n", getpid(), getppid(), g_val, &g_val);sleep(1);++cnt;if (cnt == 5) {g_val = 300;printf("子进程已将全局变量修改为 300 \n");}}}else {while (1) {printf("父进程: pid=%d, ppid=%d, g_val=%d, &g_val=%p\n", getpid(), getppid(), g_val, &g_val);sleep(1);}}return 0;
}
执行结果:
原理:
虚拟地址存在的意义:
- 阻止非法访问,保护物理内存数据
- 内存管理模块与进程管理模块进行了解耦合
- 防止物理空间内存浪费
- 地址空间+页表,将内存分部有序化
- 实现对程序的分批加载,分批换出
虚拟地址转换为物理地址:
- 地址空间是进程能看到的资源窗口
- 页表决定进程真正拥有资源的情况
- 合理的对地址空间空间+页表进行资源划分,我们可以对一个进程的所有资源进行分类管理
- 先用虚拟地址的前10位查页目录
- 再用虚拟地址的中间10位查页表
- 最后用虚拟地址的后12位查页内偏移
- 磁盘内每一个页帧的大小为4KB,对应最大页内偏移量为2^12
- 页表只有在需要使用的时候才被创建