hello~ 很高兴见到大家! 这次带来的是Linux系统中关于进程控制这部分的一些知识点,如果对你有所帮助的话,可否留下你宝贵的三连呢?
个 人 主 页 : 默|笙

文章目录
- [一、 进程创建](#一、 进程创建)
-
- [1.1 fork函数初识](#1.1 fork函数初识)
- [1.2 写时拷贝COW](#1.2 写时拷贝COW)
- 二、进程终止
一、 进程创建
1.1 fork函数初识
- fork函数在之前的博客里面就已经讲到过,可以点击查看--->[fork函数]。
- 需要注意的一点是,fork了一个子进程,父子进程谁先开始执行这是完全由调度器说了算的,无法预测父子进程谁会先被调度。
- fork调用失败是系统中有太多的进程或者是数量超过了单个用户所允许进程数量的限制。
1.2 写时拷贝COW
- 写时拷贝在之前的博客也有讲到过,可以点击查看--->[数据的独立性]。

- 写时拷贝是实现数据独立性的核心机制,进程加载数据和代码到内存里,代码段初始权限是只读的,但是数据段初始权限是可读写的。但是一旦创建子进程共享代码和数据OS就会强制改变数据的权限为只读(对于父子进程都是),一旦有父进程或者是子进程尝试改变数据,就会被操作系统知道,操作系统从而对修改后的数据重新分配物理内存页。
- 强制将权限改为只读的原因就是要让OS知道有进程要对这个数据做修改了。如果是原来的读写权限OS是没有办法知道的。
二、进程终止
2.1 进程终止情况
- 进程终止会有几种情况?我们运行一个程序,当代码跑完的时候,这个进程肯定终止了,还有代码没有跑完,发生了报错,这个时候进程也会被强制终止。它们总体可以概括为以下三种:
- 代码跑完,结果正确。
- 代码跑完,结果错误。
- 代码没有跑完,发生异常。
-
前面两种是正常终止,程序执行完毕,主动退出,无论结果是对是错,OS会对资源进行回收;而第三种是异常终止,程序代码未执行完毕就被动退出。
-
一个进程正常终止,但是结果是对的还是错的呢?我们应该如何知道?----这就得提到在代码执行结束的时候会return 0了,但是为什么要返回0?这代表什么?---这个0其实就是一个退出码,返回0就是代表执行成功,如果返回的是其他的数字则代表执行不成功。
-
进程异常终止本质是收到了操作系统发送的终止信号,每个信号都有唯一的数字编号 。0对应无信号,其他数字对应不同的信号,我们可以用kill -l 进行查看。比如下面的信号9就是之前使用过的用来杀死进程的信号。

-
如此,上面三种进程终止的情况可以用退出码和sign信号来表示:
- 代码跑完,结果正确。----退出码为0,sign数字编号为0。
- 代码跑完,结果错误。----退出码不为0,sign数字编号为0。
- 代码未跑完,发生异常。----sign数字编号不为0,退出码无效。
- ctrl + c 对应的是信号2,中断信号。
2.2 退出码
-
我们可以通过查看这个退出码来了解进程执行的是否成功,不同的退出码对应不同的退出状态。0一般代表的是执行成功,其他的退出码代表的都是执行不成功,不成功的理由各不相同。
-
退出码分为两类:一类是系统提供的退出码系统,另一类是写程序的时候自定义的退出码,比如通过宏进行定义。
-
以下是Linux Shell中的主要退出码:

strerror
- 我们可以用strerror函数(包含在string.h头文件里)来获取退出码所对应的描述:
cpp
1 #include<stdio.h>
2 #include<string.h>
3
4
5 int main()
6 {
7 int i = 0;
8 for (; i < 300; i++)
9 {
10 printf("%d->%s\n", i, strerror(i));
11 }
12 return 0;
13 }

echo $?获取退出码
- $?会存储上一个前台执行的进程的退出码,可以用echo进行查看。


- 第一张是命令执行成功所查询到的退出码,如果我们用cd去进入一个不存在的文件,那么这个命令就执行不成功,退出码自然就不是0。
2.3 让进程退出的方法
exit()
- 终止整个进程,并刷新缓存区。它不光是退出当前函数。
_exit()
- 终止整个进程,但不刷新缓存区。
- exit()是一个库函数,而_exit()是一个系统调用函数,exit()比_exit()函数多了一个刷新缓存区的功能,其实exit函数就是对_exit()函数的一个封装。
cpp
#include<stdio.h>
17 #include<unistd.h>
18 #include<stdlib.h>
19 int main()
20 {
21 printf("hello world!");
22 sleep(1);
23 exit(0);//_exit(0);
24 }
对于同一段代码,exit()执行结果:
_exit()执行结果:
- printf语句里面,我们没有使用换行符\n来刷新缓冲区,可以看到exit是有刷新缓冲区的,而_exit没有。这也说明刷新缓冲区的工作并不是由内核完成的,毕竟exit是库函数,而_exit才是系统调用函数。
return
-
退出当前函数,而非终止整个进程。在我们写一些函数的时候,返回一个值会用return,这个时候会退出当前函数,而exit/_exit是直接终止这个进程。
-
可以用此段代码进行测试:
cpp
#include <stdio.h>
#include <stdlib.h>
// 普通子函数
void test() {
printf("进入test函数\n");
// 场景1:用return,仅退出test函数,进程继续
return;
// 场景2:用exit(),直接终止整个进程,后续代码都不执行
// exit(0);
}
int main() {
printf("进入main函数\n");
test(); // 调用test函数
printf("test函数执行完,回到main继续运行\n"); // 场景1会执行,场景2不会
return 0;
}
今天的分享就到此结束啦,如果对读者朋友们有所帮助的话,可否留下宝贵的三连呢~~
让我们共同努力, 一起走下去!

