一、进程的的退出
问题:为什么使用标准 C 库函数和使用 Linux 的系统函数最后会有两个截然不同的结果呢?
exit()
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(void){
printf("hello\n");
printf("world");
exit(0);
return 0;
}
C 语言的 IO 函数 printf 是有缓冲区的,先将字符串写入缓冲区当中,\n 换行的时候会刷新缓冲区使得字符串输出。
_exit()
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(void){
printf("hello\n");
printf("world");
_exit(0);
return 0;
}
在函数结束的时候,缓冲区没有被刷新,world 依然停留在缓冲区当中。
孤儿进程
孤儿进程是什么?
下面的程序是怎样创建出孤儿进程的?
为什么子进程会单独有一个终端?
子进程由父进程创建,如果父进程在子进程终止之前终止,那么子进程便成为了孤儿进程,孤儿进程会被 init 进程接管。
下面的程序让子进程先休眠一秒,父进程先运行完成,子进程被 init 进程所接管。
可执行程序运行在后台,当程序结束的时候,会切换到终端(前台),父进程结束之后被切换到了前台,但是没有想到子进程依然在运行。
(其实就是系统控制权的一个转移的过程)
#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
int main(void){
pid_t pid = fork();
if(pid > 0){
printf("i am parent process, pid :%d, ppid : %d\n",getpid(),getppid());
}
else if(pid == 0){
sleep(1);
printf("i am child process,pid : %d,ppid : %d\n",getpid(),getppid());
}
for(int i = 0;i < 5;++i){
printf("i = %d\n",i);
}
return 0;
}
三、僵尸进程
僵尸进程是什么?
僵尸进程是如何产生的?
如何避免僵尸进程?
僵尸进程 —— 进程无法被回收,仍在内存区当中占有 PCB,会占用 pid。僵尸进程太多,会导致无法创建新的进程,因为缺乏足够的 pid 进行分配。
若子进程在父进程之前结束,且父进程陷入了死循环,子进程会因在内存当中的资源无法被释放掉而成为僵尸进程。
使用 kill -9 + 僵尸进程号无法将僵尸进程杀死。
使得父进程终止可能是一种方法。
但是,并不是在很多的情况下能够使得父进程终止。
#include <sys/types.h>
#include <unistd.h>
#include<stdio.h>
int main(void){
pid_t pid = fork();
if(pid > 0){
while (1)
{
sleep(1);
printf("i am parent process, pid :%d, ppid : %d\n",getpid(),getppid());
}
}
else if(pid == 0){
//sleep(1);
printf("i am child process,pid : %d,ppid : %d\n",getpid(),getppid());
}
for(int i = 0;i < 5;++i){
printf("i = %d\n",i);
}
return 0;
}
使用 ps aux 查看进程的状态,发现子进程为僵尸进程。
使用 top 查看进程的实时状态。
进程回收
为什么要使用 wait 函数?
详细解释 wait 函数的参数和返回值?
什么是传出参数?
下图中的函数一共有几个子进程?为什么?
如何避免这样的事情发生?
4 个。
调用第一个 fork()的时候,出现了第一个子进程。
父进程调用第二个 fork(),再来一个进程。
第一个子进程和父进程共享同一段代码,也会调用第二个 fork(),又来一个进程。所以一共有四个进程。
通过返回值来区分父进程和子进程的。首先对于执行的对象的身份进行判断,如果是子进程,就不许执行。
如何创建 5 个子进程?
使用循环创建,如果是子进程访问就退出循环。
#include <sys/types.h>
#include <sys/wait.h>
#include<unistd.h>
#include<stdio.h>
int main(void){
//有一个父进程,要创建五个子进程
pid_t pid;
for(int i = 0;i < 5;++i){
fork();
//检测是否是子进程,如果是被创建出来的子进程就跳出循环,防止多次调用创建进程的代码。
if(pid == 0){
break;
}
}
return 0;
}
#include <sys/types.h>
#include <sys/wait.h>
#include<unistd.h>
#include<stdio.h>
int main(void){
//有一个父进程,要创建五个子进程
pid_t pid;
for(int i = 0;i < 5;++i){
fork();
//检测是否是子进程,如果是被创建出来的子进程就跳出循环,防止多次调用创建进程的代码。
if(pid == 0){
break;
}
if(pid > 0){
while (1)
{
printf("oarent,pid = %d\n",getpid());
sleep(1);
}
}
else if(pid == 1){
printf("child,pid = %d\n",getpid());
}
}
return 0;
}
在此例中,子进程运行结束之后,父进程一直死循环运行,子进程无法被回收成为僵尸进程。
#include <sys/types.h>
#include <sys/wait.h>
#include<unistd.h>
#include<stdio.h>
int main(void){
//有一个父进程,要创建五个子进程
pid_t pid;
for(int i = 0;i < 5;++i){
fork();
//检测是否是子进程,如果是被创建出来的子进程就跳出循环,防止多次调用创建进程的代码。
if(pid == 0){
break;
}
if(pid > 0){
while (1)
{
printf("oarent,pid = %d\n",getpid());
wait(NULL);
printf("child die.");
sleep(1);
}
}
else if(pid == 1){
while (1)
{
printf("child,pid = %d\n",getpid());/* code */
}
}
}
return 0;
}
在上面这个程序当中,父进程会被 wait()函数阻塞,而子进程死循环,不会成为僵尸进程。当子进程被人为的信号杀死之后,父进程会接着运行。
设置子进程正常退出的状态码为0
#include <sys/types.h>
#include <sys/wait.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(void){
//有一个父进程,要创建五个子进程
pid_t pid;
for(int i = 0;i < 5;++i){
fork();
//检测是否是子进程,如果是被创建出来的子进程就跳出循环,防止多次调用创建进程的代码。
if(pid == 0){
break;
}
if(pid > 0){
while (1)
{
printf("oarent,pid = %d\n",getpid());
//wait(NULL);
int st;
int ret = wait(&st);
if(ret == -1){
break;
}
if(WIFEXITED(st)){
//判断进程是否正常退出
printf("退出的状态码:%d\n",WEXITSTATUS(st));
}
if(WIFSIGNALED(st)){
//判断是否是异常终止,被信号干掉
printf("被哪个信号干掉了:%d\n",WTERMSIG(st));
}
printf("child die.");
sleep(1);
}
}
else if(pid == 1){
// while (1)
// {
printf("child,pid = %d\n",getpid());/* code */
// }
exit(0);
}
}
return 0;
}
如果将程序当中的 exit(0)改成 exit(1),子进程退出时候得到的状态码为 1
使用信号量将子进程杀死
#include <sys/types.h>
#include <sys/wait.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(void){
//有一个父进程,要创建五个子进程
pid_t pid;
for(int i = 0;i < 5;++i){
fork();
//检测是否是子进程,如果是被创建出来的子进程就跳出循环,防止多次调用创建进程的代码。
if(pid == 0){
break;
}
if(pid > 0){
while (1)
{
printf("oarent,pid = %d\n",getpid());
//wait(NULL);
int st;
int ret = wait(&st);
if(ret == -1){
break;
}
if(WIFEXITED(st)){
//判断进程是否正常退出
printf("退出的状态码:%d\n",WEXITSTATUS(st));
}
if(WIFSIGNALED(st)){
//判断是否是异常终止,被信号干掉
printf("被哪个信号干掉了:%d\n",WTERMSIG(st));
}
printf("child die.");
sleep(1);
}
}
else if(pid == 1){
while (1)
{
printf("child,pid = %d\n",getpid());/* code */
}
exit(0);
}
}
return 0;
}