目录

1. 进程创建

1.1 写时拷贝的原理(部分)

1.2 为什么要有写时拷贝

1.3 回顾

1.3.1 fork函数

1.3.2 fork函数返回值

1.3.3 写时拷贝

1.3.4  fork常规用法

1.3.5 fork调用失败的原因

1.4 图解

2 进程终止

2.1  进程退出场景

2.2 进程常见退出方法

2.2.1 echo $?命令

2.2.2退出码

2.2.3  exit函数(库函数)

2.2.4 _exit函数(系统调用)

2.2.5  return退出

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

2.4 总结

2.4.1 main函数中的return

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

2.5 进程终止

2.6 进程等待

2.6.1 是什么和为什么

2.6.2 图解

2.6.3 结论

2.7 关于status

2.8 进程阻塞等待和非阻塞等待

2.8.1 进程的阻塞等待方式

2.9 图解

2.10 小巧思

2.11 waitpid的返回值问题

3. 代码演示

进程创建、终止、等待

进程阻塞等待和非阻塞等待


1. 进程创建

我们前面用时间换取空间效率。

进程控制:比较偏向于用代码的方式对进程进行控制。

1.1 写时拷贝的原理(部分)

1.2 为什么要有写时拷贝

1.3 回顾

1.3.1 fork函数

在linux中fork函数是非常重要的函数,它从已存在进程中创建一个新进程。新进程为子进程,而原进程为父进程。

#include <unistd.h>
pid_t fork(void);
返回值:⼦进程中返回0,⽗进程返回⼦进程id,出错返回-1
1. 为什么要给⼦进程返回0,⽗进程返回⼦进程pid?
2. 为甚⼀个函数fork会有两个返回值?
3. 为什么⼀个id即等于0,⼜⼤于0?

进程调用fork,当控制转移到内核中的fork代码后,内核做:

  • 分配新的内存块和内核数据结构给子进程
  • 将父进程部分数据结构内容拷贝至子进程
  • 添加子进程到系统进程列表当中
  • fork返回,开始调度器调度

当一个进程调用fork之后,就有两个二进制代码相同的进程。而且它们都运行到相同的地方。但每个进程都将可以开始它们自己的旅程,看如下程序。

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 进程常见退出方法

正常终⽌(可以通过 echo $? 查看进程退出码):
1. 从main返回
2. 调⽤exit
3. _exit
异常退出:
ctrl + c,信号终⽌

2.2.1 echo $?命令

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

保存的是bash命令行上运行的最近一个退出进程的退出码

2.2.2退出码

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

Linux Shell中的主要退出码:

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

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

130(SIGINT或^C)和143(SIGTERM)等终止信号是非常典型的,它们属于128+n信号,其中n代表终止码。

可以使用strerror函数来获取退出码对应的描述。
 

main函数返回值称为进程退出码。

我们检测一下对应的退出码,Linux下有133种退出码,其它操作系统还有一些退出码——

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函数(库函数)

#include <unistd.h>
void exit(int status);

exit最后也会调用_exit(底层是_exit()封装的),但在调用_exit之前,还做了其他工作:

  • 执行用户通过atexit或on_exit定义的清理函数。
  • 关闭所有打开的流,所有的缓存数据均被写入。
  • 调用_exit。

2.2.4 _exit函数(系统调用)

#include <unistd.h>
void _exit(int status);
参数:status 定义了进程的终⽌状态,⽗进程通过wait来获取该值

说明:虽然status是int,但是仅有低8位可以被父进程所用。所以_exit(-1)时,在终端执行$?发现返回值是255。

 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错误,给了一个告警,但是,我们./code还是可以执行的——

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

上图中说出现了一个浮点数错误,说白了就是/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 进程终止

2.6 进程等待

2.6.1 是什么和为什么

父进程提供wait / waitpid的系统调用——能够回收 / 解决僵尸进程问题的系统调用。

2.6.2 图解

2.6.3 结论

结论:

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

2.7 关于status

status是有一个输出型参数,让父进程获取子进程的退出信息。

通过那两个数字:exit codesignal number

父进程能不能拿到子进程的退出码?不能,写时拷贝。

2.8 进程阻塞等待和非阻塞等待

2.8.1 进程的阻塞等待方式

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;
}

运⾏结果:

[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.

#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;
}

2.9 图解

2.10 小巧思

临近期末,李四感觉自己快要挂科了,于是,李四去找了一个班上的“学霸”,张三——之所以带引号是因为这个张三爱记笔记,和真正的学霸比起来没有那么强的天赋,李四来找张三补课,张三说:“你等我十分钟!”,李四有求于人,就在楼下等着,过了一会儿,见张三没有下来,李四给张三挂了个电话,张三说,“你再等一会儿,我还有几面书没看!”李四又相继给张三打了几个电话,打电话的间隙,李四也没闲着,第二天考C语言,李四端起一本C语言书看了起来,看了一会儿又刷了刷抖音,等了一会儿,张三终于下来了,两个人一起复习,第二天考试,李四总算是没挂,60飘过。

李四一次一次给张三在打电话就是系统调用函数,李四相当于父进程,张三就相当于子进程,父进程一直在询问子进程“退出了没有?退出了没有?”——这就是 非阻塞轮询 的过程。

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

2.11 waitpid的返回值问题

3. 代码演示

进程创建、终止、等待

#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);
//    }
//}

运行结果如下:

进程阻塞等待和非阻塞等待

//#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;
}
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