目录
一、进程创建
二、进程状态
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
 - 页表只有在需要使用的时候才被创建
 

















