• Linux进程实践(2) --僵尸进程与文件共享


    孤儿进程与僵尸进程

    孤儿进程:

       如果父进程先退出,子进程还没退出那么子进程的父进程将变为init进程。(注:任何一个进程都必须有父进程)

    //生成孤儿进程
    int main(int argc, char *argv[])
    {
        pid_t pid = fork();
        if (pid < 0)
            err_exit("fork error");
        else if (pid > 0)
            exit(0);
        else
        {
            sleep(10);
            cout << "Child, ppid = " << getppid() << endl;
        }
        exit(0);
    }

    僵尸进程:

       如果子进程先退出,父进程还没退出,那么子进程必须等到父进程捕获到了子进程的退出状态才真正结束,否则这个时候子进程就成为僵尸进程。

    //生成僵尸进程
    int main(int argc, char *argv[])
    {
        pid_t pid = fork();
        if (pid < 0)
            err_exit("fork error");
        else if (pid == 0)
            exit(0);
        else
        {
            sleep(50);
        }
        exit(0);
    }

    -查询父子进程状态

      ps -le | grep main

     

    避免僵尸进程

       signal(SIGCHLD, SIG_IGN);

    //示例: 避免僵尸进程
    int main(int argc, char *argv[])
    {
        signal(SIGCHLD, SIG_IGN);
        pid_t pid = fork();
        if (pid < 0)
            err_exit("fork error");
        else if (pid == 0)
            exit(0);
        else
        {
            sleep(50);
        }
        exit(0);
    }

    文件共享

       父进程的所有文件描述符都被复制到子进程中, 就好像调用了dup函数, 父进程和子进程每个相同的打开文件描述符共享一个文件表项(因此, 父子进程共享同一个文件偏移量);


    //根据上图: 理解下面这段程序和下图的演示
    int main(int argc, char *argv[])
    {
        signal(SIGCHLD, SIG_IGN);
    
        int fd = open("test.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);
        if (fd == -1)
            err_exit("file open error");
    
        cout << "We Don`t flash memory
    ";
    
        char buf[BUFSIZ];
        bzero(buf, sizeof(buf));
    
        pid_t pid = fork();
        if (pid < 0)
            err_exit("fork error");
        else if (pid > 0)
        {
            strcpy(buf, "Parent...");
            write(fd, buf, strlen(buf));
            close(fd);
            cout << "fd = " << fd << endl;
            exit(0);
        }
        else if (pid == 0)
        {
            strcpy(buf, "Child...");
            write(fd, buf, strlen(buf));
            close(fd);
            cout << "fd = " << fd << endl;
            exit(0);
        }
    }

    fork VS vfork

      在UNIX/Linux中的fork还没实现copy on write(写时复制)技术之前。Unix设计者很关心fork之后立刻执行exec所造成的地址空间浪费,所以引入了vfork系统调用。其中,vfork子进程与父进程共享数据段,并不真正复制父进程内存,因此在vfork之后执行exec系列函数,并不会导致地址空间浪费以及无用的空间复制时间.而且,即使fork实现了copy on write,效率也没有vfork高.

      但是,vfork有个限制,子进程必须立刻执行_exit或者exec系列函数。因此我们不推荐使用vfork,因为几乎每一个vfork的实现,都或多或少存在一定的问题(可以尝试在vfork之后的子进程中既不执行_exit,也不执行exec函数)

     

    fork与vfork的区别

    1. fork子进程拷贝父进程的数据段(但是现在提供了写时复制技术,只有当子进程真正需要写内存时,才复制出该内存的一段副本),因此,在父进程/子进程中对全局变量所做的修改并不会影响子进程/父进程的数据内容.

        vfork子进程与父进程共享数据段,因此父子进程对数据的更新是同步的;

    2. fork父、子进程的执行次序是未知的,取决于操作系统的调度算法

        vfork:子进程先运行,父进程后运行

    //示例1:vfork出错情况
    //在Linux 2.6内核上会持续执行,不会退出
    //而在Linux 3.13内核上, 则会引发core dump
    int main()
    {
        int iNumber = 0;
        pid_t pid = vfork();
    
        if (pid == -1)
        {
            perror("fork");
            return -1;
        }
        else if (pid > 0)
        {
            cout << "In Parent Program..." << endl;
            cout << "iNumber = " << iNumber << endl;
            cout << "pid = " << static_cast<int>(getpid());
            cout << "	 ppid = " << static_cast<int>(getppid()) << endl;
            //_exit(0);
        }
        else if (pid == 0)
        {
            iNumber ++;
            cout << "In Child Program..." << endl;
            cout << "iNumber = " << iNumber << endl;
            cout << "pid = " << static_cast<int>(getpid());
            cout << "	 ppid = " << static_cast<int>(getppid()) << endl;
            //_exit(0);
        }
    
        return 0;
    }

    //示例2: 父进程/子进程修改全局数据的情况
    int main()
    {
        int iNumber = 10;
        cout << "Before vfork, pid = " << getpid() << endl;
    
        //对比fork()
        pid_t pid = vfork();
    
        if (pid == -1)
            err_exit("fork");
        else if (pid > 0)
        {
            sleep(4);
            cout << "Parent, iNumber: " << iNumber << endl;
        }
        else if (pid == 0)
        {
            ++ iNumber;
            cout << "Child, iNumber = " << iNumber << endl;
            _exit(0);
        }
    
        return 0;
    }
    //示例3:用vfork执行当前目录下的hello程序
    int main()
    {
        int iNumber = 0;
        pid_t pid = vfork();
    
        if (pid == -1)
        {
            perror("fork");
            return -1;
        }
        else if (pid > 0)
        {
            cout << "In Parent Program..." << endl;
            cout << "iNumber = " << iNumber << endl;
            cout << "pid = " << static_cast<int>(getpid());
            cout << "	 ppid = " << static_cast<int>(getppid()) << endl;
    
        }
        else if (pid == 0)
        {
            iNumber ++;
            cout << "In Child Program..." << endl;
            cout << "iNumber = " << iNumber << endl;
            cout << "pid = " << static_cast<int>(getpid());
            cout << "	 ppid = " << static_cast<int>(getppid()) << endl;
    
    //将自己写的程序启动起来
            execve("./hello",NULL,NULL);
            _exit(0);
        }
    
        return 0;
    }

    //测试4,用vfork执行系统命令
    int main()
    {
        int iNumber = 0;
        pid_t pid = vfork();
    
        if (pid == -1)
        {
            perror("fork");
            return -1;
        }
        else if (pid > 0)
        {
            cout << "In Parent Program..." << endl;
            cout << "iNumber = " << iNumber << endl;
            cout << "pid = " << static_cast<int>(getpid());
            cout << "	 ppid = " << static_cast<int>(getppid()) << endl;
    
        }
        else if (pid == 0)
        {
            iNumber ++;
            cout << "In Child Program..." << endl;
            cout << "iNumber = " << iNumber << endl;
            cout << "pid = " << static_cast<int>(getpid());
            cout << "	 ppid = " << static_cast<int>(getppid()) << endl;
    
    //将ls命令启动起来,注意:由于C++严格的类型转换机制,需要在字符串前加(char*)
            char *const args[] = {(char *)"/bin/ls", (char *)"-l", NULL};
            int res = execve("/bin/ls",args,NULL);
            if (res == -1)
            {
                perror("execve");
                _exit(1);
            }
            _exit(0);
        }
    
        return 0;
    }

  • 相关阅读:
    《ArcGIS Runtime SDK for Android开发笔记》——问题集:Error:Error: File path too long on Windows, keep below 240 characters
    《ArcGIS Runtime SDK for Android开发笔记》——(12)、自定义方式加载Bundle格式缓存数据
    《ArcGIS Runtime SDK for Android开发笔记》——(11)、ArcGIS Runtime SDK常见空间数据加载
    《ArcGIS Runtime SDK for Android开发笔记》——问题集:如何解决ArcGIS Runtime SDK for Android中文标注无法显示的问题(转载)
    《ArcGIS Runtime SDK for Android开发笔记》——数据制作篇:紧凑型切片制作(Server缓存切片)
    《ArcGIS Runtime SDK for Android开发笔记》——(10)、ArcGIS Runtime SDK支持的空间数据类型
    《ArcGIS Runtime SDK for Android开发笔记》——(9)、空间数据的容器-地图MapView
    《ArcGIS Runtime SDK for Android开发笔记》——(8)、关于ArcGIS Android开发的未来(“Quartz”版Beta)
    legend3---9、项目的日志以及调试信息数据量非常大
    laravel如何从mysql数据库中随机抽取n条数据
  • 原文地址:https://www.cnblogs.com/itrena/p/5926975.html
Copyright © 2020-2023  润新知