
🎬 个人主页 :艾莉丝努力练剑
❄专栏传送门**:《C语言》《数据结构与算法》《C/C++干货分享&学习过程记录》
《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》
⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平
🎬 艾莉丝的简介:

文章目录
- 前情提示
-
- [1 本文内容大纲](#1 本文内容大纲)
- [2 常见进程退出方法](#2 常见进程退出方法)
- [1 ~> 进程创建](#1 ~> 进程创建)
-
- [1.1 写时拷贝的原理(现在只能介绍部分)](#1.1 写时拷贝的原理(现在只能介绍部分))
- [1.2 为什么要有写时拷贝(为什么要拷贝)](#1.2 为什么要有写时拷贝(为什么要拷贝))
- [1.3 知识点回顾](#1.3 知识点回顾)
-
- [1.3.1 fork函数](#1.3.1 fork函数)
- [1.3.2 fork函数返回值](#1.3.2 fork函数返回值)
- [1.3.3 写时拷贝](#1.3.3 写时拷贝)
- [1.3.4 fork常规用法](#1.3.4 fork常规用法)
- [1.3.5 fork调用失败的原因](#1.3.5 fork调用失败的原因)
- [1.4 进程创建思维导图](#1.4 进程创建思维导图)
- [2 ~> 进程终止](#2 ~> 进程终止)
-
- [2.1 进程退出场景](#2.1 进程退出场景)
- [2.2 进程常见退出方法](#2.2 进程常见退出方法)
-
- [2.2.1 echo ?命令](#2.2.1 echo ?命令)
- [2.2.2 退出码](#2.2.2 退出码)
- [2.2.3 exit函数(库函数)](#2.2.3 exit函数(库函数))
- [2.2.4 _exit函数(系统调用)](#2.2.4 _exit函数(系统调用))
- [2.2.5 return退出](#2.2.5 return退出)
- [2.3 衡量一个进程运行结果是否"可信",通过两个数字!](#2.3 衡量一个进程运行结果是否“可信”,通过两个数字!)
- [2.4 (总结)进程如何正常退出?](#2.4 (总结)进程如何正常退出?)
- [2.4.1 main函数中的return](#2.4.1 main函数中的return)
-
- [2.4.2 调用exit();------最佳实践:会刷新缓冲区或者_exit();------不刷新缓冲区](#2.4.2 调用exit();——最佳实践:会刷新缓冲区或者_exit();——不刷新缓冲区)
- [2.5 进程终止思维导图](#2.5 进程终止思维导图)
- [3 ~> 进程等待](#3 ~> 进程等待)
-
- [3.1 是什么?](#3.1 是什么?)
- [3.2 为什么?](#3.2 为什么?)
-
- [3.2.1 思维导图](#3.2.1 思维导图)
- [3.2.2 结论](#3.2.2 结论)
- [3.3 关于status](#3.3 关于status)
- [3.4 进程阻塞等待和非阻塞等待](#3.4 进程阻塞等待和非阻塞等待)
-
- [3.4.1 进程的阻塞等待方式](#3.4.1 进程的阻塞等待方式)
- [3.4.2 进程的非阻塞等待方式](#3.4.2 进程的非阻塞等待方式)
- [3.4.3 思维导图](#3.4.3 思维导图)
- [3.4.4 一个故事理解阻塞等待和非阻塞等待](#3.4.4 一个故事理解阻塞等待和非阻塞等待)
- [3.4.5 waitpid的返回值问题](#3.4.5 waitpid的返回值问题)
- 本文代码演示
- 博主随记
- 结尾

前情提示
1 本文内容大纲

2 常见进程退出方法

1 ~> 进程创建
我们前面用时间换取空间效率。
进程控制:比较偏向于用代码的方式对进程进行控制。

1.1 写时拷贝的原理(现在只能介绍部分)

1.2 为什么要有写时拷贝(为什么要拷贝)

1.3 知识点回顾
1.3.1 fork函数
在linux中fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。
c
#include <unistd.h>
pid_t fork(void);
返回值:⼦进程中返回0,⽗进程返回⼦进程id,出错返回-1
1. 为什么要给⼦进程返回0,⽗进程返回⼦进程pid?
2. 为甚⼀个函数fork会有两个返回值?
3. 为什么⼀个id即等于0,⼜⼤于0?
进程调用fork,当控制转移到内核中的fork代码后,内核做:
- 分配新的内存块和内核数据结构给子进程
- 将父进程部分数据结构内容拷贝至子进程
- 添加子进程到系统进程列表当中
- fork返回,开始调度器调度

当一个进程调用fork之后,就有两个二进制代码相同的进程。而且它们都运行到相同的地方。但每个进程都将可以开始它们自己的旅程,看如下程序。
c
int main(void)
{
pid_t pid;
printf("Before: pid is %d\n", getpid());
if ((pid = fork()) == -1)perror("fork()"), exit(1);
printf("After:pid is %d, fork return %d\n", getpid(), pid);
sleep(1);
return 0;
}
运行结果:
[root@localhost linux]# . / a.out
Before : pid is 43676
After : pid is 43676, fork return 43677
After : pid is 43677, fork return 0
这里看到了三行输出,一行before,两行after。进程43676先打印before消息,然后它有打印after。另一个after消息有43677打印的。注意到进程43677没有打印before,为什么呢?如下图所示:

所以,fork之前父进程独立执行,fork之后,父子两个执行流分别执行。注意,fork之后,谁先执行完
全由调度器决定。
1.3.2 fork函数返回值
- 子进程返回0;
- 父进程返回的是子进程的pid。
1.3.3 写时拷贝
通常,父子代码共享,父子再不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本。详见下图:

因为有写时拷贝技术的存在,所以父子进程得以彻底分离离!完成了进程独立性的技术保证!
写时拷贝:一种延时申请技术,可以提高整机内存的使用率。
1.3.4 fork常规用法

- 一个父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成子进程来处理请求。
- 一个进程要执行一个不同的程序。例如子进程从fork返回后,调用exec函数。
1.3.5 fork调用失败的原因

- 系统中有太多的进程;
- 实际用户的进程数超过了限制。
1.4 进程创建思维导图
如下图所示------

2 ~> 进程终止
进程终止的本质是 释放系统资源(少一个进程),就是释放进程申请的相关内核数据结构和对应的数据和代码。
2.1 进程退出场景

2.2 进程常见退出方法

2.2.1 echo $?命令
?:?是一个变量名, ?就是提取变量的内容。

保存的是bash命令行上运行的最近一个退出进程的退出码。
2.2.2 退出码
退出码(退出状态)可以告诉我们最后一次执行的命令的状态。在命令结束以后,我们可以知道命令是成功完成的还是以错误结束的。其基本思想是,程序返回退出代码时表示执行成功,没有问题。代码1或0以外的任何代码都被视为不成功。
Linux Shell中的主要退出码:

-
退出码
0表示命令执行无误,这是完成命令的理想状态。 -
退出码
1我们也可以将其解释为"不被允许的操作"。例如在没有sudo权限的情况下使用yum;再例如除以0等操作也会返回错误码1,对应的命令为let a=1/0。 -
130(SIGINT或^C)和143(SIGTERM)等终止信号是非常典型的,它们属于128+n信号,其中n代表终止码。 -
可以使用
strerror函数来获取退出码对应的描述。
main函数返回值称为进程退出码。
我们检测一下对应的退出码,Linux下有133种退出码,其它操作系统还有一些退出码------
bash
[Alice@VM-4-17-centos 12_18]$ ./backup
0->Success
1->Operation not permitted
2->No such file or directory
3->No such process
4->Interrupted system call
5->Input/output error
6->No such device or address
7->Argument list too long
8->Exec format error
9->Bad file descriptor
10->No child processes
11->Resource temporarily unavailable
12->Cannot allocate memory
13->Permission denied
14->Bad address
15->Block device required
16->Device or resource busy
17->File exists
18->Invalid cross-device link
19->No such device
20->Not a directory
21->Is a directory
22->Invalid argument
23->Too many open files in system
24->Too many open files
25->Inappropriate ioctl for device
26->Text file busy
27->File too large
28->No space left on device
29->Illegal seek
30->Read-only file system
31->Too many links
32->Broken pipe
33->Numerical argument out of domain
34->Numerical result out of range
35->Resource deadlock avoided
36->File name too long
37->No locks available
38->Function not implemented
39->Directory not empty
40->Too many levels of symbolic links
41->Unknown error 41
42->No message of desired type
43->Identifier removed
44->Channel number out of range
45->Level 2 not synchronized
46->Level 3 halted
47->Level 3 reset
48->Link number out of range
49->Protocol driver not attached
50->No CSI structure available
51->Level 2 halted
52->Invalid exchange
53->Invalid request descriptor
54->Exchange full
55->No anode
56->Invalid request code
57->Invalid slot
58->Unknown error 58
59->Bad font file format
60->Device not a stream
61->No data available
62->Timer expired
63->Out of streams resources
64->Machine is not on the network
65->Package not installed
66->Object is remote
67->Link has been severed
68->Advertise error
69->Srmount error
70->Communication error on send
71->Protocol error
72->Multihop attempted
73->RFS specific error
74->Bad message
75->Value too large for defined data type
76->Name not unique on network
77->File descriptor in bad state
78->Remote address changed
79->Can not access a needed shared library
80->Accessing a corrupted shared library
81->.lib section in a.out corrupted
82->Attempting to link in too many shared libraries
83->Cannot exec a shared library directly
84->Invalid or incomplete multibyte or wide character
85->Interrupted system call should be restarted
86->Streams pipe error
87->Too many users
88->Socket operation on non-socket
89->Destination address required
90->Message too long
91->Protocol wrong type for socket
92->Protocol not available
93->Protocol not supported
94->Socket type not supported
95->Operation not supported
96->Protocol family not supported
97->Address family not supported by protocol
98->Address already in use
99->Cannot assign requested address
100->Network is down
101->Network is unreachable
102->Network dropped connection on reset
103->Software caused connection abort
104->Connection reset by peer
105->No buffer space available
106->Transport endpoint is already connected
107->Transport endpoint is not connected
108->Cannot send after transport endpoint shutdown
109->Too many references: cannot splice
110->Connection timed out
111->Connection refused
112->Host is down
113->No route to host
114->Operation already in progress
115->Operation now in progress
116->Stale file handle
117->Structure needs cleaning
118->Not a XENIX named type file
119->No XENIX semaphores available
120->Is a named type file
121->Remote I/O error
122->Disk quota exceeded
123->No medium found
124->Wrong medium type
125->Operation canceled
126->Required key not available
127->Key has expired
128->Key has been revoked
129->Key was rejected by service
130->Owner died
131->State not recoverable
132->Operation not possible due to RF-kill
133->Memory page has hardware error
134->Unknown error 134
135->Unknown error 135
136->Unknown error 136
137->Unknown error 137
138->Unknown error 138
139->Unknown error 139
140->Unknown error 140
141->Unknown error 141
142->Unknown error 142
143->Unknown error 143
144->Unknown error 144
145->Unknown error 145
146->Unknown error 146
147->Unknown error 147
148->Unknown error 148
149->Unknown error 149
150->Unknown error 150
151->Unknown error 151
152->Unknown error 152
153->Unknown error 153
154->Unknown error 154
155->Unknown error 155
156->Unknown error 156
157->Unknown error 157
158->Unknown error 158
159->Unknown error 159
160->Unknown error 160
161->Unknown error 161
162->Unknown error 162
163->Unknown error 163
164->Unknown error 164
165->Unknown error 165
166->Unknown error 166
167->Unknown error 167
168->Unknown error 168
169->Unknown error 169
170->Unknown error 170
171->Unknown error 171
172->Unknown error 172
173->Unknown error 173
174->Unknown error 174
175->Unknown error 175
176->Unknown error 176
177->Unknown error 177
178->Unknown error 178
179->Unknown error 179
180->Unknown error 180
181->Unknown error 181
182->Unknown error 182
183->Unknown error 183
184->Unknown error 184
185->Unknown error 185
186->Unknown error 186
187->Unknown error 187
188->Unknown error 188
189->Unknown error 189
190->Unknown error 190
191->Unknown error 191
192->Unknown error 192
193->Unknown error 193
194->Unknown error 194
195->Unknown error 195
196->Unknown error 196
197->Unknown error 197
198->Unknown error 198
199->Unknown error 199
200->Unknown error 200
201->Unknown error 201
202->Unknown error 202
203->Unknown error 203
204->Unknown error 204
205->Unknown error 205
206->Unknown error 206
207->Unknown error 207
208->Unknown error 208
209->Unknown error 209
210->Unknown error 210
211->Unknown error 211
212->Unknown error 212
213->Unknown error 213
214->Unknown error 214
215->Unknown error 215
216->Unknown error 216
217->Unknown error 217
218->Unknown error 218
219->Unknown error 219
220->Unknown error 220
221->Unknown error 221
222->Unknown error 222
223->Unknown error 223
224->Unknown error 224
225->Unknown error 225
226->Unknown error 226
227->Unknown error 227
228->Unknown error 228
229->Unknown error 229
230->Unknown error 230
231->Unknown error 231
232->Unknown error 232
233->Unknown error 233
234->Unknown error 234
235->Unknown error 235
236->Unknown error 236
237->Unknown error 237
238->Unknown error 238
239->Unknown error 239
240->Unknown error 240
241->Unknown error 241
242->Unknown error 242
243->Unknown error 243
244->Unknown error 244
245->Unknown error 245
246->Unknown error 246
247->Unknown error 247
248->Unknown error 248
249->Unknown error 249
250->Unknown error 250
251->Unknown error 251
252->Unknown error 252
253->Unknown error 253
254->Unknown error 254
haha,I'm a process! pid: 25883,ppid: 20792
其中0就是Success,程序成功执行。
2.2.3 exit函数(库函数)
c
#include <unistd.h>
void exit(int status);
exit最后也会调用_exit(底层是_exit()封装的),但在调用_exit之前,还做了其他工作:
- 执行用户通过atexit或on_exit定义的清理函数。
- 关闭所有打开的流,所有的缓存数据均被写入。
- 调用_exit。

2.2.4 _exit函数(系统调用)
c
#include <unistd.h>
void _exit(int status);
参数:status 定义了进程的终⽌状态,⽗进程通过wait来获取该值
说明:虽然status是int,但是仅有低8位可以被父进程所用。所以_exit(-1)时,在终端执行$?发现返回值是255。
c
1 // 证明信号可以杀死进程,但是还不能直观地证明进程中间出异常退出是因为受到信号的影响
2 #include<stdio.h>
3 #include<unistd.h>
4 #include<time.h>
5 #include<stdlib.h>
6 #include<string.h>
7
8 int main()
9 {
10 while(1)
11 {
12 printf("haha,I'm a process! pid: %d, ppid: %d\n",getpid(),getppid());
13 sleep(1);
14 }
15 }
信号可以杀死进程:

这里最好不要杀死父进程,你很有可能会把bash干掉了,相当于Ctrl D了,我们干掉子进程。
我们改一下代码,这样观察起来明显一点:
/0错误,给了一个告警,但是,我们./backup还是可以执行的------

程序照样能够跑,但是执行了一次就退出了,因为程序异常了------

上图中说出现了一个浮点数错误,说白了就是/0之后,CPU的寄存器溢出了。
我们知道这是收到信号了,收到的是几号信号呢?8号信号。为什么呢?

fpe即FPE,8号信号模拟/0错误------代码都没跑完。
光有这样的例子就够了吗?不够。
如果当前客观上就没有/0操作,就正常运行------


刚才说/0是因为受到信号才导致进程退出的,那我们如果自己用kill命令给目标进程发一个8号,它的报错信息就应该是和刚才/0的效果是一样的,这样你才能够证明/0确实是收到信号。

同样也是FPE!
在OS内部,所有的异常最后会转化为信号,我们之前只学过9号信号杀进程,今天我们又认识了一个8号信号。
2.2.5 return退出
return是一种更常见的退出进程方法。执行returnn等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做exit的参数。

2.3 衡量一个进程运行结果是否"可信",通过两个数字!

2.4 (总结)进程如何正常退出?
2.4.1 main函数中的return

2.4.2 调用exit();------最佳实践:会刷新缓冲区或者_exit();------不刷新缓冲区

2.5 进程终止思维导图

3 ~> 进程等待
3.1 是什么?
父进程提供wait / waitpid的系统调用------能够回收 / 解决僵尸进程问题的系统调用。

3.2 为什么?
3.2.1 思维导图

3.2.2 结论
结论:
- 1、原则上,一般都是要保证父进程最后退出;
- 2、父进程要通过wait等待子进程;
- 3、如果子进程不退出,父进程就会阻塞在wait这里,等待子进程死亡。
3.3 关于status

status是有一个输出型参数,让父进程获取子进程的退出信息。
通过那两个数字:exit code和signal number。

思维导图:

父进程能不能拿到子进程的退出码?不能,写时拷贝。
3.4 进程阻塞等待和非阻塞等待
3.4.1 进程的阻塞等待方式
c
int main() {
pid_t pid;
pid = fork();
if (pid < 0) {
printf("%s fork error\n", __FUNCTION__);
return 1;
} else if (pid == 0) { // child
printf("child is run, pid is : %d\n", getpid());
sleep(5);
exit(257);
} else {
int status = 0;
pid_t ret = waitpid(-1, &status, 0); // 阻塞式等待,等待5S
printf("this is test for wait\n");
if (WIFEXITED(status) && ret == pid) {
printf("wait child 5s success, child return code is
:%d.\n",WEXITSTATUS(status));
} else {
printf("wait child failed, return.\n");
return 1;
}
}
return 0;
}
运⾏结果:
bash
[root@localhost linux]# ./a.out
child is run, pid is : 45110
this is test for wait
wait child 5s success, child return code is :1.
3.4.2 进程的非阻塞等待方式

c
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>
typedef void (*handler_t)(); // 函数指针类型
std::vector<handler_t> handlers; // 函数指针数组
void fun_one() { printf("这是⼀个临时任务1\n"); }
void fun_two() { printf("这是⼀个临时任务2\n"); }
void Load() {
handlers.push_back(fun_one);
handlers.push_back(fun_two);
}
void handler() {
if (handlers.empty())
Load();
for (auto iter : handlers)
iter();
}
int main() {
pid_t pid;
pid = fork();
if (pid < 0) {
printf("%s fork error\n", __FUNCTION__);
return 1;
} else if (pid == 0) { // child
printf("child is run, pid is : %d\n", getpid());
sleep(5);
exit(1);
} else {
int status = 0;
pid_t ret = 0;
do {
ret = waitpid(-1, &status, WNOHANG); // ⾮阻塞式等待
if (ret == 0) {
printf("child is running\n");
}
handler();
} while (ret == 0);
if (WIFEXITED(status) && ret == pid) {
printf("wait child 5s success, child return code is :%d.\n",
WEXITSTATUS(status));
} else {
printf("wait child failed, return.\n");
return 1;
}
}
return 0;
}
3.4.3 思维导图


3.4.4 一个故事理解阻塞等待和非阻塞等待
临近期末,李四感觉自己快要挂科了,于是,李四去找了一个班上的"学霸",张三------之所以带引号是因为这个张三爱记笔记,和真正的学霸比起来没有那么强的天赋,李四来找张三补课,张三说:"你等我十分钟!",李四有求于人,就在楼下等着,过了一会儿,见张三没有下来,李四给张三挂了个电话,张三说,"你再等一会儿,我还有几面书没看!"李四又相继给张三打了几个电话,打电话的间隙,李四也没闲着,第二天考C语言,李四端起一本C语言书看了起来,看了一会儿又刷了刷抖音,等了一会儿,张三终于下来了,两个人一起复习,第二天考试,李四总算是没挂,60飘过。
李四一次一次给张三在打电话就是系统调用函数,李四相当于父进程,张三就相当于子进程,父进程一直在询问子进程"退出了没有?退出了没有?"------这就是 非阻塞轮询 的过程。

还没等李四得意多久,同学聊天的时候说,"明天要考Linux了,还没准备......",啥😳?还有Linux?没办法,李四又去找张三了,李四给张三打电话,张三说,"你来吧,还是老地方,我在六楼,但是我还有几面书没看,你等我几分钟,......"张三说完正要挂断,李四叫住张三,说"不急!先别挂,上次我等了半天,我打了那么多个电话!我这次不挂了,就要听听你到底在做什么。"张三同意了,于是把电话撂在一边,这一次,李四什么都没干,就一直盯着手机,听张三说话(好重力,秒开重力场),期间什么都没干,一直到张三下楼------这就是 阻塞等待------父进程卡在指定位置,子进程执行的时候父进程其它什么事情都不做!
3.4.5 waitpid的返回值问题

本文代码演示
进程创建、终止、等待
c
#include<time.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
int exit_code = 0;
int main()
{
printf("我是父进程:pid: %d,ppid: %d\n",getpid(),getppid());
pid_t id = fork();
if(id < 0)
{
perror("fork");
exit(1);
}
if(id == 0)
{
// 子进程
int cnt = 5;
while(cnt)
{
printf("我是子进程:pid: %d,ppid: %d,cnt: %d\n",getpid(),getppid(),cnt);
sleep(1);
cnt--;
}
printf("子进程退出!\n");
exit_code = 11;
exit(11);
}
// 父进程
//pid_t rid = wait(NULL);
int status = 0;
pid_t rid = waitpid(id,&status,0);
if(rid > 0)
{
printf("等待子进程成功. . ., status: %d,exit exit_code: %d\n",status,(status>>8)&0xFF);
}
return 0;
}
//#include<time.h>
//#include<stdio.h>
//#include<unistd.h>
//#include<stdlib.h>
//#include<string.h>
//
//int main()
//{
// printf("hello world: %d", getpid());
// sleep(2);
// //exit(14);
// exit(16);
//}
//#include<time.h>
//#include<stdio.h>
//#include<unistd.h>
//#include<stdlib.h>
//#include<string.h>
//
//int print()
//{
// printf("haha,I'm a process! pid: %d, ppid: %d\n",getpid(),getppid());
// _exit(13);
//}
//
//int main()
//{
// int n = print();
// printf("n = %d\n",n);
//
// return 10;
//}
//#include<stdio.h>
//#include<unistd.h>
//#include<time.h>
//#include<stdlib.h>
//#include<string.h>
//
//int main()
//{
// while(1)
// {
// printf("haha,I'm a process! pid: %d, ppid: %d\n",getpid(),getppid());
// sleep(1);
// //int a = 10;
// //a/=0; // 有/0错误,但是符合C语言语法,最多给一个告警,程序照样能够跑,过了1秒程序退出,因为出异常了
// }
//
//}
//// 证明信号可以杀死进程,但是还不能直观地证明进程中间出异常退出是因为受到信号的影响
//#include<stdio.h>
//#include<unistd.h>
//#include<time.h>
//#include<stdlib.h>
//#include<string.h>
//
//int main()
//{
// while(1)
// {
// printf("haha,I'm a process! pid: %d, ppid: %d\n",getpid(),getppid());
// sleep(1);
// }
//}
//#include<stdio.h>
//#include<unistd.h>
//#include<time.h>
//#include<stdlib.h>
//#include<string.h>
//
//int main()
//{
// int i = 0;
// for(;i < 255;i++)
// printf("%d->%s\n",i,strerror(i));
// printf("haha,I'm a process! pid: %d,ppid: %d\n",getpid(),getppid());
//
// return 88;
//}
//#include<stdio.h>
//#include<unistd.h>
//#include<time.h>
//#include<stdlib.h>
//
//#define NUM 10
//
//int data[NUM] = {0};
//
//void Backup()
//{
// pid_t id = fork();
// if(id == 0)
// {
// // 子进程
// int i = 0;
// printf("Backup: ");
// for(i = 0;i < NUM;i++)
// {
// printf("%d ",data[i]);
// }
// printf("\n");
// sleep(10);
// exit(0); // 进程结束
// }
//}
//
//void ChangeData()
//{
// int i = 0;
// for(;i < NUM;i++)
// {
// data[i] = i + rand();
// }
// printf("origin data: ");
// for(i = 0;i < NUM;i++)
// {
// printf("%d ",data[i]);
// }
// printf("\n");
//}
//
//int main()
//{
// srand(time(NULL));
// while(1)
// {
// // 修改
// ChangeData();
// // 备份
// Backup();
// sleep(5);
// }
//}
//#include<stdio.h>
//#include<unistd.h>
//#include<time.h>
//#include<stdlib.h>
//
//#define NUM 10
//
//int data[NUM] = {0};
//
//void Backup()
//{
// pid_t id = fork();
// if(id == 0)
// {
// // 子进程
// int i = 0;
// printf("Backup: ");
// for(i = 0;i < NUM;i++)
// {
// printf("%d ",data[i]);
// }
// printf("\n");
// sleep(10);
// exit(0); // 进程结束
// }
//}
//
//void ChangeData()
//{
// int i = 0;
// for(;i < NUM;i++)
// {
// data[i] = i + rand();
// }
//}
//
//int main()
//{
// srand(time(NULL));
// while(1)
// {
// // 修改
// ChangeData();
// // 备份
// Backup();
// sleep(5);
// }
//}
运行结果如下:

进程阻塞等待和非阻塞等待
c
//#include<iostream>
//#include<cstdlib>
//#include<cstdio>
//#include <vector>
//#include <string>
//#include <unistd.h>
//#include <sys/types.h>
//#include <sys/wait.h>
//
//const int gnum = 5;
//
//void work()
//{
// int cnt = 5;
// while(cnt)
// {
// printf("%d work...,cnt: %d\n",getpid(),cnt--);
// sleep(1);
// }
//}
//
//int main()
//{
// std::vector<pid_t> subs;
// for(int idx = 0;idx < gnum;idx++)
// {
// pid_t id = fork();
// if(id < 0)
// exit(1);
// else if(id == 0)
// {
// // child
// work();
// exit(0);
// }
// else{
// subs.push_back(id);
// }
// }
//
// for(auto &sub : subs)
// {
// int status = 0;
// pid_t rid = waitpid(sub,&status,0)
// if(rid > 0)
// {
// if(WIFEXITED(status))
// {
// printf("child quit normal,exit code:%d\n",WEXITSTATUS(status));
// }
// else{
// printf("%d child quit error!\n",sub);
// }
// }
// }
//
// return 0;
//}
// ========================================================================================
// #include <stdio.h>
// #include <stdlib.h>
// #include <unistd.h>
// #include <sys/types.h>
// #include <sys/wait.h>
//void Printlog()
//{
// printf("我要打印日志!\n");
//}
//
//void SyncMYSQL()
//{
// printf("我要访问数据库!\n");
//}
//
//void Download()
//{
// printf("我要下载核心数据!\n");
//}
typedef void(*task_t)();
//task_t tasks[3] = {
// Printlog,
// SyncMYSQL,
// Download
//};
//////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
printf("我是父进程, pid :%d, ppid : %d\n", getpid(), getppid());
pid_t id = fork();
if(id == 0)
{
//child
int cnt = 5;
while(cnt)
{
printf("我是子进程, pid :%d, ppid : %d, cnt: %d\n", getpid(), getppid(), cnt);
sleep(1);
cnt--;
//int *p = NULL;
//*p = 100;
// int a = 10;
// a/=0;
}
exit(13);
}
while(1)
{
int status = 0;
pid_t rid = waitpid(id, &status, WNOHANG);
if(rid > 0)
{
// status >>= 8 || status = status>>8
// 7F : 0111 1111
//printf("wait success, who: %d, status: %d, exit code: %d, exit sig: %d\n", rid, status, (status>>8)&0xFF, status & 0x7F);
if(WIFEXITED(status))
{
printf("子进程正常退出, 退出码: %d\n", WEXITSTATUS(status));
}
else
{
printf("进程出异常了,请注意!\n");
}
break;
}
else if(rid == 0)
{
sleep(1);
printf("子进程还没有退出,父进程轮询!\n");
for(int i = 0; i < 3; i++)
{
tasks[i]();
}
}
else
{
printf("wait failed, who: %d, status: %d\n", rid, status);
break;
}
}
return 0;
}
博主随记
艾莉丝小学的时候经常被语文老师说字写得丑,还获封"蚯蚓体"的雅号,当然艾莉丝后来自己经过努力练习,字还是不算糟糕的!
这里艾莉丝就偷个小懒,把丑字放进博客里面了,uu们见谅啊!!!



哇哇哇,脏到大佬们的眼睛啦!!!艾莉丝在这里向uu们郑重道歉!!!非常抱歉!!!
结尾
uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!
结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主"一键四连"哦!
往期回顾:
【Linux进程(七)】进程虚拟地址空间详解:从概念到实现与设计哲学
🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა
