zoukankan      html  css  js  c++  java
  • 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函数回收子进程退出状态就可以销毁僵尸进程了。

    https://i.cnblogs.com/posts?categoryid=0
  • 相关阅读:
    动态二维码
    二维码
    购物车
    logback学习与配置使用
    UML类图几种关系的总结
    java.lang.Excetion,java.lang.RuntimeException,java.lang.Error有什么区别?
    Java编程最差实践
    Hibernate利用@DynamicInsert和@DynamicUpdate生成动态SQL语句
    从 Java 代码到 Java 堆
    Project configuration is not up-to-date with pom.xml
  • 原文地址:https://www.cnblogs.com/418ks/p/14716395.html
Copyright © 2011-2022 走看看