zoukankan      html  css  js  c++  java
  • 2.父子进程

    1.fork函数

      (1)pid_t fork(void); 创建一个子进程,子进程所有的数据,代码都是从父进程开呗过来的。失败返回-1,成功则返回:父进程返回子进程的ID,子进程返回0。pid_t类型表示进程ID,但是为了表示-1,它是有符号整型(0不是有效的进程ID,init的进程ID是最小的,1)。系统中能创建的进程的数量有系统资源决定,如果进程退出之后没有回收进程资源,那么这部分资源就会被一直占用着。

      进程ID相关的函数:

      getpid()函数;//获取当前进程ID  pid_t getpid(void);

            //获取当前进程的父进程的ID  pid_t getppid(void);

      getgid()函数;//获取当前进程使用用户组ID  gid_t getgid(void);

            //获取当前进程有效用户组ID  gid_t getegid(void);

      getuid()函数;//获取当前进程实际用户ID  uid_t getuid(void);

            //获取当前进程有效用户ID  uid_t geteuid(void);

      进程的创建:

    #include <sdtio.h>
    #include <unistd.h>
    
    int main()
    {
        pid_t id = fork();
        if(id == -1){
            perror("create fail:");
            return -1;
        }
    
        if(id == 0){
            printf("本进程是子进程:%d-----父进程ID:%d",getpid(),getppid());
        }
        if(id>0){
            printf("本进程是父进程:%d-----子进程ID:%d",getpid(),id);
        }
        while(1);
        return 0;
    }

      区分一个函数是系统函数还是库函数的依据:

        1.能否访问内核数据结构

        2.能否访问外部硬件资源

      二者有任意一个就是系统函数,都没有则是库函数。

      常用的查看当前系统进程的命令:ps aux

       查看系统能创建多少进程:

    int main()
    {
        int number = 0;
        while(1)
        {
            pid_t pid = fork();
            if(pid<0)
            {
                perror("创建失败");
                sleep(1);
            }
            if(pid == 0){
                exit(-1);//子进程退出
            }
            if(pid>0){
                //父进程回收子进程资源
                int status;
                wait(&status);//阻塞直到子进程退出
                printf("number = %d
    ",number++);
            }
        }
    }

      僵尸进程:子进程退出之后,父进程没有对子进程的资源进行回收,这个时候子进程就是僵尸进程,一定要避免僵尸进程的产生,僵尸进程的数量是有限的,有系统的最大资源决定。

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    int main()
    {
        pid_t pid = fork();
        if(pid ==0)exit(-1);
        if(pid>0)
        {
            while(1);
        }
    }

      孤儿进程:子进程还在运行,但是父进程退出了,那么这时候子进程就是孤儿进程,孤儿进程对系统不会有影响,因为孤儿进程会被自动回收。

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    int main()
    {
        pid_t pid = fork();
        if(pid == 0){
            while(1);
        }
        if(pid>0)
        {
            //父进程退出
            sleep(10);
            exit(-1);
        }
        return 0;
    }

      进程退出:exit(-1);

      进程退出的方式:1.进程运行完之后自动退出

              2.通过exit(-1)退出  进程关闭文件描述符,清空缓冲区

              3._exit()退出  只是关闭进程,不会处理文件描述符,也不会清空缓冲区。

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
        printf("hello world");
        _exit(0);
    
    
        return 0;
    }

      

    (2)父子进程在fork()之后,有哪些异同?

      查看父子进程共享的数据:

    #include <sdio.h>
    #include <unistd.h>
    
    int main()
    {
        pid_t id = fork();
        if(id == -1){
            perror("create fail:");
            return -1;
        }
    
        if(id == 0){
            printf("本进程是子进程:%d-----父进程ID:%d",getpid(),getppid());
        }
        if(id>0){
            printf("本进程是父进程:%d-----子进程ID:%d",getpid(),id);
        }
        while(1);
        return 0;
    }
    
    #include <stdio.h>
    #include <unistd.h>
    
    int gdata = 123;//全局数据---数据段
    int main()
    {
        int mydata = 123;//局部数据--栈空间
        
        pid_t pid = fork();
        if(pid == 0)
        {
            while(1)
            {
                //子进程
                printf("子进程---gdata=%d mydata=%d",gdata,mydata);
                sleep(1);
            }
        }
    
        if(pid>0)
        {
            while(1)
            {
                printf("父进程---gdata=%d mydata=%d",gdata,mydata);
                sleep(1);
                mydata++;
                gdata++;
            }
        }
    
        return 0;
    }

        相同的部分:全局变量、.data、.text、堆、栈、环境变量、用户ID、宿主目录、进程工作目录、信号处理方式.....

        不同的部分:进程ID、fork的返回值、两者的父进程ID、程序运行时间、闹钟(定时器)、未决信号集。

        父子进程共享的区域:

          1.文件描述符

          2.mmap简历的映射区,fork之后,父子进程的先后执行顺序是不确定的,取决于内核所使用的的调度算法。

    2.vfork()函数

      fork()和vfork()都是用来创建子进程的,vfork()-----父进程要等子进程退出后才运行(写时复制),而fork()的父子进程的执行顺序是由系统调度决定的。

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    int main()
    {
        pid_t pid = vfork();
        if(pid == 0){
            printf("子进程
    ");
            while(1);
        }
    
        if(pid>0){
            printf("父进程
    ");
        }
    }

    3.exec函数族

      (1)fork()创建子进程后执行的是和父进程相同的程序(但有可能是执行不同的代码分支),子进程往往要调动一种exec函数来执行另一个程序。当进程调用一种exec函数时,该进程用户空间代码和数据完全被程序替换,从新程序的启动例程开始执行。调用exec函数并不创建新进程,所以调用exec函数前后的进程ID并没有改变。

        将当前进程的.text、.data替换为所要加载程序的.text、.data,然后让进程从新的.text的第一条指令开始执行,但是进程ID并没有改变,只是换核不换壳。

        exec函数如下:  

    int execl(const char *path,const char *arg,....);
    int execlp(const char *file,const char *arg....);
    int execle(const char *path,const char *arg,..,char *const envp[]);
    int execv(const char *path,char *const argv[]);
    int execvp(const char *file,char *const argv[]);
    int execve(const char *path,char *const argv[],char *const envp[]);

      1.execlp函数

        int execlp(const char *file,const char *arg....);//加载一个进程,借助PATH环境变量。

        成功:无返回;失败:-1;

        参数1:要加载的程序的名字,该函数需要配合PATH环境变量来使用,当PATH中所有目录搜索之后没有参数1则返回失败。该函数通常用来调用系统程序,例如:ls、date、cp、cat等命令。

      2.execl函数

        int execl(const char *path,const char *arg,....);//加载一个进程,通过:路径+程序名来加载

        成功:无返回;失败:-1;

        参数1:要加载的程序的绝对路径。

      3.execvp函数

        int execvp(const char *file,char *const argv[]);//加载一个进程,使用自定义环境变量env

        成功:无返回;失败:-1;

        参数1:要加载的程序名,execvp和execlp原理上一样,只是参数不同。

      4.exec函数族的一般规律

        exec函数一旦调用成功就立刻执行新的程序,无返回值,错误则返回-1,所以我们通常直接在exec()函数调用后直接调用perror()和exit(),无需if判断。

    l(list) 命令行参数列表
    p(path) 搜索file时使用path变量
    v(vector) 使用命令行参数数组
    e(environment) 使用环境变量数组,不适用进程原有的环境变量,设置新加载程序运行的环境变量

      事实上只有execve才是真正的系统调用,其他五个函数最终都是调用execve,这些函数之间的关系如下。

      

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/types.h>
    
    int main()
    {
        pid_t pid = fork();
        if(pid=0){
            char *const argv[]={"ls","-1","-a",NULL};
            execv("/bin/ls",argv);
            printf("********
    ");
        }
        if(pid>0){
            printf("#######
    ");
            while(1);
        }
        return 0;
    }

    PS:哪里写错了请指正,互相学习。

  • 相关阅读:
    算法大佬推荐
    学习的两个docker指令
    ie兼容问题解决记录
    缓存函数,curry与偏函数
    uni-app开发时遇到的注意点
    let,var,const区别
    递归的简单理解
    宏任务和微任务的进一步理解
    简单实现一个观察者模式
    业余时间
  • 原文地址:https://www.cnblogs.com/smallqizhang/p/12451982.html
Copyright © 2011-2022 走看看