实用指南:Linux操作系统之进程控制(1)

1、进程创建

复习fork函数,fork函数的返回值

fork函数是创建一个子进程,也就是当前的父进程创建一个新的子进程

整理一下就是:新的进程是子进程,原来的进程是父进程

#include 
#include 
int main()
{
    printf("I am a process:%d\n",getpid());
    pid_t id=fork();
    if(id<0)
    {
        //进程创建失败!
        printf("fail fork!\n");
        return 1;
    }else if(id==0)
    {
        //child
        printf("I am a child:%d\n",getpid());
    }else{
        //parent
        printf("I am a parent:%d,my son pid:%d\n",getpid(),id);
    }
    return 0;
}

fork调用进程,那么进程在内核中做了什么?

1、开辟新的内存块和内核数据结构给子进程

2、将父进程部分内核数据数据拷贝到子进程

3、将父进程的PCB、地址空间、页表相关内容也都复制拷贝到子进程中,但是只有pid不是复制

4、将子进程添加到系统进程列表中

5、fork返回,开始调度器调度

为什么fork有两个返回值?

  1. 你举手(调用fork()系统调用)

  2. 监考老师(操作系统)过来

    • 记下你的当前答题进度

    • 叫来一个新考生(创建子进程)

    • 给新考生一份空白的答题卡,但题目册和你共用一本

    • 悄悄告诉你:"他的考号是1001"

    • 悄悄告诉他:"你的考号是0"

  3. 你们继续考试

    • 你看你的题册,他看他的题册(同一本物理书

    • 你在你的答题卡写:助手考号=1001

    • 他在他的答题卡写:我的考号=0

  4. 当你想在题册上做标记时

    • 监考老师立刻复印该页,给你新的一页

    • 你在新页上做标记

    • 他如果也想标记,老师会复印原页给他新的一页

同理,

1、fork进入内核

2、fork函数的实现进行申请内存构建数据结构PCB,虚拟内存,页表,最后将当前新进程设置为R状态,放置进调度列表中

3、此时进程已经创建成功了,父子进程共享代码,fork函数的最后一个代码是返回一个值,return ret这个代码父子进程都会执行一次

所以会有两个返回值 在返回时,将函数的返回值返回给变量,发生了写时拷贝,一个变量名但是内容是不同的,本质父子页表映射数据到了不同的内存区域

写时拷贝

例如

父亲账户是一千元

儿子想在父亲账户取两百块钱!

银行说:不行,你不能在你爸爸账户上拿,请你立刻开一个账户,把你爸爸的一千元复制过去

儿子在新账户上操作,取了两百,余额800

父亲的账户没有变

结果:父亲的是一千(原账户),儿子的是八百(新账户)

#include 
#include 
int main()
{
    int data=1000;
    pid_t id = fork();
    if(id==0)
    {
        //儿子开始取钱,取完剩下800
        printf("儿子:我看到:%d\n",data);
        data=800;
        printf("儿子:我要取钱了,还剩下:%d\n",data);
    }else{
        //父亲检查
        sleep(1);//儿子先拿
        printf("父亲:儿子拿完之后,我的钱剩下:%d\n",data);
    }
    return 0;
}

操作系统上的解释:

物理内存变化:

初始(fork后):
父子都指向同一物理页,页面标记为"只读"

儿子写操作时:
1. CPU:"哦,这个页面是只读的"
2. 触发缺页异常 → 进入内核
3. 内核:"这是COW页面,要复制"
4. 分配新物理页,复制内容
5. 修改儿子页表:指向新页面,设为可写
6. 儿子继续写操作

结果:
父亲页表 → 原物理页(内容未变)
儿子页表 → 新物理页(修改后的内容)

进程调用失败的原因

系统有太多进程

实际用户的进程超出了限制

总结:本质是以父进程为模板创建了子进程,不是所有的子进程都复制父进程,例如PID就不是一样的

2、进程终止

进程共有三种退出场景

1、代码跑完了,结果是对的

2、代码跑完了,结果是不对的

3、代码没跑完,程序退出了(代码异常终止)

情况1和2退出的时候会有退出标志,而情况3是发生了异常会有退出的原因

进程常见退出码

int main()
{
    return ?;
}

return 后面跟着的数字就是进程的退出码

echo $?是可以查看最近一次的进程退出码

查看进程码的信息

#include
#include
int main()
{
    for(int i = 0;i<200;i++)
    {
        printf("%d:%s\n",i,strerror(i));
    }
    return 0;
}

exit和return的区别

exit:终止整个进程,任何地方调用,都会终止

return:终止函数,如果main函数return,代表终止整个进程

站在OS角度,如何理解进程终止?

进程终止的本质

进程终止的核心思想就是归还资源

1. 释放进程管理数据结构

不是真的销毁数据结构对象,而是将它们标记为“空闲”状态,放回对应的数据结构对象池中。这样后续创建新进程时可以直接复用这些已分配的内存块,避免频繁的内存分配开销。

2. 释放程序代码和数据占用的内存空间

不是把内存内容清空,而是将相关内存页标记为“无效”,从进程的页表中移除映射关系。这些物理页面可以被其他进程重用。

3. 取消进程间的链接关系

解除进程与父进程、子进程、进程组等之间的关联关系,更新相关的内核数据结构。

posted on 2026-02-04 10:56  ljbguanli  阅读(0)  评论(0)    收藏  举报