进程的创建
- 前言
- 前置知识补充(重要)
- pid_t 的本质
- 父进程和子进程
- exit(0) & break & continue辨析
- 1. 核心区别对比表
- 2. 逐个拆解(附代码例子)
- (1)`exit(0)`:结束“整个程序”
- (2)`break`:跳出“当前循环/switch”
- (3)`continue`:跳过“本轮循环剩余代码”
- 总结
- 代码初步解读
- 代码进一步解读(如何理解调用fork返回两次)
- 深入理解
- 结语
前言
我们在操作系统中 总是说进程的创建 以及进程的切换等等 但是这个创建到底是怎么实现的呢 这个时候就需要借助fork()系统调用了所谓的系统调用无非是操作系统给我们用户提供的接口 使得我们能够请求操作系统的帮助去执行一些用户态下执行不了的特权指令等等
但是 在真正学习这个之前 我需要对于一些前置知识做出补充 这些都是我自己在学习的时候遇到的困惑 希望能帮到各位
参考教材:【CSAPP-深入理解计算机系统】
前置知识补充(重要)
pid_t 的本质
- 我先把我们本文需要重点理解的代码贴在下面 大家可以先看一下
- 下面这段代码就是C 语言中利用 fork() 创建进程的经典示例,其核心是理解父子进程的内存空间独立这个关键点
但是劳资看这个main函数怎么看怎么不对 到底哪不对呢 原来是pid这个变量类型我不清楚到底是什么东西- 于是就有了下文的解释:其实pid_t 不是 C 语言的内置类型(比如 int/long),而是操作系统(Linux/Unix)在头文件中定义的 “类型别名”,目的是统一表示 “进程 ID”。它的实际底层类型通常是 int 或 long
简单说:pid_t 就是 “进程 ID 的专属类型”,本质是整数,只是系统给它起了个更专业的名字~一般来说这种经典教材上的这个变量名可不是乱取的 所以我们完全可以从英文全称的角度来理解 pid—— Process ID
父进程和子进程
- 先建立直观认知:父进程 vs 子进程(生活比喻)
- 你可以把进程理解为 “正在运行的程序”,就像一个正在工作的员工:
- 父进程:相当于公司里的 “老员工”,它主动发起指令,创建出一个 “新员工”
- 子进程:就是这个 “新员工”,它从老员工那里复制了一模一样的 “工作资料”(代码、变量、内存数据等),但拿到资料后,两人的工作是独立的 —— 新员工改自己的资料,不会影响老员工的,反之亦然。
exit(0) & break & continue辨析
你想弄清楚exit(0)、continue、break的区别,核心可以先记住:这三个都是用来“终止/跳过执行流程”的,但作用的范围、层级、效果完全不同——exit(0)是“结束整个程序”,break是“跳出当前循环/分支”,continue是“跳过本轮循环剩余代码”。
下面用通俗易懂的方式拆解,结合例子让你一眼分清:
1. 核心区别对比表
| 关键字/函数 | 作用范围 | 核心效果 | 适用场景 |
|---|---|---|---|
exit(0) | 整个进程/程序 | 直接终止当前运行的整个程序,返回状态码 | 程序完成任务后退出、出错时终止 |
break | 当前循环(for/while)或 switch 分支 | 跳出当前所在的循环/switch,执行后续代码 | 满足条件时终止循环/分支 |
continue | 当前循环(for/while) | 跳过本轮循环剩余代码,直接进入下一轮循环 | 满足条件时跳过本轮剩余逻辑 |
2. 逐个拆解(附代码例子)
(1)exit(0):结束“整个程序”
本质:是一个系统调用函数(需要包含
<stdlib.h>),不是关键字;效果:调用后,当前运行的进程(程序)立刻终止,所有代码都不再执行,
0是返回给操作系统的“退出状态码”(表示正常退出);例子(对应你之前的进程代码):
#include<stdio.h>#include<stdlib.h>intmain(){printf("程序开始\n");exit(0);// 程序直接终止printf("这段代码永远不会执行\n");// 不会打印return0;}运行结果只有
程序开始,后面的代码完全不执行。补充:在你之前的父子进程代码中,子进程执行
exit(0)只会终止子进程本身,父进程不受影响(因为父子进程是独立的)。
(2)break:跳出“当前循环/switch”
- 本质:C语言关键字;
- 效果:只能跳出当前所在的一层循环(for/while)或 switch 语句,程序会继续执行循环/switch 之外的代码;
- 例子:
运行结果:#include<stdio.h>intmain(){for(inti=1;i<=5;i++){if(i==3){break;// 跳出整个for循环}printf("i = %d\n",i);}printf("循环结束,程序继续\n");// 会打印return0;}i = 1 i = 2 循环结束,程序继续
(3)continue:跳过“本轮循环剩余代码”
- 本质:C语言关键字;
- 效果:不跳出循环,只是跳过当前轮次中
continue之后的代码,直接进入下一轮循环的条件判断; - 例子:
运行结果(缺少#include<stdio.h>intmain(){for(inti=1;i<=5;i++){if(i==3){continue;// 跳过本轮剩余代码,进入下一轮}printf("i = %d\n",i);}printf("循环结束\n");return0;}i=3):i = 1 i = 2 i = 4 i = 5 循环结束
总结
exit(0):终止整个程序/进程,是“最高级”的终止;break:终止当前循环/switch,程序继续执行后续代码;continue:跳过本轮循环剩余代码,循环本身继续执行。
代码初步解读
- 在知道上面这些之后我们再来看这段代码就大概能看出来在做一件什么事情了
- 首先定义了两个变量pid x 并且将x的初始值置为1 然后用pid用于接收fork()函数的返回值
- 接下来就是一个if语句 一旦pid=0 那么 就将x+1后 打印出来 child:x= _____; 终止进程
- 如果没有进入就将x-1之后 再打印parent:x=_____ ;终止进程
- 主要就是++x以及x++的区别
- 这个时候我自己就非常疑惑 为什么会终止两次呢 这个时候就需要理解我们的fork()系统调用到底做了一件什么事情
代码进一步解读(如何理解调用fork返回两次)
- 通常来说 调用一次函数只会返回一次 但是调用fork()确确实实的是会返回两次的
- 一次是返回到父进程 一次是返回到新创建的子进程
- 上面这张图片的右上角 就是我们执行输出的最终结果 这时候出现了一种奇怪的现象 为什么会打印出来两个结果呢 这本质上和上面为什么要终止两次进程是一个道理
- 我们可以通过右下角的流程图得到答案
- 对应在代码中:
- 我们可以将main函数认为是父进程 在父进程中我们通过调用fork()创建出子进程 返回值为0
- 执行 main() 函数的初始进程就是父进程,它的 x=1;
调用 fork() 后,系统复制出一个子进程,子进程一开始也有 x=1(复制父进程的);
子进程执行 ++x 把自己的 x 改成 2,父进程执行 --x 把自己的 x 改成 0,两者互不干扰。
深入理解
- 总的来说 我们必须要明白的是 父进程和子进程是独立的进程 都有自己的私有地址空间 执行互不干扰
- 并且这个时候我们就完全可以理解:
为什么会有两个输出结果 其原因就是父进程调用fork()函数时 标准输出文件是打开的 而子进程继承了父进程所有打开的文件
结语
玛德 有种就放我进复试 球球 真没招了 本想抄个底 现在还真不知道到底是抄底还是抄顶 O(∩_∩)O哈哈~