linux 学习笔记12 多进程——进程的控制与终止

多进程——进程的控制与终止

进程的控制

一般在多进程项目中:
1、子进程是业务进程(干活的进程)
2、父进程负责管理子进程

fork函数创造进程后,有两种情况
情况1:
如果父进程先于子进程退出,则子进程成为孤儿进程,此时将自动被 PID 为 1 的进程
(即 init进程)接管。孤儿进程退出后,它的清理工作有祖先进程 init 自动处理。但在 init 进程清理子进程之前,会一直消耗系统的资源,所以要尽量避免孤儿进程。

情况2:
如果子进程先退出,系统不会自动清理掉子进程的环境,而必须由父进程调用 wait 或
waitpid 函数来完成清理工作,如果父进程不做清理工作,则已经退出的子进程将成为僵尸
(zombie)进程,过多的僵尸会造成系统性能下降。

孤儿进程代码如下:

#include <func.h>                                                                                             

int main()
{
    if(!fork())
    {   
        printf("Child Mark2\n");
        while(1);
    }else
    {   
        printf("Parent Mark1\n");
        return 0;
    }   
}

执行效果如下:
fork_orphan

wait函数:

pid_t wait(int *status); 
pid_t waitpid(pid_t pid, int *status, int options); 

wait 和 waitpid 都将暂停父进程,等待一个已经退出的子进程,并进行清理工作
wait 函数随机地等待一个已经退出的子进程,并返回该子进程的 pid
waitpid 等待指定 pid 的子进程;如果为-1 表示等待所有子进程。
参数解析:
status 参数是传出参数,存放子进程的退出状态;通常用以下的两个来获取状态信息:
WIFEXITED(status)
如果子进程正常结束,它就取一个非 0 值。传入整型值,非地址
WEXITSTATUS(status) 如果 WIFEXITED 非零,它返回子进程的退出码.
options 用于改变 waitpid 的行为,其中最常用的是 WNOHANG,它表示无论子进程是否退出都将立即返回,不会将调用者的执行挂起。

例:不带子进程退出状态(status)的wait
代码如下:

#include <func.h>                                                                                             
//不带返回状态(status)的wait
int main()
{
    if(!fork())
    {   
        printf("Child Mark2,my pid is %d\n",getpid());
        return 0;
    }else
    {   
        printf("Parent Mark1\n");
        pid_t pid=wait(NULL);//等待子进程
        printf("wait success,pid=%d\n",pid);
        return 0;
    }   
}

执行效果如下:
fork_wait

例:带返回状态(status)的wait (重要)
代码如下;

#include <func.h>
//带返回状态的wait
int main()
{
    if(!fork())
    {   
        printf("Child Mark2,my pid is %d\n",getpid());
        return -1;//return的范围在(0-255),return的返回值即下方的exit code
    }else
    {   
        printf("Parent Mark1\n");
        int status;
        pid_t pid=wait(&status);
        if(WIFEXITED(status))//如果子进程正常结束,宏就取一个非0值
        {                                                                                                     
            printf("Child Mark2 exit code=%d\n",WEXITSTATUS(status));
        }else
        {   
            printf("Child Mark2 crash\n");
        }   
        printf("wait success,pid=%d\n",pid);
        return 0;
    }   
}

执行效果如下:
fork _wait _status
例:子进程非正常退出(子进程崩溃)
代码如下:

#include <func.h>                                                                                             
//子进程崩溃举例——除以0或者空指针
int main()
{
    if(!fork())
    {   
        printf("Child Mark2,my pid is %d\n",getpid());
        //例1
        //float f=1/0;//除以0子进程崩溃
        //printf("f=%f\n",f);
        //例2
        char *p=NULL;//空指针
        *p=5;
        return -1;//return的范围在(0-255),return的返回值即下方的exit code
    }else
    {   
        printf("Parent Mark1\n");
        int status;
        pid_t pid=wait(&status);
        if(WIFEXITED(status))//如果子进程正常结束,宏就取一个非0值
        {   
            printf("Child Mark2 exit code=%d\n",WEXITSTATUS(status));
        }else
        {   
            printf("Child Mark2 crash\n");
        }   
        printf("wait success,pid=%d\n",pid);
        return 0;
    }   
}

执行效果如下:
fork child crash
例:waitpid,接指定的Pid
代码如下:

