【Linux进程控制(一)】进程创建是呼吸,进程终止是死亡,进程等待是重生:进程控制三部曲

🎬 个人主页艾莉丝努力练剑
❄专栏传送门**:《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 退出码

退出码(退出状态)可以告诉我们最后一次执行的命令的状态。在命令结束以后,我们可以知道命令是成功完成的还是以错误结束的。其基本思想是,程序返回退出代码时表示执行成功,没有问题。代码10以外的任何代码都被视为不成功。

Linux Shell中的主要退出码:

  • 退出码0表示命令执行无误,这是完成命令的理想状态。

  • 退出码1我们也可以将其解释为"不被允许的操作"。例如在没有sudo权限的情况下使用yum;再例如除以0等操作也会返回错误码1,对应的命令为let a=1/0

  • 130SIGINT^C)和143SIGTERM)等终止信号是非常典型的,它们属于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 codesignal 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进程(七)】进程虚拟地址空间详解:从概念到实现与设计哲学

🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა

相关推荐
jaysee-sjc17 小时前
【练习十】Java 面向对象实战:智能家居控制系统
java·开发语言·算法·智能家居
liulilittle17 小时前
ISIF-COP香港服务器,启用OPENPPP2 VMUX全双工
运维·服务器·网络·信息与通信·通信
哪里不会点哪里.17 小时前
Spring Boot 启动原理深度解析
java·spring boot·后端
零基础的修炼17 小时前
算法---常见位运算总结
java·开发语言·前端
蜂蜜黄油呀土豆17 小时前
Java虚拟机内存模型解析与内存管理问题
java·jvm·内存管理·内存泄漏·内存溢出
wgslucky17 小时前
sm2 js加密,java服务器端解密
java·开发语言·javascript
Hx_Ma1617 小时前
SpringBoot配置文件占位符
java·spring boot·后端
小龙报17 小时前
【SOLIDWORKS 练习题】草图专题:1.带座轴承
人工智能·嵌入式硬件·物联网·硬件架构·3d建模·硬件工程·精益工程
人工智能AI技术17 小时前
【C#程序员入门AI】AI应用的操作系统:Semantic Kernel 2026实战
人工智能·c#
海天一色y17 小时前
基于Inception-V3实现CIFAR-100数据集的分类任务
人工智能·分类·数据挖掘