linux下进程创建/僵尸进程/孤儿进程

什么是进程:

  程序加载进内存以后开始执行,执行中的程序就叫做进程。

 

linux下程序的加载过程:

  bash程序首先调用fork函数创建出一个新的进程,随后调用exec函数执行指定的elf文件,读取elf文件头,判断elf文件的文件类型,完成程序代码段,数据段到进程空间的地址映射。

 

linux下进程管理的相关命令:

  top:实时显示进程状态,相当于windows的任务管理器。

 

   pstree:以树形结构显示进程,根节点是Init进程,进程id为1。

   pstree pid:以pid为根,显示进程树。

 

   ps aux  显示所有进程

   ps ef  显示所有进程

   ps ef | grep test  显示名称为test的进程

   kill pid  杀死进程号为pid的进程

   kill -9 pid  暴力杀死进程号为pid的进程

 

fork()函数:

  fork()函数用于创建一个新进程(子进程),fork函数调用一次返回两次,在父进程和子进程中分别返回一次,在父进程中返回子进程id,在子进程中返回0,如果创建子进程失败返回0。调用fork函数以后,子进程复制父进程的数据段和堆栈段,共享父进程的代码段。所以在父子进程中对同一个变量的读写是互相不影响的,因为两个进程的数据段堆栈段是独立的。

#include <iostream>
#include <unistd.h>

using namespace std;

int g_value = 1;

int main()
{
    int value = 20;
    int *ptr = new int(10);

    auto pid = fork();
    if (pid < 0) {
        cout << "fork failed" << endl;
        delete ptr;
        return 0;
    }

    if (pid == 0) {
     // 子进程 g_value
++; value++; (*ptr)++; cout << "child g_value:" << g_value << ",value:" << value << ",*ptr:" << *ptr << endl; } if (pid > 0) {
     // 父进程 sleep(
2); cout << "father g_value:" << g_value << ",value:" << value << ",*ptr:" << *ptr << endl; } delete ptr; return 0; }

运行结果:在父进程中sleep(2),让子进程先运行对变量进行+1,然后在父进程中输出变量,发现变量值没有被改变,说明父子进程的内存的独立的。

 

 

vfork()函数:

  vfork函数也可以创建一个子进程,和fork()函数的区别在于:

  1:fork函数,父子进程只共享代码段,数据段堆栈段不共享,vfork函数父子进程共享代码段,数据段,堆栈段。

  2:fork函数,不保证父子进程的执行顺序,vfork函数保证子进程先执行,执行结束以后再执行父进程。

 

进程退出的方式:

  正常退出:

    1:从Main函数return

    2:调用eixt函数,退出之前会刷新流

    3:调用_eixt函数,退出之前不会刷新流

  异常退出:

    1:某个信号是进程退出,如ctrl+c,kill

    2:程序发生严重错误,程序崩溃导致进程调用abort函数退出。

 

 孤儿进程和僵尸进程:

  子进程的结束和父进程的结束是一个异步的过程,父进程永远无法预测子进程到底什么时候结束。

  孤儿进程:如果父进程先执行结束后退出,子进程还没执行结束,这时子进程就会成为孤儿进程,孤儿进程会被Init进程收养,收养后就不再是孤儿进程了,被收养后就由init进程来回收子进程的退出状态。孤儿进程在系统中只是短暂存在后就被init进程收养,所以孤儿进程是无害的,不需要我们刻意解决孤儿进程。

  僵尸进程:如果子进程退出,父进程还没有退出并没有回收子进程的退出状态,那么子进程的进程描述符会一致存在于系统中,此时的子进程就会变成僵尸进程,如果不对僵尸进程进行处理,越来越多的僵尸进程会耗尽系统资源,导致不能再创建新的进程。所以僵尸进程需要被解决。

#include <iostream>
#include <unistd.h>

using namespace std;

int g_value = 1;

int main()
{
    int value = 20;
    int *ptr = new int(10);

    auto pid = fork();
    if (pid < 0) {
        cout << "fork failed" << endl;
        delete ptr;
        return 0;
    }

    if (pid == 0) {
        g_value++;
        value++;
        (*ptr)++;
        cout << "child g_value:" << g_value << ",value:" << value << ",*ptr:" << *ptr << endl;
    }

    if (pid > 0) {
        sleep(60);
        cout << "father g_value:" << g_value << ",value:" << value << ",*ptr:" << *ptr << endl;
    }

    delete ptr;
    return 0;
}

 

运行结果:父进程等待60秒,子进程先退出,就成为了僵尸进程,用top命令可以看到当前存在一个僵尸进程。

 

 

怎样解决僵尸进程:

  方法1:pid_t wait(int *status)函数,调用wait函数回收子进程的退出状态,成功返回子进程id,失败返回-1,子进程的退出状态保存在status中,该函数是阻塞的,如果没有子进程退出,将一致阻塞父进程,知道有子进程退出才会唤醒父进程。

  方法2:pid_t waitpid(int pid, int *status, int options)函数,pid表示等待终止的目标进程,传入-1代表任意进程,options设置成WHOHANG表示不阻塞等待,但需要不断循环调用waitpid函数判断是否有子进程退出。

  方法3:利用信号机制,子进程结束时会产生一个信号传到父进程,我们在父进程中注册子进程退出信号的处理函数,在处理函数中调用wait函数或waitpid函数回收子进程退出状态就可以销毁僵尸进程了。

 

posted @ 2021-04-29 00:54  我是团长  阅读(477)  评论(0编辑  收藏  举报