#include <func.h>
//不带返回状态的waitpid
int main()
{
    pid_t pid=fork();
    if(0==pid)
    {   
        printf("Child Mark2,my pid is %d\n",getpid());
        return 0;
    }else
    {   
        printf("Parent Mark1\n");
        //pid=waitpid(pid,NULL,0);
        pid=waitpid(pid,NULL,WNOHANG);//一般WNOHANG用于循环内,父进程也在干活,它表示无论子进程是否退出都将立即返回,不会将调用者的执行挂起。 
        printf("wait success,pid=%d\n",pid);
        return 0;
    }   
}

执行效果如下:
fork_waitpid

进程的终止

进程有5种终止方式:

  1. main函数的正常结束
  2. 调用exit函数(man 3 exit)
  3. 调用_exit函数
  4. 调用abort函数
  5. 发送信号ctrl+c,ctrl+(2号信号SIGINT,3号信号SIGQUIT)
    前 3 种方式为正常的终止,后 2 种为非正常终止。
    常用1和2的终止方式,3~5是一般用于识别异常的函数。

exit与_exit的区别在于:

exit 函数在退出之前会检查文件的打开情况,把文件缓冲区中的内容写回文件,即清理 I/O 缓冲(刷新标准缓冲区),而_exit不会,因此_exit需要手动输入\n,否则数据会丢失。
由于 linux 的标准函数库中,有一种被称作“缓冲 I/O”操作,其特征就是对应每一个打开的文件,在内存中都有一片缓冲区。每次读文件时,会连续读出若干条记录,这样在下次读文件时就可以直接从内存的缓冲区中读取;同样,每次写文件的时候,也仅仅是写入内存中的缓冲区,等满足一定的条件(如达到一定数量或遇到特定字符等),再将缓冲区中的内容一次性写入文件。这种技术大大增加了文件读写的速度,但也为编程带来了麻烦。比如有一些数据,认为已经写入文件,实际上因为没有满足特定的条件,它们还只是保存在缓冲区内,这时用_exit 函数直接将进程关闭,缓冲区中的数据就会丢失。一般情况下,exit函数较为常用
例:exit使用
代码如下:

#include <func.h>
//exit如何操作

int print()
{
    printf("now is exit\n");
    exit(1);//子进程在函数中终止                                                                              
    return 0;
}
int main()
{
    if(!fork())
    {   
        printf("Child Mark2,my pid is %d\n",getpid());
        print();
        printf("After print\n");
        return 0;
    }else
    {   
        printf("Parent Mark1\n");
        int status;
        pid_t pid=wait(&status);
        if(WIFEXITED(status))//如果子进程正常结束,宏就取一个非0值
        {   
            printf("Child Mark2 exit code=%d\n",WEXITSTATUS(status));
        }else
        {   
            printf("Child Mark2 crash\n");
        }   
        printf("wait success,pid=%d\n",pid);
        return 0;
    }   
 }

执行效果如下:
exit
例:_exit
代码如下:

#include <func.h>
//_exit和exit有何区别

int print()
{
    //printf("now is exit");//不加\n,没有刷新缓冲区,虽然实际已经执行,但不会打印
    printf("now is exit\n");//刷新标准输入输出缓冲区,本句会打印                                                
    _exit(1);//子进程在函数中终止
    //exit和_exit的区别在于是否会刷新标准输入输出缓冲区,为了安全性一般常用exit
}
int main()
{
    if(!fork())
    {   
        printf("Child Mark2,my pid is %d\n",getpid());
        print();
        printf("After print\n");
        return 0;
    }else
    {   
        printf("Parent Mark1\n");
        int status;
        pid_t pid=wait(&status);
        if(WIFEXITED(status))//如果子进程正常结束,宏就取一个非0值
        {   
            printf("Child Mark2 exit code=%d\n",WEXITSTATUS(status));
        }else
        {   
            printf("Child Mark2 crash\n");
        }   
        printf("wait success,pid=%d\n",pid);
        return 0;
    }
}

执行效果如下:
加 \n 的_exit会刷新标准缓冲区,因此会打印 now is exit
加换行的_exit
没换行的_exit
不加 \n 的 _exit 没有刷新标准缓冲区,因此没有打印 now is exit

abort函数
abort常用于中间层,实际是调用6号信号SIGABRT代码如下:
代码如下:

#include <func.h>
  //abort常用于中间层                                                                                         
 int main(int argc,char *argv[])
  {
      abort();//abort会发出6号信号,使进程终止
      printf("I am Mark3\n");//由于进程终止,本句不会打印
      return 0;
  }

执行效果如下:

./abort

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值